Merge "Updates noparent to only apply to the per-file files."
diff --git a/Android.bp b/Android.bp
index 26e7165..d234411 100644
--- a/Android.bp
+++ b/Android.bp
@@ -314,6 +314,7 @@
":framework-telecomm-sources",
":framework-telephony-common-sources",
":framework-telephony-sources",
+ ":framework-vcn-util-sources",
":framework-wifi-annotations",
":framework-wifi-non-updatable-sources",
":PacProcessor-aidl-sources",
@@ -1267,7 +1268,6 @@
// TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
- "--ignore-classes-on-classpath " +
"--hide-package com.android.server " +
"--hide-package android.audio.policy.configuration.V7_0 " +
"--error UnhiddenSystemApi " +
diff --git a/ApiDocs.bp b/ApiDocs.bp
index ba93a48..ada80bb 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -91,7 +91,9 @@
arg_files: [
"core/res/AndroidManifest.xml",
],
- args: metalava_framework_docs_args,
+ args: metalava_framework_docs_args +
+ // Needed for hidden libcore annotations for now.
+ " --ignore-classes-on-classpath ",
write_sdk_values: true,
}
@@ -101,7 +103,10 @@
arg_files: [
"core/res/AndroidManifest.xml",
],
- args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+ args: metalava_framework_docs_args +
+ // Needed for hidden libcore annotations for now.
+ " --ignore-classes-on-classpath " +
+ " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
write_sdk_values: true,
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 6cabc57..d4abeee 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -50,7 +50,9 @@
":art.module.public.api{.public.stubs.source}",
"**/package.html",
],
- sdk_version: "core_platform",
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
arg_files: ["core/res/AndroidManifest.xml"],
// TODO(b/147699819): remove below aidl includes.
aidl: {
@@ -77,6 +79,7 @@
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.vibrator-V1.3-java",
"framework-protos",
+ "stable.core.platform.api.stubs",
// There are a few classes from modules used as type arguments that
// need to be resolved by metalava. For now, we can use a previously
// finalized stub library to resolve them. If a new class gets added,
diff --git a/apex/blobstore/OWNERS b/apex/blobstore/OWNERS
index 8e04399..a53bbea 100644
--- a/apex/blobstore/OWNERS
+++ b/apex/blobstore/OWNERS
@@ -1,4 +1,2 @@
-set noparent
-
sudheersai@google.com
yamasani@google.com
diff --git a/apex/jobscheduler/OWNERS b/apex/jobscheduler/OWNERS
index d004eed..c77ea33 100644
--- a/apex/jobscheduler/OWNERS
+++ b/apex/jobscheduler/OWNERS
@@ -1,6 +1,7 @@
-yamasani@google.com
-omakoto@google.com
ctate@android.com
ctate@google.com
+dplotnikov@google.com
kwekua@google.com
-suprabh@google.com
\ No newline at end of file
+omakoto@google.com
+suprabh@google.com
+yamasani@google.com
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 878cef9..e21a6b2 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -181,7 +181,6 @@
"idmap2/Dump.cpp",
"idmap2/Lookup.cpp",
"idmap2/Main.cpp",
- "idmap2/Scan.cpp",
],
target: {
android: {
diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h
index 69eea8d..4099671 100644
--- a/cmds/idmap2/idmap2/Commands.h
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -26,6 +26,5 @@
android::idmap2::Result<android::idmap2::Unit> CreateMultiple(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Dump(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Lookup(const std::vector<std::string>& args);
-android::idmap2::Result<android::idmap2::Unit> Scan(const std::vector<std::string>& args);
#endif // IDMAP2_IDMAP2_COMMANDS_H_
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index c441709..437180d 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -45,11 +45,8 @@
using android::ApkAssetsCookie;
using android::AssetManager2;
using android::ConfigDescription;
-using android::is_valid_resid;
-using android::kInvalidCookie;
using android::Res_value;
using android::ResStringPool;
-using android::ResTable_config;
using android::StringPiece16;
using android::base::StringPrintf;
using android::idmap2::CommandLineOptions;
@@ -59,7 +56,6 @@
using android::idmap2::Result;
using android::idmap2::Unit;
using android::idmap2::utils::ExtractOverlayManifestInfo;
-using android::util::Utf16ToUtf8;
namespace {
@@ -69,25 +65,23 @@
// first, try to parse as a hex number
char* endptr = nullptr;
- ResourceId resid;
- resid = strtol(res.c_str(), &endptr, kBaseHex);
+ const ResourceId parsed_resid = strtol(res.c_str(), &endptr, kBaseHex);
if (*endptr == '\0') {
- return resid;
+ return parsed_resid;
}
// next, try to parse as a package:type/name string
- resid = am.GetResourceId(res, "", fallback_package);
- if (is_valid_resid(resid)) {
- return resid;
+ if (auto resid = am.GetResourceId(res, "", fallback_package); resid.ok()) {
+ return *resid;
}
// end of the road: res could not be parsed
return Error("failed to obtain resource id for %s", res.c_str());
}
-void PrintValue(AssetManager2* const am, const Res_value& value, const ApkAssetsCookie& cookie,
+void PrintValue(AssetManager2* const am, const AssetManager2::SelectedValue& value,
std::string* const out) {
- switch (value.dataType) {
+ switch (value.type) {
case Res_value::TYPE_INT_DEC:
out->append(StringPrintf("%d", value.data));
break;
@@ -98,30 +92,21 @@
out->append(value.data != 0 ? "true" : "false");
break;
case Res_value::TYPE_STRING: {
- const ResStringPool* pool = am->GetStringPoolForCookie(cookie);
+ const ResStringPool* pool = am->GetStringPoolForCookie(value.cookie);
out->append("\"");
- size_t len;
- if (pool->isUTF8()) {
- const char* str = pool->string8At(value.data, &len);
- out->append(str, len);
- } else {
- const char16_t* str16 = pool->stringAt(value.data, &len);
- out->append(Utf16ToUtf8(StringPiece16(str16, len)));
+ if (auto str = pool->string8ObjectAt(value.data); str.ok()) {
+ out->append(*str);
}
- out->append("\"");
} break;
default:
- out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
+ out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.type, value.data));
break;
}
}
Result<std::string> WARN_UNUSED GetValue(AssetManager2* const am, ResourceId resid) {
- Res_value value;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = am->GetResource(resid, true, 0, &value, &config, &flags);
- if (cookie == kInvalidCookie) {
+ auto value = am->GetResource(resid);
+ if (!value.has_value()) {
return Error("no resource 0x%08x in asset manager", resid);
}
@@ -129,41 +114,35 @@
// TODO(martenkongstad): use optional parameter GetResource(..., std::string*
// stacktrace = NULL) instead
- out.append(StringPrintf("cookie=%d ", cookie));
+ out.append(StringPrintf("cookie=%d ", value->cookie));
out.append("config='");
- out.append(config.toString().c_str());
+ out.append(value->config.toString().c_str());
out.append("' value=");
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- const android::ResolvedBag* bag = am->GetBag(static_cast<uint32_t>(value.data));
- if (bag == nullptr) {
- out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
+ if (value->type == Res_value::TYPE_REFERENCE) {
+ auto bag_result = am->GetBag(static_cast<uint32_t>(value->data));
+ if (!bag_result.has_value()) {
+ out.append(StringPrintf("dataType=0x%02x data=0x%08x", value->type, value->data));
return out;
}
+
out.append("[");
- Res_value bag_val;
- ResTable_config selected_config;
- uint32_t flags;
- uint32_t ref;
- ApkAssetsCookie bag_cookie;
+ const android::ResolvedBag* bag = bag_result.value();
for (size_t i = 0; i < bag->entry_count; ++i) {
- const android::ResolvedBag::Entry& entry = bag->entries[i];
- bag_val = entry.value;
- bag_cookie = am->ResolveReference(entry.cookie, &bag_val, &selected_config, &flags, &ref);
- if (bag_cookie == kInvalidCookie) {
- out.append(
- StringPrintf("Error: dataType=0x%02x data=0x%08x", bag_val.dataType, bag_val.data));
+ AssetManager2::SelectedValue entry(bag, bag->entries[i]);
+ if (am->ResolveReference(entry).has_value()) {
+ out.append(StringPrintf("Error: dataType=0x%02x data=0x%08x", entry.type, entry.data));
continue;
}
- PrintValue(am, bag_val, bag_cookie, &out);
+ PrintValue(am, entry, &out);
if (i != bag->entry_count - 1) {
out.append(", ");
}
}
out.append("]");
} else {
- PrintValue(am, value, cookie, &out);
+ PrintValue(am, *value, &out);
}
return out;
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index fb093f0..aa6d0e7 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -53,8 +53,10 @@
int main(int argc, char** argv) {
SYSTRACE << "main";
const NameToFunctionMap commands = {
- {"create", Create}, {"create-multiple", CreateMultiple}, {"dump", Dump}, {"lookup", Lookup},
- {"scan", Scan},
+ {"create", Create},
+ {"create-multiple", CreateMultiple},
+ {"dump", Dump},
+ {"lookup", Lookup},
};
if (argc <= 1) {
PrintUsage(commands, std::cerr);
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
deleted file mode 100644
index 3625045..0000000
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ /dev/null
@@ -1,257 +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 <dirent.h>
-
-#include <fstream>
-#include <memory>
-#include <ostream>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "Commands.h"
-#include "android-base/properties.h"
-#include "idmap2/CommandLineOptions.h"
-#include "idmap2/CommandUtils.h"
-#include "idmap2/FileUtils.h"
-#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
-#include "idmap2/PolicyUtils.h"
-#include "idmap2/ResourceUtils.h"
-#include "idmap2/Result.h"
-#include "idmap2/SysTrace.h"
-#include "idmap2/XmlParser.h"
-
-using android::idmap2::CommandLineOptions;
-using android::idmap2::Error;
-using android::idmap2::Idmap;
-using android::idmap2::Result;
-using android::idmap2::Unit;
-using android::idmap2::policy::kPolicyOdm;
-using android::idmap2::policy::kPolicyOem;
-using android::idmap2::policy::kPolicyProduct;
-using android::idmap2::policy::kPolicyPublic;
-using android::idmap2::policy::kPolicySystem;
-using android::idmap2::policy::kPolicyVendor;
-using android::idmap2::utils::ExtractOverlayManifestInfo;
-using android::idmap2::utils::FindFiles;
-using android::idmap2::utils::OverlayManifestInfo;
-using android::idmap2::utils::PoliciesToBitmaskResult;
-
-using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
-
-namespace {
-
-struct InputOverlay {
- bool operator<(InputOverlay const& rhs) const {
- return priority < rhs.priority || (priority == rhs.priority && apk_path < rhs.apk_path);
- }
-
- std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes)
- int priority; // NOLINT(misc-non-private-member-variables-in-classes)
- std::vector<std::string> policies; // NOLINT(misc-non-private-member-variables-in-classes)
- bool ignore_overlayable; // NOLINT(misc-non-private-member-variables-in-classes)
-};
-
-bool VendorIsQOrLater() {
- constexpr int kQSdkVersion = 29;
- constexpr int kBase = 10;
- std::string version_prop = android::base::GetProperty("ro.vndk.version", "29");
- int version = strtol(version_prop.data(), nullptr, kBase);
-
- // If the string cannot be parsed, it is a development sdk codename.
- return version >= kQSdkVersion || version == 0;
-}
-
-Result<std::unique_ptr<std::vector<std::string>>> FindApkFiles(const std::vector<std::string>& dirs,
- bool recursive) {
- SYSTRACE << "FindApkFiles " << dirs << " " << recursive;
- const auto predicate = [](unsigned char type, const std::string& path) -> bool {
- static constexpr size_t kExtLen = 4; // strlen(".apk")
- return type == DT_REG && path.size() > kExtLen &&
- path.compare(path.size() - kExtLen, kExtLen, ".apk") == 0;
- };
- // pass apk paths through a set to filter out duplicates
- std::set<std::string> paths;
- for (const auto& dir : dirs) {
- const auto apk_paths = FindFiles(dir, recursive, predicate);
- if (!apk_paths) {
- return Error("failed to open directory %s", dir.c_str());
- }
- paths.insert(apk_paths->cbegin(), apk_paths->cend());
- }
- return std::make_unique<std::vector<std::string>>(paths.cbegin(), paths.cend());
-}
-
-std::vector<std::string> PoliciesForPath(const std::string& apk_path) {
- // clang-format off
- static const std::vector<std::pair<std::string, std::string>> values = {
- {"/odm/", kPolicyOdm},
- {"/oem/", kPolicyOem},
- {"/product/", kPolicyProduct},
- {"/system/", kPolicySystem},
- {"/system_ext/", kPolicySystem},
- {"/vendor/", kPolicyVendor},
- };
- // clang-format on
-
- std::vector<std::string> fulfilled_policies = {kPolicyPublic};
- for (auto const& pair : values) {
- if (apk_path.compare(0, pair.first.size(), pair.first) == 0) {
- fulfilled_policies.emplace_back(pair.second);
- break;
- }
- }
-
- return fulfilled_policies;
-}
-
-} // namespace
-
-Result<Unit> Scan(const std::vector<std::string>& args) {
- SYSTRACE << "Scan " << args;
- std::vector<std::string> input_directories;
- std::string target_package_name;
- std::string target_apk_path;
- std::string output_directory;
- std::vector<std::string> override_policies;
- bool recursive = false;
-
- const CommandLineOptions opts =
- CommandLineOptions("idmap2 scan")
- .MandatoryOption("--input-directory", "directory containing overlay apks to scan",
- &input_directories)
- .OptionalFlag("--recursive", "also scan subfolders of overlay-directory", &recursive)
- .MandatoryOption("--target-package-name", "package name of target package",
- &target_package_name)
- .MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path)
- .MandatoryOption("--output-directory",
- "directory in which to write artifacts (idmap files and overlays.list)",
- &output_directory)
- .OptionalOption(
- "--override-policy",
- "input: an overlayable policy this overlay fulfills "
- "(if none or supplied, the overlays will not have their policies overriden",
- &override_policies);
- const auto opts_ok = opts.Parse(args);
- if (!opts_ok) {
- return opts_ok.GetError();
- }
-
- const auto apk_paths = FindApkFiles(input_directories, recursive);
- if (!apk_paths) {
- return Error(apk_paths.GetError(), "failed to find apk files");
- }
-
- std::vector<InputOverlay> interesting_apks;
- for (const std::string& path : **apk_paths) {
- Result<OverlayManifestInfo> overlay_info =
- ExtractOverlayManifestInfo(path, /* assert_overlay */ false);
- if (!overlay_info) {
- return overlay_info.GetError();
- }
-
- if (!overlay_info->is_static) {
- continue;
- }
-
- if (overlay_info->target_package.empty() ||
- overlay_info->target_package != target_package_name) {
- continue;
- }
-
- if (overlay_info->priority < 0) {
- continue;
- }
-
- // Note that conditional property enablement/exclusion only applies if
- // the attribute is present. In its absence, all overlays are presumed enabled.
- if (!overlay_info->requiredSystemPropertyName.empty() &&
- !overlay_info->requiredSystemPropertyValue.empty()) {
- // if property set & equal to value, then include overlay - otherwise skip
- if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "") !=
- overlay_info->requiredSystemPropertyValue) {
- continue;
- }
- }
-
- std::vector<std::string> fulfilled_policies;
- if (!override_policies.empty()) {
- fulfilled_policies = override_policies;
- } else {
- fulfilled_policies = PoliciesForPath(path);
- }
-
- bool ignore_overlayable = false;
- if (std::find(fulfilled_policies.begin(), fulfilled_policies.end(), kPolicyVendor) !=
- fulfilled_policies.end() &&
- !VendorIsQOrLater()) {
- // 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.
- ignore_overlayable = true;
- }
-
- std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path);
-
- // Sort the static overlays in ascending priority order
- InputOverlay input{path, idmap_path, overlay_info->priority, fulfilled_policies,
- ignore_overlayable};
- interesting_apks.insert(
- std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input);
- }
-
- std::stringstream stream;
- for (const auto& overlay : interesting_apks) {
- const auto policy_bitmask = PoliciesToBitmaskResult(overlay.policies);
- if (!policy_bitmask) {
- LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path
- << "\": " << policy_bitmask.GetErrorMessage();
- continue;
- }
-
- if (!Verify(overlay.idmap_path, target_apk_path, overlay.apk_path, *policy_bitmask,
- !overlay.ignore_overlayable)) {
- std::vector<std::string> create_args = {"--target-apk-path", target_apk_path,
- "--overlay-apk-path", overlay.apk_path,
- "--idmap-path", overlay.idmap_path};
- if (overlay.ignore_overlayable) {
- create_args.emplace_back("--ignore-overlayable");
- }
-
- for (const std::string& policy : overlay.policies) {
- create_args.emplace_back("--policy");
- create_args.emplace_back(policy);
- }
-
- const auto create_ok = Create(create_args);
- if (!create_ok) {
- LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path
- << "\": " << create_ok.GetError().GetMessage();
- continue;
- }
- }
-
- stream << overlay.idmap_path << std::endl;
- }
-
- std::cout << stream.str();
-
- return Unit{};
-}
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index 3f03236..c4e0e1f 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -17,27 +17,13 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
-#include <sys/types.h>
-
-#include <functional>
-#include <memory>
#include <string>
-#include <vector>
namespace android::idmap2::utils {
constexpr const char* kIdmapCacheDir = "/data/resource-cache";
constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r
-typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)>
- FindFilesPredicate;
-std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
- const FindFilesPredicate& predicate);
-
-std::unique_ptr<std::string> ReadFile(int fd);
-
-std::unique_ptr<std::string> ReadFile(const std::string& path);
-
bool UidHasWriteAccessToPath(uid_t uid, const std::string& path);
} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
index 0a58ec4..f66916c 100644
--- a/cmds/idmap2/include/idmap2/ResourceMapping.h
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -117,7 +117,8 @@
static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am,
const AssetManager2* overlay_am,
const LoadedPackage* target_package,
- const LoadedPackage* overlay_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,
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
index 3e8e329..3af1f70 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -16,19 +16,7 @@
#include "idmap2/FileUtils.h"
-#include <dirent.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cerrno>
-#include <climits>
-#include <cstdlib>
-#include <cstring>
-#include <fstream>
-#include <memory>
#include <string>
-#include <utility>
-#include <vector>
#include "android-base/file.h"
#include "android-base/macros.h"
@@ -37,54 +25,6 @@
namespace android::idmap2::utils {
-std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
- const FindFilesPredicate& predicate) {
- DIR* dir = opendir(root.c_str());
- if (dir == nullptr) {
- return nullptr;
- }
- std::unique_ptr<std::vector<std::string>> vector(new std::vector<std::string>());
- struct dirent* dirent;
- while ((dirent = readdir(dir)) != nullptr) {
- const std::string path = root + "/" + dirent->d_name;
- if (predicate(dirent->d_type, path)) {
- vector->push_back(path);
- }
- if (recurse && dirent->d_type == DT_DIR && strcmp(dirent->d_name, ".") != 0 &&
- strcmp(dirent->d_name, "..") != 0) {
- auto sub_vector = FindFiles(path, recurse, predicate);
- if (!sub_vector) {
- closedir(dir);
- return nullptr;
- }
- vector->insert(vector->end(), sub_vector->begin(), sub_vector->end());
- }
- }
- closedir(dir);
-
- return vector;
-}
-
-std::unique_ptr<std::string> ReadFile(const std::string& path) {
- std::unique_ptr<std::string> str(new std::string());
- std::ifstream fin(path);
- str->append({std::istreambuf_iterator<char>(fin), std::istreambuf_iterator<char>()});
- fin.close();
- return str;
-}
-
-std::unique_ptr<std::string> ReadFile(int fd) {
- static constexpr const size_t kBufSize = 1024;
-
- std::unique_ptr<std::string> str(new std::string());
- char buf[kBufSize];
- ssize_t r;
- while ((r = read(fd, buf, sizeof(buf))) > 0) {
- str->append(buf, r);
- }
- return r == 0 ? std::move(str) : nullptr;
-}
-
#ifdef __ANDROID__
bool UidHasWriteAccessToPath(uid_t uid, const std::string& path) {
// resolve symlinks and relative paths; the directories must exist
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index a93202a..3037a79 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -100,10 +100,9 @@
stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id)
<< utils::DataTypeToString(target_entry.value.data_type);
- size_t unused;
if (target_entry.value.data_type == Res_value::TYPE_STRING) {
- auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset, &unused);
- stream_ << " \"" << StringPiece16(str) << "\"";
+ auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset);
+ stream_ << " \"" << str.value_or(StringPiece16(u"")) << "\"";
} else {
stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value);
}
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 31f1c16..d777cbf 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -71,9 +71,10 @@
if (!target_package.DefinesOverlayable()) {
return (sDefaultPolicies & fulfilled_policies) != 0
? Result<Unit>({})
- : Error("overlay must be preinstalled, signed with the same signature as the target,"
- " or signed with the same signature as the package referenced through"
- " <overlay-config-signature>.");
+ : Error(
+ "overlay must be preinstalled, signed with the same signature as the target,"
+ " or signed with the same signature as the package referenced through"
+ " <overlay-config-signature>.");
}
const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
@@ -119,32 +120,25 @@
Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
const AssetManager2& asset_manager) {
- Res_value value{};
- ResTable_config selected_config{};
- uint32_t flags;
- auto cookie =
- asset_manager.GetResource(resource_id, /* may_be_bag */ false,
- /* density_override */ 0U, &value, &selected_config, &flags);
- if (cookie == kInvalidCookie) {
+ 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.dataType != Res_value::TYPE_STRING) {
+ 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(cookie);
- size_t len;
- auto file_path16 = string_pool->stringAt(value.data, &len);
- if (file_path16 == nullptr) {
- return Error("failed to find string for index %d", value.data);
+ 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);
}
// Load the overlay resource mappings from the file specified using android:resourcesMap.
- auto file_path = String8(String16(file_path16));
- auto asset = asset_manager.OpenNonAsset(file_path.c_str(), Asset::AccessMode::ACCESS_BUFFER);
+ auto asset = asset_manager.OpenNonAsset(file->c_str(), Asset::AccessMode::ACCESS_BUFFER);
if (asset == nullptr) {
- return Error("file \"%s\" not found", file_path.c_str());
+ return Error("file \"%s\" not found", file->c_str());
}
return asset;
@@ -190,16 +184,16 @@
return Error(R"(<item> tag missing expected attribute "value")");
}
- ResourceId target_id =
+ auto target_id_result =
target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
- if (target_id == 0U) {
+ if (!target_id_result.has_value()) {
log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource
<< "\" in target resources");
continue;
}
// Retrieve the compile-time resource id of the target resource.
- target_id = REWRITE_PACKAGE(target_id, target_package_id);
+ 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;
@@ -220,7 +214,7 @@
Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
const AssetManager2* target_am, const AssetManager2* overlay_am,
- const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
+ 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();
@@ -234,13 +228,15 @@
// 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());
- ResourceId target_resource = target_am->GetResourceId(full_name);
- if (target_resource == 0U) {
+ 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;
}
// Retrieve the compile-time resource id of the target resource.
- target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
+ ResourceId target_resource = REWRITE_PACKAGE(*target_resource_result, target_package_id);
resource_mapping.AddMapping(target_resource, overlay_resid,
false /* rewrite_overlay_reference */);
}
@@ -347,7 +343,9 @@
auto& string_pool = (*parser)->get_strings();
string_pool_data_length = string_pool.bytes();
string_pool_data.reset(new uint8_t[string_pool_data_length]);
- memcpy(string_pool_data.get(), string_pool.data(), 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();
@@ -358,7 +356,7 @@
// 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);
+ target_pkg, overlay_pkg, log_info);
}
if (!resource_mapping) {
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index 98d026b..e817140 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -72,21 +72,21 @@
}
Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) {
- AssetManager2::ResourceName name;
- if (!am.GetResourceName(resid, &name)) {
+ const auto name = am.GetResourceName(resid);
+ if (!name.has_value()) {
return Error("no resource 0x%08x in asset manager", resid);
}
std::string out;
- if (name.type != nullptr) {
- out.append(name.type, name.type_len);
+ if (name->type != nullptr) {
+ out.append(name->type, name->type_len);
} else {
- out += Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
+ out += Utf16ToUtf8(StringPiece16(name->type16, name->type_len));
}
out.append("/");
- if (name.entry != nullptr) {
- out.append(name.entry, name.entry_len);
+ if (name->entry != nullptr) {
+ out.append(name->entry, name->entry_len);
} else {
- out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
+ out += Utf16ToUtf8(StringPiece16(name->entry16, name->entry_len));
}
return out;
}
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
index 526a560..4030b83 100644
--- a/cmds/idmap2/libidmap2/XmlParser.cpp
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -98,18 +98,19 @@
switch ((*value).dataType) {
case Res_value::TYPE_STRING: {
- size_t len;
- const String16 value16(parser_.getStrings().stringAt((*value).data, &len));
- return std::string(String8(value16).c_str());
+ if (auto str = parser_.getStrings().string8ObjectAt((*value).data); str.ok()) {
+ return std::string(str->string());
+ }
+ break;
}
case Res_value::TYPE_INT_DEC:
case Res_value::TYPE_INT_HEX:
case Res_value::TYPE_INT_BOOLEAN: {
return std::to_string((*value).data);
}
- default:
- return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str());
}
+
+ return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str());
}
Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index 8af4037..5750ca1 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -14,73 +14,16 @@
* limitations under the License.
*/
-#include <dirent.h>
-#include <fcntl.h>
-
-#include <set>
#include <string>
#include "TestHelpers.h"
-#include "android-base/macros.h"
#include "android-base/stringprintf.h"
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/FileUtils.h"
#include "private/android_filesystem_config.h"
-using ::testing::NotNull;
-
namespace android::idmap2::utils {
-TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
- const auto& root = GetTestDataPath();
- auto v = utils::FindFiles(root, false,
- [](unsigned char type ATTRIBUTE_UNUSED,
- const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
- ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 7U);
- ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), std::set<std::string>({
- root + "/.",
- root + "/..",
- root + "/overlay",
- root + "/target",
- root + "/signature-overlay",
- root + "/system-overlay",
- root + "/system-overlay-invalid",
- }));
-}
-
-TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
- const auto& root = GetTestDataPath();
- auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool {
- return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
- });
- ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 11U);
- ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
- std::set<std::string>(
- {root + "/target/target.apk", root + "/target/target-no-overlayable.apk",
- root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk",
- root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-shared.apk",
- root + "/overlay/overlay-static-1.apk", root + "/overlay/overlay-static-2.apk",
- root + "/signature-overlay/signature-overlay.apk",
- root + "/system-overlay/system-overlay.apk",
- root + "/system-overlay-invalid/system-overlay-invalid.apk"}));
-}
-
-TEST(FileUtilsTests, ReadFile) {
- int pipefd[2];
- ASSERT_EQ(pipe2(pipefd, O_CLOEXEC), 0);
-
- ASSERT_EQ(write(pipefd[1], "foobar", 6), 6);
- close(pipefd[1]);
-
- auto data = ReadFile(pipefd[0]);
- ASSERT_THAT(data, NotNull());
- ASSERT_EQ(*data, "foobar");
- close(pipefd[0]);
-}
-
#ifdef __ANDROID__
TEST(FileUtilsTests, UidHasWriteAccessToPath) {
constexpr const char* tmp_path = "/data/local/tmp/test@idmap";
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index eba102da..e7e9e4c 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -159,131 +159,6 @@
unlink(GetIdmapPath().c_str());
}
-TEST_F(Idmap2BinaryTests, Scan) {
- SKIP_TEST_IF_CANT_EXEC_IDMAP2;
-
- const std::string overlay_static_no_name_apk_path =
- GetTestDataPath() + "/overlay/overlay-no-name-static.apk";
- const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
- const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
- const std::string idmap_static_no_name_path =
- Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_no_name_apk_path);
- const std::string idmap_static_1_path =
- Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_1_apk_path);
- const std::string idmap_static_2_path =
- Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_2_apk_path);
-
- // single input directory, recursive
- // clang-format off
- auto result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath(),
- "--recursive",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- std::stringstream expected;
- expected << idmap_static_no_name_path << std::endl;
- expected << idmap_static_1_path << std::endl;
- expected << idmap_static_2_path << std::endl;
- ASSERT_EQ(result->stdout, expected.str());
-
- auto idmap_static_no_name_raw_string = utils::ReadFile(idmap_static_no_name_path);
- auto idmap_static_no_name_raw_stream = std::istringstream(*idmap_static_no_name_raw_string);
- auto idmap_static_no_name = Idmap::FromBinaryStream(idmap_static_no_name_raw_stream);
- ASSERT_TRUE(idmap_static_no_name);
- ASSERT_IDMAP(**idmap_static_no_name, GetTargetApkPath(), overlay_static_no_name_apk_path);
-
- auto idmap_static_1_raw_string = utils::ReadFile(idmap_static_1_path);
- auto idmap_static_1_raw_stream = std::istringstream(*idmap_static_1_raw_string);
- auto idmap_static_1 = Idmap::FromBinaryStream(idmap_static_1_raw_stream);
- ASSERT_TRUE(idmap_static_1);
- ASSERT_IDMAP(**idmap_static_1, GetTargetApkPath(), overlay_static_1_apk_path);
-
- auto idmap_static_2_raw_string = utils::ReadFile(idmap_static_2_path);
- auto idmap_static_2_raw_stream = std::istringstream(*idmap_static_2_raw_string);
- auto idmap_static_2 = Idmap::FromBinaryStream(idmap_static_2_raw_stream);
- ASSERT_TRUE(idmap_static_2);
- ASSERT_IDMAP(**idmap_static_2, GetTargetApkPath(), overlay_static_2_apk_path);
-
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-
- // multiple input directories, non-recursive
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath() + "/target",
- "--input-directory", GetTestDataPath() + "/overlay",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, expected.str());
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-
- // the same input directory given twice, but no duplicate entries
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath(),
- "--input-directory", GetTestDataPath(),
- "--recursive",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, expected.str());
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-
- // no APKs in input-directory: ok, but no output
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTempDirPath(),
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, "");
-
- // the signature idmap failing to generate should not cause scanning to fail
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath(),
- "--recursive",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, expected.str());
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-}
-
TEST_F(Idmap2BinaryTests, Lookup) {
SKIP_TEST_IF_CANT_EXEC_IDMAP2;
diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh
index b4ebab0..84daeec 100755
--- a/cmds/idmap2/valgrind.sh
+++ b/cmds/idmap2/valgrind.sh
@@ -53,7 +53,5 @@
_eval "idmap2 create" "$valgrind idmap2 create --policy public --target-apk-path $target_path --overlay-apk-path $overlay_path --idmap-path $idmap_path"
_eval "idmap2 dump" "$valgrind idmap2 dump --idmap-path $idmap_path"
_eval "idmap2 lookup" "$valgrind idmap2 lookup --idmap-path $idmap_path --config '' --resid test.target:string/str1"
-_eval "idmap2 scan" "$valgrind idmap2 scan --input-directory ${prefix}/tests/data/overlay --recursive --target-package-name test.target --target-apk-path $target_path --output-directory /tmp --override-policy public"
-_eval "idmap2 verify" "$valgrind idmap2 verify --idmap-path $idmap_path"
_eval "idmap2_tests" "$valgrind $ANDROID_HOST_OUT/nativetest64/idmap2_tests/idmap2_tests"
exit $errors
diff --git a/config/preloaded-classes b/config/preloaded-classes
index d56fc77..5e88d97d 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -3716,9 +3716,9 @@
android.media.IRingtonePlayer$Stub$Proxy
android.media.IRingtonePlayer$Stub
android.media.IRingtonePlayer
-android.media.IStrategyPreferredDeviceDispatcher$Stub$Proxy
-android.media.IStrategyPreferredDeviceDispatcher$Stub
-android.media.IStrategyPreferredDeviceDispatcher
+android.media.IStrategyPreferredDevicesDispatcher$Stub$Proxy
+android.media.IStrategyPreferredDevicesDispatcher$Stub
+android.media.IStrategyPreferredDevicesDispatcher
android.media.IVolumeController$Stub$Proxy
android.media.IVolumeController$Stub
android.media.IVolumeController
@@ -11759,7 +11759,6 @@
libcore.util.ZoneInfo$CheckedArithmeticException
libcore.util.ZoneInfo$WallTime
libcore.util.ZoneInfo
-org.apache.harmony.dalvik.NativeTestTarget
org.apache.harmony.dalvik.ddmc.Chunk
org.apache.harmony.dalvik.ddmc.ChunkHandler
org.apache.harmony.dalvik.ddmc.DdmServer
diff --git a/core/api/current.txt b/core/api/current.txt
index d7c0213..74d313c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12142,6 +12142,7 @@
field public static final String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef";
field public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = "android.hardware.nfc.ese";
field public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC = "android.hardware.nfc.uicc";
+ field public static final String FEATURE_OPENGLES_DEQP_LEVEL = "android.software.opengles.deqp.level";
field public static final String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
field public static final String FEATURE_PC = "android.hardware.type.pc";
field public static final String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
@@ -29588,6 +29589,7 @@
field public static final String ID;
field public static final String MANUFACTURER;
field public static final String MODEL;
+ field @NonNull public static final String ODM_SKU;
field public static final String PRODUCT;
field @Deprecated public static final String RADIO;
field @Deprecated public static final String SERIAL;
@@ -33223,8 +33225,8 @@
}
protected static interface ContactsContract.DataColumns {
- field public static final String CARRIER_PRESENCE = "carrier_presence";
- field public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1
+ field @Deprecated public static final String CARRIER_PRESENCE = "carrier_presence";
+ field @Deprecated public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1
field public static final String DATA1 = "data1";
field public static final String DATA10 = "data10";
field public static final String DATA11 = "data11";
@@ -40252,9 +40254,10 @@
field public static final String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool";
field public static final String KEY_TTY_SUPPORTED_BOOL = "tty_supported_bool";
field public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY = "unloggable_numbers_string_array";
+ field public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
- field public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+ field @Deprecated public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
field public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = "use_wfc_home_network_mode_in_roaming_network_bool";
field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
@@ -40295,9 +40298,12 @@
}
public static final class CarrierConfigManager.Ims {
+ field public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = "ims.enable_presence_capability_exchange_bool";
+ field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
field public static final String KEY_PREFIX = "ims.";
+ field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
}
@@ -40505,7 +40511,8 @@
public final class CellSignalStrengthLte extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
method public int getAsuLevel();
- method public int getCqi();
+ method @IntRange(from=0, to=15) public int getCqi();
+ method @IntRange(from=1, to=6) public int getCqiTableIndex();
method public int getDbm();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
method public int getRsrp();
@@ -40520,6 +40527,8 @@
public final class CellSignalStrengthNr extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
method public int getAsuLevel();
+ method @IntRange(from=0, to=15) @NonNull public java.util.List<java.lang.Integer> getCsiCqiReport();
+ method @IntRange(from=1, to=3) public int getCsiCqiTableIndex();
method public int getCsiRsrp();
method public int getCsiRsrq();
method public int getCsiSinr();
@@ -40687,6 +40696,28 @@
field public static final int IP_VERSION_MISMATCH = 2055; // 0x807
field public static final int IRAT_HANDOVER_FAILED = 2194; // 0x892
field public static final int IS707B_MAX_ACCESS_PROBES = 2089; // 0x829
+ field public static final int IWLAN_AUTHORIZATION_REJECTED = 9003; // 0x232b
+ field public static final int IWLAN_DNS_RESOLUTION_NAME_FAILURE = 16388; // 0x4004
+ field public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 16389; // 0x4005
+ field public static final int IWLAN_IKEV2_AUTH_FAILURE = 16385; // 0x4001
+ field public static final int IWLAN_IKEV2_CERT_INVALID = 16387; // 0x4003
+ field public static final int IWLAN_IKEV2_CONFIG_FAILURE = 16384; // 0x4000
+ field public static final int IWLAN_IKEV2_MSG_TIMEOUT = 16386; // 0x4002
+ field public static final int IWLAN_ILLEGAL_ME = 9006; // 0x232e
+ field public static final int IWLAN_IMEI_NOT_ACCEPTED = 11005; // 0x2afd
+ field public static final int IWLAN_MAX_CONNECTION_REACHED = 8193; // 0x2001
+ field public static final int IWLAN_NETWORK_FAILURE = 10500; // 0x2904
+ field public static final int IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED = 9000; // 0x2328
+ field public static final int IWLAN_NO_APN_SUBSCRIPTION = 9002; // 0x232a
+ field public static final int IWLAN_PDN_CONNECTION_REJECTION = 8192; // 0x2000
+ field public static final int IWLAN_PLMN_NOT_ALLOWED = 11011; // 0x2b03
+ field public static final int IWLAN_RAT_TYPE_NOT_ALLOWED = 11001; // 0x2af9
+ field public static final int IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS = 8244; // 0x2034
+ field public static final int IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 8241; // 0x2031
+ field public static final int IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS = 8245; // 0x2035
+ field public static final int IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION = 8242; // 0x2032
+ field public static final int IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 11055; // 0x2b2f
+ field public static final int IWLAN_USER_UNKNOWN = 9001; // 0x2329
field public static final int LIMITED_TO_IPV4 = 2234; // 0x8ba
field public static final int LIMITED_TO_IPV6 = 2235; // 0x8bb
field public static final int LLC_SNDCP = 25; // 0x19
@@ -57294,6 +57325,7 @@
method public int getModifiers();
method @NonNull public String getName();
method @Nullable public Package getPackage();
+ method @NonNull public String getPackageName();
method @Nullable public java.security.ProtectionDomain getProtectionDomain();
method @Nullable public java.net.URL getResource(@NonNull String);
method @Nullable public java.io.InputStream getResourceAsStream(@NonNull String);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index e857951..05d7bb6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -237,7 +237,9 @@
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
+ field public static final String WIFI_ACCESS_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS";
field public static final String WIFI_SET_DEVICE_MOBILITY_STATE = "android.permission.WIFI_SET_DEVICE_MOBILITY_STATE";
+ field public static final String WIFI_UPDATE_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS";
field public static final String WIFI_UPDATE_USABILITY_STATS_SCORE = "android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
field public static final String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
@@ -1577,6 +1579,7 @@
field @NonNull public static final android.os.ParcelUuid HOGP;
field @NonNull public static final android.os.ParcelUuid HSP;
field @NonNull public static final android.os.ParcelUuid HSP_AG;
+ field @NonNull public static final android.os.ParcelUuid LE_AUDIO;
field @NonNull public static final android.os.ParcelUuid MAP;
field @NonNull public static final android.os.ParcelUuid MAS;
field @NonNull public static final android.os.ParcelUuid MNS;
@@ -1873,6 +1876,9 @@
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
method public boolean isEncryptionAware();
method public boolean isInstantApp();
+ method public boolean isOem();
+ method public boolean isProduct();
+ method public boolean isVendor();
field public String credentialProtectedDataDir;
field public int targetSandboxVersion;
}
@@ -4139,7 +4145,8 @@
public class AudioManager {
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
method public void clearAudioServerStateCallback();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
@@ -4150,13 +4157,15 @@
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method public boolean isAudioServerRunning();
method public boolean isHdmiSystemAudioSupported();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
@@ -4165,6 +4174,7 @@
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -4183,8 +4193,12 @@
method public void onAudioServerUp();
}
- public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
- method public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
+ @Deprecated public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
+ method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
+ }
+
+ public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener {
+ method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
}
public abstract static class AudioManager.VolumeGroupCallback {
@@ -6401,6 +6415,19 @@
method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
}
+ public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
+ ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException;
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR;
+ field public final int ipTos;
+ field public final int ipTtl;
+ field public final int tcpAck;
+ field public final int tcpSeq;
+ field public final int tcpWindow;
+ field public final int tcpWindowScale;
+ }
+
public class TrafficStats {
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();
@@ -7001,6 +7028,7 @@
public abstract static class BugreportManager.BugreportCallback {
ctor public BugreportManager.BugreportCallback();
+ method public void onEarlyReportFinished();
method public void onError(int);
method public void onFinished();
method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float);
@@ -7328,12 +7356,14 @@
public class RecoverySystem {
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void cancelScheduledUpdate(android.content.Context) throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void clearPrepareForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void clearPrepareForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void installPackage(android.content.Context, java.io.File, boolean) throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static boolean isPreparedForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
@@ -7885,6 +7915,7 @@
field public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
+ field public static final String NAMESPACE_OTA = "ota";
field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
field public static final String NAMESPACE_PERMISSIONS = "permissions";
field public static final String NAMESPACE_PRIVACY = "privacy";
@@ -11425,18 +11456,29 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRcsVolteSingleRegistrationCapable() throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerRcsProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void triggerRcsReconfiguration();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterRcsProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback);
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE = "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE";
+ field public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS";
+ field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.ims.extra.SUBSCRIPTION_ID";
field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43
field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
+ field public static final int STATUS_CAPABLE = 0; // 0x0
+ field public static final int STATUS_CARRIER_NOT_CAPABLE = 2; // 0x2
+ field public static final int STATUS_DEVICE_NOT_CAPABLE = 1; // 0x1
field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
}
@@ -11447,6 +11489,27 @@
method public void onProvisioningStringChanged(int, @NonNull String);
}
+ public static class ProvisioningManager.RcsProvisioningCallback {
+ ctor public ProvisioningManager.RcsProvisioningCallback();
+ method public void onAutoConfigurationErrorReceived(int, @NonNull String);
+ method public void onConfigurationChanged(@NonNull byte[]);
+ method public void onConfigurationReset();
+ method public void onRemoved();
+ }
+
+ public final class RcsClientConfiguration implements android.os.Parcelable {
+ ctor public RcsClientConfiguration(@NonNull String, @NonNull String, @NonNull String, @NonNull String);
+ method public int describeContents();
+ method @NonNull public String getClientVendor();
+ method @NonNull public String getClientVersion();
+ method @NonNull public String getRcsProfile();
+ method @NonNull public String getRcsVersion();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsClientConfiguration> CREATOR;
+ field public static final String RCS_PROFILE_1_0 = "UP_1.0";
+ field public static final String RCS_PROFILE_2_3 = "UP_2.3";
+ }
+
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
@@ -11539,6 +11602,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
+ method public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String);
field public static final int DENIED_REASON_INVALID = 4; // 0x4
field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1
field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2
@@ -11734,11 +11798,15 @@
ctor public ImsConfigImplBase();
method public int getConfigInt(int);
method public String getConfigString(int);
+ method public final void notifyAutoConfigurationErrorReceived(int, @NonNull String);
method public final void notifyProvisionedValueChanged(int, int);
method public final void notifyProvisionedValueChanged(int, String);
method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
+ method public void notifyRcsAutoConfigurationRemoved();
method public int setConfig(int, int);
method public int setConfig(int, String);
+ method public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration);
+ method public void triggerAutoConfiguration();
field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
field public static final int CONFIG_RESULT_SUCCESS = 0; // 0x0
field public static final int CONFIG_RESULT_UNKNOWN = -1; // 0xffffffff
@@ -11783,6 +11851,9 @@
method public final void onRegistering(int);
method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]);
method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
+ method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String);
+ method public void triggerSipDelegateDeregistration();
+ method public void updateSipDelegateRegistration();
field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index feadd06..b6bd687 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -871,6 +871,9 @@
method public static float getMasterBalance();
method public static final int getNumStreamTypes();
method public static int setMasterBalance(float);
+ field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2
+ field public static final int DEVICE_ROLE_NONE = 0; // 0x0
+ field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1
field public static final int STREAM_DEFAULT = -1; // 0xffffffff
}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index abdd537..06ad9c9 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -15,22 +15,18 @@
# Notification
per-file *Notification* = file:/packages/SystemUI/OWNERS
-#Wallpaper
-per-file Wallpaper*.java = file:/core/java/android/service/wallpaper/OWNERS
-per-file IWallpaper*.aidl = file:/core/java/android/service/wallpaper/OWNERS
+# ResourcesManager
+per-file ResourcesManager = rtmitchell@google.com, toddke@google.com
+
+# Wallpaper
+per-file *Wallpaper* = file:/core/java/android/service/wallpaper/OWNERS
# WindowManager
-per-file Activity*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
-per-file Activity*.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Activity* = file:/services/core/java/com/android/server/wm/OWNERS
per-file ClientTransactionHandler.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file Fragment.java = file:/services/core/java/com/android/server/wm/OWNERS
-per-file IActivity*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
-per-file IAppTask.aidl = file:/services/core/java/com/android/server/wm/OWNERS
-per-file ITaskStackListener.aidl = file:/services/core/java/com/android/server/wm/OWNERS
-per-file LocalActivityManager.java = file:/services/core/java/com/android/server/wm/OWNERS
-per-file Task*.java = file:/services/core/java/com/android/server/wm/OWNERS
-per-file Window*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
-per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Task* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file Window* = file:/services/core/java/com/android/server/wm/OWNERS
# TODO(b/174932174): determine the ownership of KeyguardManager.java
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 496ac3b..82e48bf 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -113,7 +113,6 @@
import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityManager;
-import android.net.ConnectivityThread;
import android.net.EthernetManager;
import android.net.IConnectivityManager;
import android.net.IEthernetManager;
@@ -768,8 +767,7 @@
public LowpanManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.LOWPAN_SERVICE);
ILowpanManager service = ILowpanManager.Stub.asInterface(b);
- return new LowpanManager(ctx.getOuterContext(), service,
- ConnectivityThread.getInstanceLooper());
+ return new LowpanManager(ctx.getOuterContext(), service);
}});
registerService(Context.ETHERNET_SERVICE, EthernetManager.class,
diff --git a/core/java/android/app/admin/OWNERS b/core/java/android/app/admin/OWNERS
index 64a1d27..8462cbe 100644
--- a/core/java/android/app/admin/OWNERS
+++ b/core/java/android/app/admin/OWNERS
@@ -1,4 +1,11 @@
# Bug component: 142675
-yamasani@google.com
+# Android Enterprise team
rubinxu@google.com
+sandness@google.com
+eranm@google.com
+alexkershaw@google.com
+pgrafov@google.com
+
+# Emeritus
+yamasani@google.com
diff --git a/core/java/android/app/time/OWNERS b/core/java/android/app/time/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/java/android/app/time/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/core/java/android/app/timedetector/GnssTimeSuggestion.aidl
similarity index 66%
copy from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
copy to core/java/android/app/timedetector/GnssTimeSuggestion.aidl
index b1f99e6..81475ec 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/core/java/android/app/timedetector/GnssTimeSuggestion.aidl
@@ -14,17 +14,6 @@
* limitations under the License.
*/
-package android.media;
+package android.app.timedetector;
-import android.media.AudioDeviceAttributes;
-
-/**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
- *
- * {@hide}
- */
-oneway interface IStrategyPreferredDeviceDispatcher {
-
- void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
-
-}
+parcelable GnssTimeSuggestion;
diff --git a/core/java/android/app/timedetector/GnssTimeSuggestion.java b/core/java/android/app/timedetector/GnssTimeSuggestion.java
new file mode 100644
index 0000000..6478a2d
--- /dev/null
+++ b/core/java/android/app/timedetector/GnssTimeSuggestion.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.TimestampedValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A time signal from a GNSS source.
+ *
+ * <p>{@code utcTime} is the suggested time. The {@code utcTime.value} is the number of milliseconds
+ * elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the value of the
+ * elapsed realtime clock when the {@code utcTime.value} was established.
+ * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
+ * suggestions cannot be persisted across device resets.
+ *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists and how it was entered. This information exists only to aid in
+ * debugging and therefore is used by {@link #toString()}, but it is not for use in detection
+ * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
+ *
+ * @hide
+ */
+public final class GnssTimeSuggestion implements Parcelable {
+
+ public static final @NonNull Creator<GnssTimeSuggestion> CREATOR =
+ new Creator<GnssTimeSuggestion>() {
+ public GnssTimeSuggestion createFromParcel(Parcel in) {
+ return GnssTimeSuggestion.createFromParcel(in);
+ }
+
+ public GnssTimeSuggestion[] newArray(int size) {
+ return new GnssTimeSuggestion[size];
+ }
+ };
+
+ @NonNull private final TimestampedValue<Long> mUtcTime;
+ @Nullable private ArrayList<String> mDebugInfo;
+
+ public GnssTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
+ mUtcTime = Objects.requireNonNull(utcTime);
+ Objects.requireNonNull(utcTime.getValue());
+ }
+
+ private static GnssTimeSuggestion createFromParcel(Parcel in) {
+ TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
+ GnssTimeSuggestion suggestion = new GnssTimeSuggestion(utcTime);
+ @SuppressWarnings("unchecked")
+ ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+ suggestion.mDebugInfo = debugInfo;
+ return suggestion;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mUtcTime, 0);
+ dest.writeList(mDebugInfo);
+ }
+
+ @NonNull
+ public TimestampedValue<Long> getUtcTime() {
+ return mUtcTime;
+ }
+
+ @NonNull
+ public List<String> getDebugInfo() {
+ return mDebugInfo == null
+ ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
+ }
+
+ /**
+ * Associates information with the instance that can be useful for debugging / logging. The
+ * information is present in {@link #toString()} but is not considered for
+ * {@link #equals(Object)} and {@link #hashCode()}.
+ */
+ public void addDebugInfo(String... debugInfos) {
+ if (mDebugInfo == null) {
+ mDebugInfo = new ArrayList<>();
+ }
+ mDebugInfo.addAll(Arrays.asList(debugInfos));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ GnssTimeSuggestion that = (GnssTimeSuggestion) o;
+ return Objects.equals(mUtcTime, that.mUtcTime);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUtcTime);
+ }
+
+ @Override
+ public String toString() {
+ return "GnssTimeSuggestion{"
+ + "mUtcTime=" + mUtcTime
+ + ", mDebugInfo=" + mDebugInfo
+ + '}';
+ }
+}
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index 7bea5d7..87e7233 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -16,6 +16,7 @@
package android.app.timedetector;
+import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -34,6 +35,7 @@
* {@hide}
*/
interface ITimeDetectorService {
+ void suggestGnssTime(in GnssTimeSuggestion timeSuggestion);
boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion);
void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion);
void suggestTelephonyTime(in TelephonyTimeSuggestion timeSuggestion);
diff --git a/core/java/android/app/timedetector/OWNERS b/core/java/android/app/timedetector/OWNERS
index 8c11324..941eed8 100644
--- a/core/java/android/app/timedetector/OWNERS
+++ b/core/java/android/app/timedetector/OWNERS
@@ -1,4 +1,4 @@
# Bug component: 847766
-
+mingaleev@google.com
narayan@google.com
nfuller@google.com
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 162e182..52016b6 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -71,4 +71,12 @@
*/
@RequiresPermission(android.Manifest.permission.SET_TIME)
void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion);
+
+ /**
+ * Suggests the time according to a gnss time source.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.SET_TIME)
+ void suggestGnssTime(GnssTimeSuggestion timeSuggestion);
}
diff --git a/core/java/android/app/timedetector/TimeDetectorImpl.java b/core/java/android/app/timedetector/TimeDetectorImpl.java
index ac02c89..b0aa3c8 100644
--- a/core/java/android/app/timedetector/TimeDetectorImpl.java
+++ b/core/java/android/app/timedetector/TimeDetectorImpl.java
@@ -74,4 +74,16 @@
throw e.rethrowFromSystemServer();
}
}
+
+ @Override
+ public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) {
+ if (DEBUG) {
+ Log.d(TAG, "suggestGnssTime called: " + timeSuggestion);
+ }
+ try {
+ mITimeDetectorService.suggestGnssTime(timeSuggestion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/timezone/OWNERS b/core/java/android/app/timezone/OWNERS
index 8c11324..8f80897 100644
--- a/core/java/android/app/timezone/OWNERS
+++ b/core/java/android/app/timezone/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 847766
-
-narayan@google.com
-nfuller@google.com
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/java/android/app/timezonedetector/OWNERS b/core/java/android/app/timezonedetector/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/java/android/app/timezonedetector/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2dfbb3a..e4b2d70 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2933,6 +2933,16 @@
}
});
}
+ synchronized (mBluetoothConnectionCallbackExecutorMap) {
+ if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
+ try {
+ mService.registerBluetoothConnectionCallback(mConnectionCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth"
+ + "connection callback", e);
+ }
+ }
+ }
}
public void onBluetoothServiceDown() {
@@ -3582,25 +3592,25 @@
return false;
}
- // If the callback map is empty, we register the service-to-app callback
- if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
- try {
- mServiceLock.readLock().lock();
- if (mService != null) {
- if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) {
- return false;
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- mBluetoothConnectionCallbackExecutorMap.remove(callback);
- } finally {
- mServiceLock.readLock().unlock();
- }
- }
-
- // Adds the passed in callback to our map of callbacks to executors
synchronized (mBluetoothConnectionCallbackExecutorMap) {
+ // If the callback map is empty, we register the service-to-app callback
+ if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) {
+ return false;
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ mBluetoothConnectionCallbackExecutorMap.remove(callback);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ // Adds the passed in callback to our map of callbacks to executors
if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) {
throw new IllegalArgumentException("This callback has already been registered");
}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index e274af1..56c4824 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -153,7 +153,12 @@
@SystemApi
public static final ParcelUuid HEARING_AID =
ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb");
-
+ /** Placeholder until specification is released
+ * @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid LE_AUDIO =
+ ParcelUuid.fromString("EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE");
/** @hide */
@NonNull
@SystemApi
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 81d9b11..e32068f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -2101,7 +2101,7 @@
}
/** @hide */
- @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ @SystemApi
public boolean isOem() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
}
@@ -2149,13 +2149,13 @@
}
/** @hide */
- @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ @SystemApi
public boolean isVendor() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
}
/** @hide */
- @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ @SystemApi
public boolean isProduct() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
}
diff --git a/core/java/android/content/pm/LimitedLengthInputStream.java b/core/java/android/content/pm/LimitedLengthInputStream.java
index 19b681e..05089f6 100644
--- a/core/java/android/content/pm/LimitedLengthInputStream.java
+++ b/core/java/android/content/pm/LimitedLengthInputStream.java
@@ -1,6 +1,6 @@
package android.content.pm;
-import libcore.util.ArrayUtils;
+import com.android.internal.util.ArrayUtils;
import java.io.FilterInputStream;
import java.io.IOException;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ae1067d..00f5fb95 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2367,6 +2367,23 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the feature version
+ * specifies a date such that the device is known to pass the OpenGLES dEQP test suite
+ * associated with that date. The date is encoded as follows:
+ * <ul>
+ * <li>Year in bits 31-16</li>
+ * <li>Month in bits 15-8</li>
+ * <li>Day in bits 7-0</li>
+ * </ul>
+ * <p>
+ * Example: 2021-03-01 is encoded as 0x07E50301, and would indicate that the device passes the
+ * OpenGL ES dEQP test suite version that was current on 2021-03-01.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_OPENGLES_DEQP_LEVEL = "android.software.opengles.deqp.level";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device includes broadcast radio tuner.
* @hide
*/
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 36986de..13433dc 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1574,7 +1574,6 @@
private static native long nativeAssetGetLength(long assetPtr);
private static native long nativeAssetGetRemainingLength(long assetPtr);
- private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
private static native @Nullable Map nativeGetOverlayableMap(long ptr,
@NonNull String packageName);
private static native @Nullable String nativeGetOverlayablesToString(long ptr,
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index c16006a..477ffef 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -548,7 +548,24 @@
remainder = languageTag.substring(separator);
}
- return Locale.adjustLanguageCode(language) + remainder;
+ // No need to convert to lower cases because the language in the return value of
+ // Locale.toLanguageTag has been lower-cased.
+ final String adjustedLanguage;
+ switch(language) {
+ case "id":
+ adjustedLanguage = "in";
+ break;
+ case "yi":
+ adjustedLanguage = "ji";
+ break;
+ case "he":
+ adjustedLanguage = "iw";
+ break;
+ default:
+ adjustedLanguage = language;
+ break;
+ }
+ return adjustedLanguage + remainder;
}
/**
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 920d34f..a00ff8e 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1693,35 +1693,24 @@
private static native long nativeAllocateCopy(long ptr)
throws NullPointerException;
- @FastNative
private static synchronized native void nativeWriteToParcel(Parcel dest, long ptr);
- @FastNative
private static synchronized native void nativeReadFromParcel(Parcel source, long ptr);
- @FastNative
private static synchronized native void nativeSwap(long ptr, long otherPtr)
throws NullPointerException;
- @FastNative
private static synchronized native void nativeClose(long ptr);
- @FastNative
private static synchronized native boolean nativeIsEmpty(long ptr);
- @FastNative
private static synchronized native int nativeGetEntryCount(long ptr);
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @FastNative
private static synchronized native byte[] nativeReadValues(int tag, long ptr);
- @FastNative
private static synchronized native void nativeWriteValues(int tag, byte[] src, long ptr);
private static synchronized native void nativeDump(long ptr) throws IOException; // dump to LOGD
- @FastNative
private static synchronized native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass);
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @FastNative
private static synchronized native int nativeGetTagFromKeyLocal(long ptr, String keyName)
throws IllegalArgumentException;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @FastNative
private static synchronized native int nativeGetTypeFromTagLocal(long ptr, int tag)
throws IllegalArgumentException;
@FastNative
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index cf5d4e5..540ea5c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2108,17 +2108,6 @@
// ignored
}
- /** {@hide} */
- @Deprecated
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
- try {
- return mService.getActiveNetworkQuotaInfo();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
/**
* @hide
* @deprecated Talk to TelephonyManager directly
@@ -3163,9 +3152,9 @@
}
/**
- * Set sign in error notification to visible or in visible
+ * Set sign in error notification to visible or invisible
*
- * {@hide}
+ * @hide
* @deprecated Doesn't properly deal with multiple connected networks of the same type.
*/
@Deprecated
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 4173200..95a2f2e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -25,13 +25,13 @@
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.ISocketKeepaliveCallback;
import android.net.ProxyInfo;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.INetworkActivityListener;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
@@ -76,7 +76,6 @@
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
NetworkState[] getAllNetworkState();
- NetworkQuotaInfo getActiveNetworkQuotaInfo();
boolean isActiveNetworkMetered();
boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress,
@@ -235,4 +234,10 @@
in PersistableBundle extras);
void systemReady();
+
+ void registerNetworkActivityListener(in INetworkActivityListener l);
+
+ void unregisterNetworkActivityListener(in INetworkActivityListener l);
+
+ boolean isDefaultNetworkActive();
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 0941e7d..486e2d7 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -20,12 +20,13 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.LinkPropertiesUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.net.module.util.LinkPropertiesUtils;
+
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 178183d..c7116b4 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -20,13 +20,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.MacAddressUtils;
import android.net.wifi.WifiInfo;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
+import com.android.net.module.util.MacAddressUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 6166a75..94f849f 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -21,11 +21,12 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.NetUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.net.module.util.NetUtils;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.Inet4Address;
diff --git a/core/java/android/net/TcpKeepalivePacketData.java b/core/java/android/net/TcpKeepalivePacketData.java
new file mode 100644
index 0000000..ddb3a6a7
--- /dev/null
+++ b/core/java/android/net/TcpKeepalivePacketData.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.util.Objects;
+
+/**
+ * Represents the actual tcp keep alive packets which will be used for hardware offload.
+ * @hide
+ */
+@SystemApi
+public final class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
+ private static final String TAG = "TcpKeepalivePacketData";
+
+ /** TCP sequence number. */
+ public final int tcpSeq;
+
+ /** TCP ACK number. */
+ public final int tcpAck;
+
+ /** TCP RCV window. */
+ public final int tcpWindow;
+
+ /** TCP RCV window scale. */
+ public final int tcpWindowScale;
+
+ /** IP TOS. */
+ public final int ipTos;
+
+ /** IP TTL. */
+ public final int ipTtl;
+
+ public TcpKeepalivePacketData(@NonNull final InetAddress srcAddress, int srcPort,
+ @NonNull final InetAddress dstAddress, int dstPort, @NonNull final byte[] data,
+ int tcpSeq, int tcpAck, int tcpWindow, int tcpWindowScale, int ipTos, int ipTtl)
+ throws InvalidPacketException {
+ super(srcAddress, srcPort, dstAddress, dstPort, data);
+ this.tcpSeq = tcpSeq;
+ this.tcpAck = tcpAck;
+ this.tcpWindow = tcpWindow;
+ this.tcpWindowScale = tcpWindowScale;
+ this.ipTos = ipTos;
+ this.ipTtl = ipTtl;
+ }
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (!(o instanceof TcpKeepalivePacketData)) return false;
+ final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
+ final InetAddress srcAddress = getSrcAddress();
+ final InetAddress dstAddress = getDstAddress();
+ return srcAddress.equals(other.getSrcAddress())
+ && dstAddress.equals(other.getDstAddress())
+ && getSrcPort() == other.getSrcPort()
+ && getDstPort() == other.getDstPort()
+ && this.tcpAck == other.tcpAck
+ && this.tcpSeq == other.tcpSeq
+ && this.tcpWindow == other.tcpWindow
+ && this.tcpWindowScale == other.tcpWindowScale
+ && this.ipTos == other.ipTos
+ && this.ipTtl == other.ipTtl;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort(),
+ tcpAck, tcpSeq, tcpWindow, tcpWindowScale, ipTos, ipTtl);
+ }
+
+ /**
+ * Parcelable Implementation.
+ * Note that this object implements parcelable (and needs to keep doing this as it inherits
+ * from a class that does), but should usually be parceled as a stable parcelable using
+ * the toStableParcelable() and fromStableParcelable() methods.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Write to parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeString(getSrcAddress().getHostAddress());
+ out.writeString(getDstAddress().getHostAddress());
+ out.writeInt(getSrcPort());
+ out.writeInt(getDstPort());
+ out.writeByteArray(getPacket());
+ out.writeInt(tcpSeq);
+ out.writeInt(tcpAck);
+ out.writeInt(tcpWindow);
+ out.writeInt(tcpWindowScale);
+ out.writeInt(ipTos);
+ out.writeInt(ipTtl);
+ }
+
+ private static TcpKeepalivePacketData readFromParcel(Parcel in) throws InvalidPacketException {
+ InetAddress srcAddress = InetAddresses.parseNumericAddress(in.readString());
+ InetAddress dstAddress = InetAddresses.parseNumericAddress(in.readString());
+ int srcPort = in.readInt();
+ int dstPort = in.readInt();
+ byte[] packet = in.createByteArray();
+ int tcpSeq = in.readInt();
+ int tcpAck = in.readInt();
+ int tcpWnd = in.readInt();
+ int tcpWndScale = in.readInt();
+ int ipTos = in.readInt();
+ int ipTtl = in.readInt();
+ return new TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, packet, tcpSeq,
+ tcpAck, tcpWnd, tcpWndScale, ipTos, ipTtl);
+ }
+
+ /** Parcelable Creator. */
+ public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
+ new Parcelable.Creator<TcpKeepalivePacketData>() {
+ public TcpKeepalivePacketData createFromParcel(Parcel in) {
+ try {
+ return readFromParcel(in);
+ } catch (InvalidPacketException e) {
+ throw new IllegalArgumentException(
+ "Invalid TCP keepalive data: " + e.getError());
+ }
+ }
+
+ public TcpKeepalivePacketData[] newArray(int size) {
+ return new TcpKeepalivePacketData[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "saddr: " + getSrcAddress()
+ + " daddr: " + getDstAddress()
+ + " sport: " + getSrcPort()
+ + " dport: " + getDstPort()
+ + " seq: " + tcpSeq
+ + " ack: " + tcpAck
+ + " window: " + tcpWindow
+ + " windowScale: " + tcpWindowScale
+ + " tos: " + ipTos
+ + " ttl: " + ipTtl;
+ }
+}
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index 148acf1..d4a3fa7 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -15,30 +15,104 @@
*/
package android.net.vcn;
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
/**
* This class represents a configuration for a Virtual Carrier Network.
*
+ * <p>Each {@link VcnGatewayConnectionConfig} instance added represents a connection that will be
+ * brought up on demand based on active {@link NetworkRequest}(s).
+ *
+ * @see VcnManager for more information on the Virtual Carrier Network feature
* @hide
*/
public final class VcnConfig implements Parcelable {
@NonNull private static final String TAG = VcnConfig.class.getSimpleName();
- private VcnConfig() {
+ private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
+ @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;
+
+ private VcnConfig(@NonNull Set<VcnGatewayConnectionConfig> tunnelConfigs) {
+ mGatewayConnectionConfigs = Collections.unmodifiableSet(tunnelConfigs);
+
validate();
}
- // TODO: Implement getters, validators, etc
/**
- * Validates this configuration.
+ * Deserializes a VcnConfig from a PersistableBundle.
*
* @hide
*/
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public VcnConfig(@NonNull PersistableBundle in) {
+ final PersistableBundle gatewayConnectionConfigsBundle =
+ in.getPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY);
+ mGatewayConnectionConfigs =
+ new ArraySet<>(
+ PersistableBundleUtils.toList(
+ gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new));
+
+ validate();
+ }
+
private void validate() {
- // TODO: implement validation logic
+ Preconditions.checkCollectionNotEmpty(
+ mGatewayConnectionConfigs, "gatewayConnectionConfigs");
+ }
+
+ /** Retrieves the set of configured tunnels. */
+ @NonNull
+ public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() {
+ return Collections.unmodifiableSet(mGatewayConnectionConfigs);
+ }
+
+ /**
+ * Serializes this object to a PersistableBundle.
+ *
+ * @hide
+ */
+ @NonNull
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ final PersistableBundle gatewayConnectionConfigsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mGatewayConnectionConfigs),
+ VcnGatewayConnectionConfig::toPersistableBundle);
+ result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle);
+
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGatewayConnectionConfigs);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof VcnConfig)) {
+ return false;
+ }
+
+ final VcnConfig rhs = (VcnConfig) other;
+ return mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
}
// Parcelable methods
@@ -49,15 +123,16 @@
}
@Override
- public void writeToParcel(Parcel out, int flags) {}
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(toPersistableBundle(), flags);
+ }
@NonNull
public static final Parcelable.Creator<VcnConfig> CREATOR =
new Parcelable.Creator<VcnConfig>() {
@NonNull
public VcnConfig createFromParcel(Parcel in) {
- // TODO: Ensure all methods are pulled from the parcels
- return new VcnConfig();
+ return new VcnConfig((PersistableBundle) in.readParcelable(null));
}
@NonNull
@@ -68,7 +143,23 @@
/** This class is used to incrementally build {@link VcnConfig} objects. */
public static class Builder {
- // TODO: Implement this builder
+ @NonNull
+ private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
+
+ /**
+ * Adds a configuration for an individual gateway connection.
+ *
+ * @param gatewayConnectionConfig the configuration for an individual gateway connection
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ public Builder addGatewayConnectionConfig(
+ @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
+ Objects.requireNonNull(gatewayConnectionConfig, "gatewayConnectionConfig was null");
+
+ mGatewayConnectionConfigs.add(gatewayConnectionConfig);
+ return this;
+ }
/**
* Builds and validates the VcnConfig.
@@ -77,7 +168,7 @@
*/
@NonNull
public VcnConfig build() {
- return new VcnConfig();
+ return new VcnConfig(mGatewayConnectionConfigs);
}
}
}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 8160edc..039360a 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -15,7 +15,27 @@
*/
package android.net.vcn;
+import static android.net.NetworkCapabilities.NetCapability;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
/**
* This class represents a configuration for a connection to a Virtual Carrier Network gateway.
@@ -49,38 +69,399 @@
* <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_MCX}
* </ul>
*
+ * <p>The meteredness and roaming of the VCN {@link Network} will be determined by that of the
+ * underlying Network(s).
+ *
* @hide
*/
public final class VcnGatewayConnectionConfig {
- private VcnGatewayConnectionConfig() {
+ // TODO: Use MIN_MTU_V6 once it is public, @hide
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int MIN_MTU_V6 = 1280;
+
+ private static final Set<Integer> ALLOWED_CAPABILITIES;
+
+ static {
+ Set<Integer> allowedCaps = new ArraySet<>();
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_MMS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_SUPL);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_DUN);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_FOTA);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_IMS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_CBS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_IA);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_RCS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_XCAP);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_EIMS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_MCX);
+
+ ALLOWED_CAPABILITIES = Collections.unmodifiableSet(allowedCaps);
+ }
+
+ private static final int DEFAULT_MAX_MTU = 1500;
+
+ /**
+ * The maximum number of retry intervals that may be specified.
+ *
+ * <p>Limited to ensure an upper bound on config sizes.
+ */
+ private static final int MAX_RETRY_INTERVAL_COUNT = 10;
+
+ /**
+ * The minimum allowable repeating retry interval
+ *
+ * <p>To ensure the device is not constantly being woken up, this retry interval MUST be greater
+ * than this value.
+ *
+ * @see {@link Builder#setRetryInterval()}
+ */
+ private static final long MINIMUM_REPEATING_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15);
+
+ private static final long[] DEFAULT_RETRY_INTERVALS_MS =
+ new long[] {
+ TimeUnit.SECONDS.toMillis(1),
+ TimeUnit.SECONDS.toMillis(2),
+ TimeUnit.SECONDS.toMillis(5),
+ TimeUnit.SECONDS.toMillis(30),
+ TimeUnit.MINUTES.toMillis(1),
+ TimeUnit.MINUTES.toMillis(5),
+ TimeUnit.MINUTES.toMillis(15)
+ };
+
+ private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
+ @NonNull private final Set<Integer> mExposedCapabilities;
+
+ private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities";
+ @NonNull private final Set<Integer> mUnderlyingCapabilities;
+
+ // TODO: Add Ike/ChildSessionParams as a subclass - maybe VcnIkeGatewayConnectionConfig
+
+ private static final String MAX_MTU_KEY = "mMaxMtu";
+ private final int mMaxMtu;
+
+ private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs";
+ @NonNull private final long[] mRetryIntervalsMs;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public VcnGatewayConnectionConfig(
+ @NonNull Set<Integer> exposedCapabilities,
+ @NonNull Set<Integer> underlyingCapabilities,
+ @NonNull long[] retryIntervalsMs,
+ @IntRange(from = MIN_MTU_V6) int maxMtu) {
+ mExposedCapabilities = exposedCapabilities;
+ mUnderlyingCapabilities = underlyingCapabilities;
+ mRetryIntervalsMs = retryIntervalsMs;
+ mMaxMtu = maxMtu;
+
validate();
}
- // TODO: Implement getters, validators, etc
+ /** @hide */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public VcnGatewayConnectionConfig(@NonNull PersistableBundle in) {
+ final PersistableBundle exposedCapsBundle =
+ in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY);
+ final PersistableBundle underlyingCapsBundle =
+ in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY);
+
+ mExposedCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
+ mUnderlyingCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ underlyingCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
+ mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
+ mMaxMtu = in.getInt(MAX_MTU_KEY);
+
+ validate();
+ }
+
+ private void validate() {
+ Preconditions.checkArgument(
+ mExposedCapabilities != null && !mExposedCapabilities.isEmpty(),
+ "exposedCapsBundle was null or empty");
+ for (Integer cap : getAllExposedCapabilities()) {
+ checkValidCapability(cap);
+ }
+
+ Preconditions.checkArgument(
+ mUnderlyingCapabilities != null && !mUnderlyingCapabilities.isEmpty(),
+ "underlyingCapabilities was null or empty");
+ for (Integer cap : getAllUnderlyingCapabilities()) {
+ checkValidCapability(cap);
+ }
+
+ Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null");
+ validateRetryInterval(mRetryIntervalsMs);
+
+ Preconditions.checkArgument(
+ mMaxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)");
+ }
+
+ private static void checkValidCapability(int capability) {
+ Preconditions.checkArgument(
+ ALLOWED_CAPABILITIES.contains(capability),
+ "NetworkCapability " + capability + "out of range");
+ }
+
+ private static void validateRetryInterval(@Nullable long[] retryIntervalsMs) {
+ Preconditions.checkArgument(
+ retryIntervalsMs != null
+ && retryIntervalsMs.length > 0
+ && retryIntervalsMs.length <= MAX_RETRY_INTERVAL_COUNT,
+ "retryIntervalsMs was null, empty or exceed max interval count");
+
+ final long repeatingInterval = retryIntervalsMs[retryIntervalsMs.length - 1];
+ if (repeatingInterval < MINIMUM_REPEATING_RETRY_INTERVAL_MS) {
+ throw new IllegalArgumentException(
+ "Repeating retry interval was too short, must be a minimum of 15 minutes: "
+ + repeatingInterval);
+ }
+ }
/**
- * Validates this configuration
+ * Returns all exposed capabilities.
*
* @hide
*/
- private void validate() {
- // TODO: implement validation logic
+ @NonNull
+ public Set<Integer> getAllExposedCapabilities() {
+ return Collections.unmodifiableSet(mExposedCapabilities);
}
- // Parcelable methods
+ /**
+ * Checks if this config is configured to support/expose a specific capability.
+ *
+ * @param capability the capability to check for
+ */
+ public boolean hasExposedCapability(@NetCapability int capability) {
+ checkValidCapability(capability);
- /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects */
+ return mExposedCapabilities.contains(capability);
+ }
+
+ /**
+ * Returns all capabilities required of underlying networks.
+ *
+ * @hide
+ */
+ @NonNull
+ public Set<Integer> getAllUnderlyingCapabilities() {
+ return Collections.unmodifiableSet(mUnderlyingCapabilities);
+ }
+
+ /**
+ * Checks if this config requires an underlying network to have the specified capability.
+ *
+ * @param capability the capability to check for
+ */
+ public boolean requiresUnderlyingCapability(@NetCapability int capability) {
+ checkValidCapability(capability);
+
+ return mUnderlyingCapabilities.contains(capability);
+ }
+
+ /** Retrieves the configured retry intervals. */
+ @NonNull
+ public long[] getRetryIntervalsMs() {
+ return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
+ }
+
+ /** Retrieves the maximum MTU allowed for this Gateway Connection. */
+ @IntRange(from = MIN_MTU_V6)
+ public int getMaxMtu() {
+ return mMaxMtu;
+ }
+
+ /**
+ * Converts this config to a PersistableBundle.
+ *
+ * @hide
+ */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PROTECTED)
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ final PersistableBundle exposedCapsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mExposedCapabilities),
+ PersistableBundleUtils.INTEGER_SERIALIZER);
+ final PersistableBundle underlyingCapsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mUnderlyingCapabilities),
+ PersistableBundleUtils.INTEGER_SERIALIZER);
+
+ result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
+ result.putPersistableBundle(UNDERLYING_CAPABILITIES_KEY, underlyingCapsBundle);
+ result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
+ result.putInt(MAX_MTU_KEY, mMaxMtu);
+
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mExposedCapabilities,
+ mUnderlyingCapabilities,
+ Arrays.hashCode(mRetryIntervalsMs),
+ mMaxMtu);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof VcnGatewayConnectionConfig)) {
+ return false;
+ }
+
+ final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other;
+ return mExposedCapabilities.equals(rhs.mExposedCapabilities)
+ && mUnderlyingCapabilities.equals(rhs.mUnderlyingCapabilities)
+ && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
+ && mMaxMtu == rhs.mMaxMtu;
+ }
+
+ /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. */
public static class Builder {
- // TODO: Implement this builder
+ @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
+ @NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
+ @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
+ private int mMaxMtu = DEFAULT_MAX_MTU;
+
+ // TODO: (b/175829816) Consider VCN-exposed capabilities that may be transport dependent.
+ // Consider the case where the VCN might only expose MMS on WiFi, but defer to MMS
+ // when on Cell.
/**
- * Builds and validates the VcnGatewayConnectionConfig
+ * Add a capability that this VCN Gateway Connection will support.
+ *
+ * @param exposedCapability the app-facing capability to be exposed by this VCN Gateway
+ * Connection (i.e., the capabilities that this VCN Gateway Connection will support).
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
+ * Connection
+ */
+ public Builder addExposedCapability(@NetCapability int exposedCapability) {
+ checkValidCapability(exposedCapability);
+
+ mExposedCapabilities.add(exposedCapability);
+ return this;
+ }
+
+ /**
+ * Remove a capability that this VCN Gateway Connection will support.
+ *
+ * @param exposedCapability the app-facing capability to not be exposed by this VCN Gateway
+ * Connection (i.e., the capabilities that this VCN Gateway Connection will support)
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
+ * Connection
+ */
+ public Builder removeExposedCapability(@NetCapability int exposedCapability) {
+ checkValidCapability(exposedCapability);
+
+ mExposedCapabilities.remove(exposedCapability);
+ return this;
+ }
+
+ /**
+ * Require a capability for Networks underlying this VCN Gateway Connection.
+ *
+ * @param underlyingCapability the capability that a network MUST have in order to be an
+ * underlying network for this VCN Gateway Connection.
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
+ * networks
+ */
+ public Builder addRequiredUnderlyingCapability(@NetCapability int underlyingCapability) {
+ checkValidCapability(underlyingCapability);
+
+ mUnderlyingCapabilities.add(underlyingCapability);
+ return this;
+ }
+
+ /**
+ * Remove a requirement of a capability for Networks underlying this VCN Gateway Connection.
+ *
+ * <p>Calling this method will allow Networks that do NOT have this capability to be
+ * selected as an underlying network for this VCN Gateway Connection. However, underlying
+ * networks MAY still have the removed capability.
+ *
+ * @param underlyingCapability the capability that a network DOES NOT need to have in order
+ * to be an underlying network for this VCN Gateway Connection.
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
+ * networks
+ */
+ public Builder removeRequiredUnderlyingCapability(@NetCapability int underlyingCapability) {
+ checkValidCapability(underlyingCapability);
+
+ mUnderlyingCapabilities.remove(underlyingCapability);
+ return this;
+ }
+
+ /**
+ * Set the retry interval between VCN establishment attempts upon successive failures.
+ *
+ * <p>The last retry interval will be repeated until safe mode is entered, or a connection
+ * is successfully established, at which point the retry timers will be reset. For power
+ * reasons, the last (repeated) retry interval MUST be at least 15 minutes.
+ *
+ * <p>Retry intervals MAY be subject to system power saving modes. That is to say that if
+ * the system enters a power saving mode, the retry may not occur until the device leaves
+ * the specified power saving mode. Intervals are sequential, and intervals will NOT be
+ * skipped if system power saving results in delaying retries (even if it exceed multiple
+ * retry intervals).
+ *
+ * <p>Each Gateway Connection will retry according to the retry intervals configured, but if
+ * safe mode is enabled, all Gateway Connection(s) will be disabled.
+ *
+ * @param retryIntervalsMs an array of between 1 and 10 millisecond intervals after which
+ * the VCN will attempt to retry a session initiation. The last (repeating) retry
+ * interval must be at least 15 minutes. Defaults to: {@code [1s, 2s, 5s, 30s, 1m, 5m,
+ * 15m]}
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnManager for additional discussion on fail-safe mode
+ */
+ @NonNull
+ public Builder setRetryInterval(@NonNull long[] retryIntervalsMs) {
+ validateRetryInterval(retryIntervalsMs);
+
+ mRetryIntervalsMs = retryIntervalsMs;
+ return this;
+ }
+
+ /**
+ * Sets the maximum MTU allowed for this VCN Gateway Connection.
+ *
+ * <p>This MTU is applied to the VCN Gateway Connection exposed Networks, and represents the
+ * MTU of the virtualized network.
+ *
+ * <p>The system may reduce the MTU below the maximum specified based on signals such as the
+ * MTU of the underlying networks (and adjusted for Gateway Connection overhead).
+ *
+ * @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than
+ * the IPv6 minimum MTU of 1280. Defaults to 1500.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) {
+ Preconditions.checkArgument(
+ maxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)");
+
+ mMaxMtu = maxMtu;
+ return this;
+ }
+
+ /**
+ * Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
*/
@NonNull
public VcnGatewayConnectionConfig build() {
- return new VcnGatewayConnectionConfig();
+ return new VcnGatewayConnectionConfig(
+ mExposedCapabilities, mUnderlyingCapabilities, mRetryIntervalsMs, mMaxMtu);
}
}
}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 6769b9e..19c183f 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -23,10 +23,37 @@
import android.content.Context;
import android.os.ParcelUuid;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+import java.io.IOException;
/**
* VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks.
*
+ * <p>A VCN creates a virtualization layer to allow MVNOs to aggregate heterogeneous physical
+ * networks, unifying them as a single carrier network. This enables infrastructure flexibility on
+ * the part of MVNOs without impacting user connectivity, abstracting the physical network
+ * technologies as an implementation detail of their public network.
+ *
+ * <p>Each VCN virtualizes an Carrier's network by building tunnels to a carrier's core network over
+ * carrier-managed physical links and supports a IP mobility layer to ensure seamless transitions
+ * between the underlying networks. Each VCN is configured based on a Subscription Group (see {@link
+ * android.telephony.SubscriptionManager}) and aggregates all networks that are brought up based on
+ * a profile or suggestion in the specified Subscription Group.
+ *
+ * <p>The VCN can be configured to expose one or more {@link android.net.Network}(s), each with
+ * different capabilities, allowing for APN virtualization.
+ *
+ * <p>If a tunnel fails to connect, or otherwise encounters a fatal error, the VCN will attempt to
+ * reestablish the connection. If the tunnel still has not reconnected after a system-determined
+ * timeout, the VCN Safe Mode (see below) will be entered.
+ *
+ * <p>The VCN Safe Mode ensures that users (and carriers) have a fallback to restore system
+ * connectivity to update profiles, diagnose issues, contact support, or perform other remediation
+ * tasks. In Safe Mode, the system will allow underlying cellular networks to be used as default.
+ * Additionally, during Safe Mode, the VCN will continue to retry the connections, and will
+ * automatically exit Safe Mode if all active tunnels connect successfully.
+ *
* @hide
*/
@SystemService(Context.VCN_MANAGEMENT_SERVICE)
@@ -63,15 +90,20 @@
* @param config the configuration parameters for the VCN
* @throws SecurityException if the caller does not have carrier privileges, or is not running
* as the primary user
+ * @throws IOException if the configuration failed to be persisted. A caller encountering this
+ * exception should attempt to retry (possibly after a delay).
* @hide
*/
@RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant
- public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
+ public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config)
+ throws IOException {
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
requireNonNull(config, "config was null");
try {
mService.setVcnConfig(subscriptionGroup, config);
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -88,14 +120,18 @@
* @param subscriptionGroup the subscription group that the configuration should be applied to
* @throws SecurityException if the caller does not have carrier privileges, or is not running
* as the primary user
+ * @throws IOException if the configuration failed to be cleared. A caller encountering this
+ * exception should attempt to retry (possibly after a delay).
* @hide
*/
@RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant
- public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
+ public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) throws IOException {
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
try {
mService.clearVcnConfig(subscriptionGroup);
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index fe4d729..46ad7b8 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -26,7 +26,6 @@
import android.annotation.SystemService;
import android.app.ActivityManager;
import android.content.Context;
-import android.content.Intent;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
@@ -52,8 +51,6 @@
public final class BugreportManager {
private static final String TAG = "BugreportManager";
- private static final String INTENT_UI_INTENSIVE_BUGREPORT_DUMPS_FINISHED =
- "com.android.internal.intent.action.UI_INTENSIVE_BUGREPORT_DUMPS_FINISHED";
private final Context mContext;
private final IDumpstate mBinder;
@@ -126,6 +123,12 @@
* Called when taking bugreport finishes successfully.
*/
public void onFinished() {}
+
+ /**
+ * Called when it is ready for calling app to show UI, showing any extra UI before this
+ * callback can interfere with bugreport generation.
+ */
+ public void onEarlyReportFinished() {}
}
/**
@@ -288,21 +291,12 @@
}
@Override
- public void onUiIntensiveBugreportDumpsFinished(String callingPackage)
+ public void onUiIntensiveBugreportDumpsFinished()
throws RemoteException {
final long identity = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> {
- // Send intent to let calling app to show UI safely without interfering with
- // the bugreport/screenshot generation.
- // TODO(b/154298410): When S is ready for API change, add a method in
- // BugreportCallback so we can just call the callback instead of using
- // broadcast.
- Intent intent = new Intent(INTENT_UI_INTENSIVE_BUGREPORT_DUMPS_FINISHED);
- intent.setPackage(callingPackage);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mContext.sendBroadcast(intent, android.Manifest.permission.DUMP);
+ mCallback.onEarlyReportFinished();
});
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 78ba7f0..0d8769e 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -106,12 +106,24 @@
public static final String HARDWARE = getString("ro.hardware");
/**
- * The hardware variant (SKU), if available.
+ * The SKU of the hardware (from the kernel command line). The SKU is reported by the bootloader
+ * to configure system software features.
*/
@NonNull
public static final String SKU = getString("ro.boot.hardware.sku");
/**
+ * The SKU of the device as set by the original design manufacturer (ODM). This is a
+ * runtime-initialized property set during startup to configure device services.
+ *
+ * <p>The ODM SKU may have multiple variants for the same system SKU in case a manufacturer
+ * produces variants of the same design. For example, the same build may be released with
+ * variations in physical keyboard and/or display hardware, each with a different ODM SKU.
+ */
+ @NonNull
+ public static final String ODM_SKU = getString("ro.boot.product.hardware.sku");
+
+ /**
* Whether this build was for an emulator device.
* @hide
*/
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
index ab5637c..7b84575 100644
--- a/core/java/android/os/FileBridge.java
+++ b/core/java/android/os/FileBridge.java
@@ -22,11 +22,12 @@
import android.system.Os;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+
import libcore.io.IoBridge;
import libcore.io.IoUtils;
import libcore.io.Memory;
import libcore.io.Streams;
-import libcore.util.ArrayUtils;
import java.io.FileDescriptor;
import java.io.IOException;
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index df1f1b2..e77b163 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -249,6 +249,7 @@
}
if (appInfo == null) {
Log.w(TAG, "Debug layer app '" + packageName + "' not installed");
+ return "";
}
final String abi = chooseAbi(appInfo);
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index e1d9005..25d84ba 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -321,16 +321,6 @@
void setFirewallChainEnabled(int chain, boolean enable);
/**
- * Set all packets from users in ranges to go through VPN specified by netId.
- */
- void addVpnUidRanges(int netId, in UidRange[] ranges);
-
- /**
- * Clears the special VPN rules for users in ranges and VPN specified by netId.
- */
- void removeVpnUidRanges(int netId, in UidRange[] ranges);
-
- /**
* Start listening for mobile activity state changes.
*/
void registerNetworkActivityListener(INetworkActivityListener listener);
@@ -361,7 +351,5 @@
void removeInterfaceFromLocalNetwork(String iface);
int removeRoutesFromLocalNetwork(in List<RouteInfo> routes);
- void setAllowOnlyVpnForUids(boolean enable, in UidRange[] uidRanges);
-
boolean isNetworkRestricted(int uid);
}
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index 2561e1e..2052883 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -27,7 +27,9 @@
boolean setupBcb(in String command);
boolean clearBcb();
void rebootRecoveryWithCommand(in String command);
- boolean requestLskf(in String updateToken, in IntentSender sender);
- boolean clearLskf();
- boolean rebootWithLskf(in String updateToken, in String reason);
+ boolean requestLskf(in String packageName, in IntentSender sender);
+ boolean clearLskf(in String packageName);
+ boolean isLskfCaptured(in String packageName);
+ boolean rebootWithLskfAssumeSlotSwitch(in String packageName, in String reason);
+ boolean rebootWithLskf(in String packageName, in String reason, in boolean slotSwitch);
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 6aa1be9..563ff62 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -28,3 +28,6 @@
per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
per-file *Telephony* = file:/telephony/OWNERS
per-file *Zygote* = file:/ZYGOTE_OWNERS
+
+# RecoverySystem
+per-file *Recovery* = file:/services/core/java/com/android/server/recoverysystem/OWNERS
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index cf90174..6acdcc4 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -33,11 +33,12 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import com.android.internal.util.ArrayUtils;
+
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
import dalvik.system.VMRuntime;
-import libcore.util.ArrayUtils;
import libcore.util.SneakyThrow;
import java.io.ByteArrayInputStream;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 38e1704..6713de8 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -631,31 +631,35 @@
/**
* Prepare to apply an unattended update by asking the user for their Lock Screen Knowledge
* Factor (LSKF). If supplied, the {@code intentSender} will be called when the system is setup
- * and ready to apply the OTA.
- * <p>
- * When the system is already prepared for update and this API is called again with the same
- * {@code updateToken}, it will not call the intent sender nor request the user enter their Lock
- * Screen Knowledge Factor.
- * <p>
- * When this API is called again with a different {@code updateToken}, the prepared-for-update
- * status is reset and process repeats as though it's the initial call to this method as
- * described in the first paragraph.
+ * and ready to apply the OTA. This API is expected to handle requests from multiple clients
+ * simultaneously, e.g. from ota and mainline.
+ *
+ * <p> The behavior of multi-client Resume on Reboot works as follows
+ * <li> Each client should call this function to prepare for Resume on Reboot before calling
+ * {@link #rebootAndApply(Context, String, boolean)} </li>
+ * <li> One client cannot clear the Resume on Reboot preparation of another client. </li>
+ * <li> If multiple clients have prepared for Resume on Reboot, the subsequent reboot will be
+ * first come, first served. </li>
*
* @param context the Context to use.
- * @param updateToken token used to indicate which update was prepared
+ * @param updateToken this parameter is deprecated and won't be used. Callers can supply with
+ * an empty string. See details in
+ * <a href="http://go/multi-client-ror">http://go/multi-client-ror</a>
+ * TODO(xunchang) update the link of document with the public doc.
* @param intentSender the intent to call when the update is prepared; may be {@code null}
* @throws IOException if there were any errors setting up unattended update
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.RECOVERY)
+ @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
+ android.Manifest.permission.REBOOT})
public static void prepareForUnattendedUpdate(@NonNull Context context,
@NonNull String updateToken, @Nullable IntentSender intentSender) throws IOException {
if (updateToken == null) {
throw new NullPointerException("updateToken == null");
}
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
- if (!rs.requestLskf(updateToken, intentSender)) {
+ if (!rs.requestLskf(context.getPackageName(), intentSender)) {
throw new IOException("preparation for update failed");
}
}
@@ -664,32 +668,38 @@
* Request that any previously requested Lock Screen Knowledge Factor (LSKF) is cleared and
* the preparation for unattended update is reset.
*
+ * <p> Note that the API won't clear the underlying Resume on Reboot preparation state if
+ * another client has requested. So the reboot call from the other client can still succeed.
+ *
* @param context the Context to use.
* @throws IOException if there were any errors clearing the unattended update state
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.RECOVERY)
+ @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
+ android.Manifest.permission.REBOOT})
public static void clearPrepareForUnattendedUpdate(@NonNull Context context)
throws IOException {
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
- if (!rs.clearLskf()) {
+ if (!rs.clearLskf(context.getPackageName())) {
throw new IOException("could not reset unattended update state");
}
}
/**
- * Request that the device reboot and apply the update that has been prepared. The
- * {@code updateToken} must match what was given for {@link #prepareForUnattendedUpdate} or
- * this will return {@code false}.
+ * Request that the device reboot and apply the update that has been prepared. This API is
+ * deprecated, and is expected to be used by OTA only on devices running Android 11.
*
* @param context the Context to use.
- * @param updateToken the token used to call {@link #prepareForUnattendedUpdate} before
+ * @param updateToken this parameter is deprecated and won't be used. See details in
+ * <a href="http://go/multi-client-ror">http://go/multi-client-ror</a>
+ * TODO(xunchang) update the link of document with the public doc.
* @param reason the reboot reason to give to the {@link PowerManager}
* @throws IOException if the reboot couldn't proceed because the device wasn't ready for an
* unattended reboot or if the {@code updateToken} did not match the previously
* given token
* @hide
+ * @deprecated Use {@link #rebootAndApply(Context, String, boolean)} instead
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.RECOVERY)
@@ -699,7 +709,47 @@
throw new NullPointerException("updateToken == null");
}
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
- if (!rs.rebootWithLskf(updateToken, reason)) {
+ // OTA is the sole user, who expects a slot switch.
+ if (!rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason)) {
+ throw new IOException("system not prepared to apply update");
+ }
+ }
+
+ /**
+ * Query if Resume on Reboot has been prepared for a given caller.
+ *
+ * @param context the Context to use.
+ * @throws IOException if there were any errors connecting to the service or querying the state.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
+ android.Manifest.permission.REBOOT})
+ public static boolean isPreparedForUnattendedUpdate(@NonNull Context context)
+ throws IOException {
+ RecoverySystem rs = context.getSystemService(RecoverySystem.class);
+ return rs.isLskfCaptured(context.getPackageName());
+ }
+
+ /**
+ * Request that the device reboot and apply the update that has been prepared.
+ * {@link #prepareForUnattendedUpdate} must be called before for the given client,
+ * otherwise the function call will fail.
+ *
+ * @param context the Context to use.
+ * @param reason the reboot reason to give to the {@link PowerManager}
+ * @param slotSwitch true if the caller expects the slot to be switched on A/B devices.
+ * @throws IOException if the reboot couldn't proceed because the device wasn't ready for an
+ * unattended reboot.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
+ android.Manifest.permission.REBOOT})
+ public static void rebootAndApply(@NonNull Context context,
+ @NonNull String reason, boolean slotSwitch) throws IOException {
+ RecoverySystem rs = context.getSystemService(RecoverySystem.class);
+ if (!rs.rebootWithLskf(context.getPackageName(), reason, slotSwitch)) {
throw new IOException("system not prepared to apply update");
}
}
@@ -1283,16 +1333,15 @@
/**
* Begins the process of asking the user for the Lock Screen Knowledge Factor.
*
- * @param updateToken token that will be used in calls to {@link #rebootAndApply} to ensure
- * that the preparation was for the correct update
+ * @param packageName the package name of the caller who requests Resume on Reboot
* @return true if the request was correct
* @throws IOException if the recovery system service could not be contacted
*/
- private boolean requestLskf(String updateToken, IntentSender sender) throws IOException {
+ private boolean requestLskf(String packageName, IntentSender sender) throws IOException {
try {
- return mService.requestLskf(updateToken, sender);
+ return mService.requestLskf(packageName, sender);
} catch (RemoteException e) {
- throw new IOException("could request update");
+ throw new IOException("could request LSKF capture");
}
}
@@ -1302,22 +1351,52 @@
* @return true if the setup for OTA was cleared
* @throws IOException if the recovery system service could not be contacted
*/
- private boolean clearLskf() throws IOException {
+ private boolean clearLskf(String packageName) throws IOException {
try {
- return mService.clearLskf();
+ return mService.clearLskf(packageName);
} catch (RemoteException e) {
throw new IOException("could not clear LSKF");
}
}
/**
+ * Queries if the Resume on Reboot has been prepared for a given caller.
+ *
+ * @param packageName the identifier of the caller who requests Resume on Reboot
+ * @return true if Resume on Reboot is prepared.
+ * @throws IOException if the recovery system service could not be contacted
+ */
+ private boolean isLskfCaptured(String packageName) throws IOException {
+ try {
+ return mService.isLskfCaptured(packageName);
+ } catch (RemoteException e) {
+ throw new IOException("could not get LSKF capture state");
+ }
+ }
+
+ /**
* Calls the recovery system service to reboot and apply update.
*
- * @param updateToken the update token for which the update was prepared
*/
- private boolean rebootWithLskf(String updateToken, String reason) throws IOException {
+ private boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch)
+ throws IOException {
try {
- return mService.rebootWithLskf(updateToken, reason);
+ return mService.rebootWithLskf(packageName, reason, slotSwitch);
+ } catch (RemoteException e) {
+ throw new IOException("could not reboot for update");
+ }
+ }
+
+
+ /**
+ * Calls the recovery system service to reboot and apply update. This is the legacy API and
+ * expects a slot switch for A/B devices.
+ *
+ */
+ private boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason)
+ throws IOException {
+ try {
+ return mService.rebootWithLskfAssumeSlotSwitch(packageName, reason);
} catch (RemoteException e) {
throw new IOException("could not reboot for update");
}
diff --git a/core/java/android/os/incremental/OWNERS b/core/java/android/os/incremental/OWNERS
new file mode 100644
index 0000000..3795493
--- /dev/null
+++ b/core/java/android/os/incremental/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 554432
+alexbuy@google.com
+schfan@google.com
+toddke@google.com
+zyy@google.com
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index fa1b7d5..38a59f0 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4305,13 +4305,23 @@
* <P>
* Type: INTEGER (A bitmask of CARRIER_PRESENCE_* fields)
* </P>
+ *
+ * @deprecated The contacts database will only show presence
+ * information on devices where
+ * {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is true,
+ * otherwise use {@link android.telephony.ims.RcsUceAdapter}.
*/
+ @Deprecated
public static final String CARRIER_PRESENCE = "carrier_presence";
/**
* Indicates that the entry is Video Telephony (VT) capable on the
* current carrier. An allowed bitmask of {@link #CARRIER_PRESENCE}.
+ *
+ * @deprecated Same as {@link DataColumns#CARRIER_PRESENCE}.
+ *
*/
+ @Deprecated
public static final int CARRIER_PRESENCE_VT_CAPABLE = 0x01;
/**
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 4d67d46..e7e2c61 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -403,6 +403,14 @@
public static final String NAMESPACE_PERMISSIONS = "permissions";
/**
+ * Namespace for ota related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_OTA = "ota";
+
+ /**
* Namespace for all widget related features.
*
* @hide
diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS
index 792ff20..b02102b 100644
--- a/core/java/android/provider/OWNERS
+++ b/core/java/android/provider/OWNERS
@@ -1,9 +1,17 @@
-per-file DeviceConfig.java = svetoslavganov@google.com
-per-file DeviceConfig.java = hackbod@google.com
-per-file DeviceConfig.java = schfan@google.com
+per-file *BlockedNumber* = file:/telephony/OWNERS
+per-file *Telephony* = file:/telephony/OWNERS
-per-file CallLog.java = file:/telephony/OWNERS
-per-file DocumentsContract.java = file:/core/java/android/os/storage/OWNERS
-per-file DocumentsProvider.java = file:/core/java/android/os/storage/OWNERS
-per-file MediaStore.java = file:/core/java/android/os/storage/OWNERS
-per-file Telephony.java = file:/telephony/OWNERS
+per-file *CallLog* = file:platform/packages/providers/ContactsProvider:/OWNERS
+per-file *Contacts* = file:platform/packages/providers/ContactsProvider:/OWNERS
+per-file *Voicemail* = file:platform/packages/providers/ContactsProvider:/OWNERS
+
+per-file *Calendar* = file:platform/packages/providers/CalendarProvider:/OWNERS
+
+per-file *Downloads* = file:platform/packages/providers/DownloadProvider:/OWNERS
+
+per-file *DeviceConfig* = file:/packages/SettingsProvider/OWNERS
+
+per-file *Documents* = file:/core/java/android/os/storage/OWNERS
+per-file *Documents* = file:platform/packages/apps/DocumentsUI:/OWNERS
+
+per-file *Slices* = file:/core/java/android/app/slice/OWNERS
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 6054de8..727769c 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5167,6 +5167,14 @@
public static final String COLUMN_IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled";
/**
+ * TelephonyProvider column name for determining if the user has enabled cross SIM calling
+ * for this subscription.
+ *
+ * @hide
+ */
+ public static final String COLUMN_CROSS_SIM_CALLING_ENABLED = "cross_sim_calling_enabled";
+
+ /**
* TelephonyProvider column name for whether a subscription is opportunistic, that is,
* whether the network it connects to is limited in functionality or coverage.
* For example, CBRS.
@@ -5270,5 +5278,13 @@
* @hide
*/
public static final String COLUMN_ALLOWED_NETWORK_TYPES = "allowed_network_types";
+
+ /**
+ * TelephonyProvider column name for RCS configuration.
+ * <p>TYPE: BLOB
+ *
+ * @hide
+ */
+ public static final String COLUMN_RCS_CONFIG = "rcs_config";
}
}
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 3f8d75e..7140ff1 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -7,3 +7,5 @@
per-file NetworkSecurityPolicy.java = klyubin@google.com
per-file FrameworkNetworkSecurityPolicy.java = cbrubaker@google.com
per-file FrameworkNetworkSecurityPolicy.java = klyubin@google.com
+per-file Confirmation*.java = jdanis@google.com
+per-file Confirmation*.java = swillden@google.com
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 6ef9e7e..017f405 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -16,18 +16,18 @@
package android.security.keymaster;
-import android.hardware.keymint.Algorithm;
-import android.hardware.keymint.BlockMode;
-import android.hardware.keymint.Digest;
-import android.hardware.keymint.ErrorCode;
-import android.hardware.keymint.HardwareAuthenticatorType;
-import android.hardware.keymint.KeyFormat;
-import android.hardware.keymint.KeyOrigin;
-import android.hardware.keymint.KeyPurpose;
-import android.hardware.keymint.PaddingMode;
-import android.hardware.keymint.SecurityLevel;
-import android.hardware.keymint.Tag;
-import android.hardware.keymint.TagType;
+import android.hardware.security.keymint.Algorithm;
+import android.hardware.security.keymint.BlockMode;
+import android.hardware.security.keymint.Digest;
+import android.hardware.security.keymint.ErrorCode;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
+import android.hardware.security.keymint.KeyFormat;
+import android.hardware.security.keymint.KeyOrigin;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.PaddingMode;
+import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.Tag;
+import android.hardware.security.keymint.TagType;
import java.util.HashMap;
import java.util.Map;
diff --git a/core/java/android/service/attestation/OWNERS b/core/java/android/service/attestation/OWNERS
new file mode 100644
index 0000000..b9e7b99
--- /dev/null
+++ b/core/java/android/service/attestation/OWNERS
@@ -0,0 +1,2 @@
+chaviw@google.com
+ogunwale@google.com
diff --git a/core/java/android/service/storage/OWNERS b/core/java/android/service/storage/OWNERS
new file mode 100644
index 0000000..6f9dbea
--- /dev/null
+++ b/core/java/android/service/storage/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/os/storage/OWNERS
diff --git a/core/java/android/service/textservice/OWNERS b/core/java/android/service/textservice/OWNERS
index a637754..10b8b76 100644
--- a/core/java/android/service/textservice/OWNERS
+++ b/core/java/android/service/textservice/OWNERS
@@ -1,5 +1,3 @@
# Bug component: 34867
-ogunwale@google.com
-roosa@google.com
-yukawa@google.com
+include ../../inputmethodservice/OWNERS
\ No newline at end of file
diff --git a/core/java/android/timezone/OWNERS b/core/java/android/timezone/OWNERS
index 09447a97..8f80897 100644
--- a/core/java/android/timezone/OWNERS
+++ b/core/java/android/timezone/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
index 33bc121..93b5fd4 100644
--- a/core/java/android/uwb/AngleMeasurement.java
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -17,6 +17,7 @@
package android.uwb;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -109,7 +110,7 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeDouble(mRadians);
dest.writeDouble(mErrorRadians);
dest.writeDouble(mConfidenceLevel);
@@ -146,6 +147,7 @@
* @param radians angle in radians
* @throws IllegalArgumentException if angle exceeds allowed limits of [-Math.PI, +Math.PI]
*/
+ @NonNull
public Builder setRadians(double radians) {
if (radians < -Math.PI || radians > Math.PI) {
throw new IllegalArgumentException("Invalid radians: " + radians);
@@ -160,6 +162,7 @@
* @param errorRadians error of the angle in radians
* @throws IllegalArgumentException if the error exceeds the allowed limits of [0, +Math.PI]
*/
+ @NonNull
public Builder setErrorRadians(double errorRadians) {
if (errorRadians < 0.0 || errorRadians > Math.PI) {
throw new IllegalArgumentException(
@@ -175,6 +178,7 @@
* @param confidenceLevel level of confidence of the angle measurement
* @throws IllegalArgumentException if the error exceeds the allowed limits of [0.0, 1.0]
*/
+ @NonNull
public Builder setConfidenceLevel(double confidenceLevel) {
if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
throw new IllegalArgumentException(
@@ -189,6 +193,7 @@
*
* @throws IllegalStateException if angle, error, or confidence values are missing
*/
+ @NonNull
public AngleMeasurement build() {
if (Double.isNaN(mRadians)) {
throw new IllegalStateException("Angle is not set");
diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java
index cd5af69..20a1c7a 100644
--- a/core/java/android/uwb/AngleOfArrivalMeasurement.java
+++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java
@@ -53,7 +53,7 @@
* @return the azimuth {@link AngleMeasurement}
*/
@NonNull
- public AngleMeasurement getAzimuth() {
+ public AngleMeasurement getAzimuthAngleMeasurement() {
return mAzimuthAngleMeasurement;
}
@@ -70,7 +70,7 @@
* @return altitude {@link AngleMeasurement} or null when this is not available
*/
@Nullable
- public AngleMeasurement getAltitude() {
+ public AngleMeasurement getAltitudeAngleMeasurement() {
return mAltitudeAngleMeasurement;
}
@@ -85,8 +85,8 @@
if (obj instanceof AngleOfArrivalMeasurement) {
AngleOfArrivalMeasurement other = (AngleOfArrivalMeasurement) obj;
- return mAzimuthAngleMeasurement.equals(other.getAzimuth())
- && mAltitudeAngleMeasurement.equals(other.getAltitude());
+ return mAzimuthAngleMeasurement.equals(other.getAzimuthAngleMeasurement())
+ && mAltitudeAngleMeasurement.equals(other.getAltitudeAngleMeasurement());
}
return false;
}
@@ -105,7 +105,7 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mAzimuthAngleMeasurement, flags);
dest.writeParcelable(mAltitudeAngleMeasurement, flags);
}
@@ -143,6 +143,7 @@
*
* @param azimuthAngle azimuth angle
*/
+ @NonNull
public Builder setAzimuthAngleMeasurement(@NonNull AngleMeasurement azimuthAngle) {
mAzimuthAngleMeasurement = azimuthAngle;
return this;
@@ -153,6 +154,7 @@
*
* @param altitudeAngle altitude angle
*/
+ @NonNull
public Builder setAltitudeAngleMeasurement(@NonNull AngleMeasurement altitudeAngle) {
mAltitudeAngleMeasurement = altitudeAngle;
return this;
@@ -163,6 +165,7 @@
*
* @throws IllegalStateException if the required azimuth angle is not provided
*/
+ @NonNull
public AngleOfArrivalMeasurement build() {
if (mAzimuthAngleMeasurement == null) {
throw new IllegalStateException("Azimuth angle measurement is not set");
diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java
index c959840..10c2172 100644
--- a/core/java/android/uwb/DistanceMeasurement.java
+++ b/core/java/android/uwb/DistanceMeasurement.java
@@ -17,6 +17,7 @@
package android.uwb;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -106,7 +107,7 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeDouble(mMeters);
dest.writeDouble(mErrorMeters);
dest.writeDouble(mConfidenceLevel);
@@ -143,6 +144,7 @@
* @param meters distance in meters
* @throws IllegalArgumentException if meters is NaN
*/
+ @NonNull
public Builder setMeters(double meters) {
if (Double.isNaN(meters)) {
throw new IllegalArgumentException("meters cannot be NaN");
@@ -157,6 +159,7 @@
* @param errorMeters distance error in meters
* @throws IllegalArgumentException if error is negative or NaN
*/
+ @NonNull
public Builder setErrorMeters(double errorMeters) {
if (Double.isNaN(errorMeters) || errorMeters < 0.0) {
throw new IllegalArgumentException(
@@ -172,6 +175,7 @@
* @param confidenceLevel the confidence level in the distance measurement
* @throws IllegalArgumentException if confidence level is not in the range of [0.0, 1.0]
*/
+ @NonNull
public Builder setConfidenceLevel(double confidenceLevel) {
if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
throw new IllegalArgumentException(
@@ -186,6 +190,7 @@
*
* @throws IllegalStateException if meters, error, or confidence are not set
*/
+ @NonNull
public DistanceMeasurement build() {
if (Double.isNaN(mMeters)) {
throw new IllegalStateException("Meters cannot be NaN");
diff --git a/core/java/android/uwb/OWNERS b/core/java/android/uwb/OWNERS
new file mode 100644
index 0000000..ea41c39
--- /dev/null
+++ b/core/java/android/uwb/OWNERS
@@ -0,0 +1,5 @@
+bstack@google.com
+eliptus@google.com
+jsolnit@google.com
+siyuanh@google.com
+zachoverflow@google.com
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
new file mode 100644
index 0000000..a9bf4ab
--- /dev/null
+++ b/core/java/android/uwb/RangingManager.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Hashtable;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
+ private static final String TAG = "Uwb.RangingManager";
+
+ private final IUwbAdapter mAdapter;
+ private final Hashtable<SessionHandle, RangingSession> mRangingSessionTable = new Hashtable<>();
+
+ public RangingManager(IUwbAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ /**
+ * Open a new ranging session
+ *
+ * @param params the parameters that define the ranging session
+ * @param executor {@link Executor} to run callbacks
+ * @param callbacks {@link RangingSession.Callback} to associate with the {@link RangingSession}
+ * that is being opened.
+ * @return a new {@link RangingSession}
+ */
+ public RangingSession openSession(@NonNull PersistableBundle params, @NonNull Executor executor,
+ @NonNull RangingSession.Callback callbacks) {
+ SessionHandle sessionHandle;
+ try {
+ sessionHandle = mAdapter.startRanging(this, params);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ synchronized (this) {
+ if (hasSession(sessionHandle)) {
+ Log.w(TAG, "Newly created session unexpectedly reuses an active SessionHandle");
+ executor.execute(() -> callbacks.onClosed(
+ RangingSession.Callback.CLOSE_REASON_LOCAL_GENERIC_ERROR,
+ new PersistableBundle()));
+ }
+
+ RangingSession session =
+ new RangingSession(executor, callbacks, mAdapter, sessionHandle);
+ mRangingSessionTable.put(sessionHandle, session);
+ return session;
+ }
+ }
+
+ private boolean hasSession(SessionHandle sessionHandle) {
+ return mRangingSessionTable.containsKey(sessionHandle);
+ }
+
+ @Override
+ public void onRangingStarted(SessionHandle sessionHandle, PersistableBundle parameters) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(TAG,
+ "onRangingStarted - received unexpected SessionHandle: " + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingStarted(parameters);
+ }
+ }
+
+ @Override
+ public void onRangingStartFailed(SessionHandle sessionHandle, int reason,
+ PersistableBundle params) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: "
+ + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingClosed(convertStartFailureToCloseReason(reason), params);
+ mRangingSessionTable.remove(sessionHandle);
+ }
+ }
+
+ @Override
+ public void onRangingClosed(SessionHandle sessionHandle, int reason, PersistableBundle params) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(TAG, "onRangingClosed - received unexpected SessionHandle: " + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingClosed(convertToCloseReason(reason), params);
+ mRangingSessionTable.remove(sessionHandle);
+ }
+ }
+
+ @Override
+ public void onRangingResult(SessionHandle sessionHandle, RangingReport result) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(TAG, "onRangingResult - received unexpected SessionHandle: " + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingResult(result);
+ }
+ }
+
+ @RangingSession.Callback.CloseReason
+ private static int convertToCloseReason(@CloseReason int reason) {
+ switch (reason) {
+ case CloseReason.LOCAL_API:
+ return RangingSession.Callback.CLOSE_REASON_LOCAL_CLOSE_API;
+
+ case CloseReason.MAX_SESSIONS_REACHED:
+ return RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED;
+
+ case CloseReason.SYSTEM_POLICY:
+ return RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY;
+
+ case CloseReason.REMOTE_REQUEST:
+ return RangingSession.Callback.CLOSE_REASON_REMOTE_REQUEST;
+
+ case CloseReason.PROTOCOL_SPECIFIC:
+ return RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC;
+
+ case CloseReason.UNKNOWN:
+ default:
+ return RangingSession.Callback.CLOSE_REASON_UNKNOWN;
+ }
+ }
+
+ @RangingSession.Callback.CloseReason
+ private static int convertStartFailureToCloseReason(@StartFailureReason int reason) {
+ switch (reason) {
+ case StartFailureReason.BAD_PARAMETERS:
+ return RangingSession.Callback.CLOSE_REASON_LOCAL_BAD_PARAMETERS;
+
+ case StartFailureReason.MAX_SESSIONS_REACHED:
+ return RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED;
+
+ case StartFailureReason.SYSTEM_POLICY:
+ return RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY;
+
+ case StartFailureReason.PROTOCOL_SPECIFIC:
+ return RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC;
+
+ case StartFailureReason.UNKNOWN:
+ default:
+ return RangingSession.Callback.CLOSE_REASON_UNKNOWN;
+ }
+ }
+}
diff --git a/core/java/android/uwb/RangingMeasurement.java b/core/java/android/uwb/RangingMeasurement.java
index f1c3162..50e5f0d 100644
--- a/core/java/android/uwb/RangingMeasurement.java
+++ b/core/java/android/uwb/RangingMeasurement.java
@@ -60,6 +60,9 @@
return mRemoteDeviceAddress;
}
+ /**
+ * @hide
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
RANGING_STATUS_SUCCESS,
@@ -115,7 +118,7 @@
* {@link #RANGING_STATUS_SUCCESS}
*/
@Nullable
- public DistanceMeasurement getDistance() {
+ public DistanceMeasurement getDistanceMeasurement() {
return mDistanceMeasurement;
}
@@ -126,7 +129,7 @@
* {@link #RANGING_STATUS_SUCCESS}
*/
@Nullable
- public AngleOfArrivalMeasurement getAngleOfArrival() {
+ public AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
return mAngleOfArrivalMeasurement;
}
@@ -144,8 +147,8 @@
return mRemoteDeviceAddress.equals(other.getRemoteDeviceAddress())
&& mStatus == other.getStatus()
&& mElapsedRealtimeNanos == other.getElapsedRealtimeNanos()
- && mDistanceMeasurement.equals(other.getDistance())
- && mAngleOfArrivalMeasurement.equals(other.getAngleOfArrival());
+ && mDistanceMeasurement.equals(other.getDistanceMeasurement())
+ && mAngleOfArrivalMeasurement.equals(other.getAngleOfArrivalMeasurement());
}
return false;
}
@@ -165,7 +168,7 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mRemoteDeviceAddress, flags);
dest.writeInt(mStatus);
dest.writeLong(mElapsedRealtimeNanos);
@@ -210,6 +213,7 @@
*
* @param remoteDeviceAddress remote device's address
*/
+ @NonNull
public Builder setRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) {
mRemoteDeviceAddress = remoteDeviceAddress;
return this;
@@ -220,6 +224,7 @@
*
* @param status the status of the ranging measurement
*/
+ @NonNull
public Builder setStatus(@Status int status) {
mStatus = status;
return this;
@@ -230,6 +235,7 @@
*
* @param elapsedRealtimeNanos time the ranging measurement occurred
*/
+ @NonNull
public Builder setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
if (elapsedRealtimeNanos < 0) {
throw new IllegalArgumentException("elapsedRealtimeNanos must be >= 0");
@@ -243,6 +249,7 @@
*
* @param distanceMeasurement the distance measurement for this ranging measurement
*/
+ @NonNull
public Builder setDistanceMeasurement(@NonNull DistanceMeasurement distanceMeasurement) {
mDistanceMeasurement = distanceMeasurement;
return this;
@@ -254,6 +261,7 @@
* @param angleOfArrivalMeasurement the angle of arrival measurement for this ranging
* measurement
*/
+ @NonNull
public Builder setAngleOfArrivalMeasurement(
@NonNull AngleOfArrivalMeasurement angleOfArrivalMeasurement) {
mAngleOfArrivalMeasurement = angleOfArrivalMeasurement;
@@ -268,6 +276,7 @@
* elapsedRealtimeNanos of the measurement is invalid, or
* if no remote device address is set
*/
+ @NonNull
public RangingMeasurement build() {
if (mStatus != RANGING_STATUS_SUCCESS) {
if (mDistanceMeasurement != null) {
diff --git a/core/java/android/uwb/RangingReport.java b/core/java/android/uwb/RangingReport.java
index 45180bf..5b5f084 100644
--- a/core/java/android/uwb/RangingReport.java
+++ b/core/java/android/uwb/RangingReport.java
@@ -83,7 +83,7 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeTypedList(mRangingMeasurements);
}
@@ -113,6 +113,7 @@
*
* @param rangingMeasurement a ranging measurement
*/
+ @NonNull
public Builder addMeasurement(@NonNull RangingMeasurement rangingMeasurement) {
mMeasurements.add(rangingMeasurement);
return this;
@@ -123,6 +124,7 @@
*
* @param rangingMeasurements {@link List} of {@link RangingMeasurement}s to add
*/
+ @NonNull
public Builder addMeasurements(@NonNull List<RangingMeasurement> rangingMeasurements) {
mMeasurements.addAll(rangingMeasurements);
return this;
@@ -133,6 +135,7 @@
*
* @throws IllegalStateException if measurements are not in monotonically increasing order
*/
+ @NonNull
public RangingReport build() {
// Verify that all measurement timestamps are monotonically increasing
RangingMeasurement prevMeasurement = null;
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
index 8639269..b0dbd85 100644
--- a/core/java/android/uwb/RangingSession.java
+++ b/core/java/android/uwb/RangingSession.java
@@ -17,7 +17,11 @@
package android.uwb;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Binder;
import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -33,12 +37,26 @@
* {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} to request to open a
* session. Once the session is opened, a {@link RangingSession} object is provided through
* {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)}. If opening a
- * session fails, the failure is reported through {@link RangingSession.Callback#onClosed(int)} with
- * the failure reason.
+ * session fails, the failure is reported through
+ * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} with the failure reason.
*
* @hide
*/
public final class RangingSession implements AutoCloseable {
+ private static final String TAG = "Uwb.RangingSession";
+ private final SessionHandle mSessionHandle;
+ private final IUwbAdapter mAdapter;
+ private final Executor mExecutor;
+ private final Callback mCallback;
+
+ private enum State {
+ INIT,
+ OPEN,
+ CLOSED,
+ }
+
+ private State mState;
+
/**
* Interface for receiving {@link RangingSession} events
*/
@@ -50,8 +68,11 @@
* @param session the newly opened {@link RangingSession}
* @param sessionInfo session specific parameters from lower layers
*/
- void onOpenSuccess(RangingSession session, PersistableBundle sessionInfo);
+ void onOpenSuccess(@NonNull RangingSession session, @NonNull PersistableBundle sessionInfo);
+ /**
+ * @hide
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
CLOSE_REASON_UNKNOWN,
@@ -112,20 +133,46 @@
int CLOSE_REASON_REMOTE_REQUEST = 7;
/**
+ * Indicates that the session was closed for a protocol specific reason. The associated
+ * {@link PersistableBundle} should be consulted for additional information.
+ */
+ int CLOSE_REASON_PROTOCOL_SPECIFIC = 8;
+
+ /**
* Invoked when session is either closed spontaneously, or per user request via
* {@link RangingSession#close()} or {@link AutoCloseable#close()}, or when session failed
* to open.
*
* @param reason reason for the session closure
+ * @param parameters protocol specific parameters related to the close reason
*/
- void onClosed(@CloseReason int reason);
+ void onClosed(@CloseReason int reason, @NonNull PersistableBundle parameters);
/**
* Called once per ranging interval even when a ranging measurement fails
*
* @param rangingReport ranging report for this interval's measurements
*/
- void onReportReceived(RangingReport rangingReport);
+ void onReportReceived(@NonNull RangingReport rangingReport);
+ }
+
+ /**
+ * @hide
+ */
+ public RangingSession(Executor executor, Callback callback, IUwbAdapter adapter,
+ SessionHandle sessionHandle) {
+ mState = State.INIT;
+ mExecutor = executor;
+ mCallback = callback;
+ mAdapter = adapter;
+ mSessionHandle = sessionHandle;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isOpen() {
+ return mState == State.OPEN;
}
/**
@@ -133,16 +180,74 @@
* <p>If this session is currently open, it will close and stop the session.
* <p>If the session is in the process of being opened, it will attempt to stop the session from
* being opened.
- * <p>If the session is already closed, the registered {@link Callback#onClosed(int)} callback
- * will still be invoked.
+ * <p>If the session is already closed, the registered
+ * {@link Callback#onClosed(int, PersistableBundle)} callback will still be invoked.
*
- * <p>{@link Callback#onClosed(int)} will be invoked using the same callback
+ * <p>{@link Callback#onClosed(int, PersistableBundle)} will be invoked using the same callback
* object given to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
* when the {@link RangingSession} was opened. The callback will be invoked after each call to
* {@link #close()}, even if the {@link RangingSession} is already closed.
*/
@Override
public void close() {
- throw new UnsupportedOperationException();
+ if (mState == State.CLOSED) {
+ mExecutor.execute(() -> mCallback.onClosed(
+ Callback.CLOSE_REASON_LOCAL_CLOSE_API, new PersistableBundle()));
+ return;
+ }
+
+ try {
+ mAdapter.closeRanging(mSessionHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void onRangingStarted(@NonNull PersistableBundle parameters) {
+ if (mState == State.CLOSED) {
+ Log.w(TAG, "onRangingStarted invoked for a closed session");
+ return;
+ }
+
+ mState = State.OPEN;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onOpenSuccess(this, parameters));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void onRangingClosed(@Callback.CloseReason int reason, PersistableBundle parameters) {
+ mState = State.CLOSED;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onClosed(reason, parameters));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void onRangingResult(@NonNull RangingReport report) {
+ if (!isOpen()) {
+ Log.w(TAG, "onRangingResult invoked for non-open session");
+ return;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onReportReceived(report));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
diff --git a/core/java/android/uwb/UwbAddress.java b/core/java/android/uwb/UwbAddress.java
index 828324c..b9523a3 100644
--- a/core/java/android/uwb/UwbAddress.java
+++ b/core/java/android/uwb/UwbAddress.java
@@ -51,7 +51,7 @@
* {@link #SHORT_ADDRESS_BYTE_LENGTH} or {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes
*/
@NonNull
- public static UwbAddress fromBytes(@NonNull byte[] address) throws IllegalArgumentException {
+ public static UwbAddress fromBytes(@NonNull byte[] address) {
if (address.length != SHORT_ADDRESS_BYTE_LENGTH
&& address.length != EXTENDED_ADDRESS_BYTE_LENGTH) {
throw new IllegalArgumentException("Invalid UwbAddress length " + address.length);
@@ -107,7 +107,7 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mAddressBytes.length);
dest.writeByteArray(mAddressBytes);
}
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index ed5cf36..f4d8018 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -49,12 +49,16 @@
private IUwbAdapter mUwbAdapter;
private static final String SERVICE_NAME = "uwb";
- private AdapterStateListener mAdapterStateListener;
+ private final AdapterStateListener mAdapterStateListener;
+ private final RangingManager mRangingManager;
/**
* Interface for receiving UWB adapter state changes
*/
public interface AdapterStateCallback {
+ /**
+ * @hide
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
STATE_CHANGED_REASON_SESSION_STARTED,
@@ -116,6 +120,7 @@
private UwbManager(IUwbAdapter adapter) {
mUwbAdapter = adapter;
mAdapterStateListener = new AdapterStateListener(adapter);
+ mRangingManager = new RangingManager(adapter);
}
/**
@@ -195,6 +200,9 @@
}
}
+ /**
+ * @hide
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE,
@@ -387,8 +395,8 @@
*/
@NonNull
public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters,
- @NonNull Executor executor,
+ @NonNull @CallbackExecutor Executor executor,
@NonNull RangingSession.Callback callbacks) {
- throw new UnsupportedOperationException();
+ return mRangingManager.openSession(parameters, executor, callbacks);
}
}
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 5196c3e..bae6ee8 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -8,6 +8,7 @@
sumir@google.com
ogunwale@google.com
jjaggi@google.com
+roosa@google.com
# Display
per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS
@@ -46,11 +47,13 @@
per-file View.java = file:/graphics/java/android/graphics/OWNERS
per-file View.java = file:/services/core/java/com/android/server/input/OWNERS
per-file View.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file View.java = file:/core/java/android/view/inputmethod/OWNERS
per-file ViewRootImpl.java = file:/services/accessibility/OWNERS
per-file ViewRootImpl.java = file:/core/java/android/service/autofill/OWNERS
per-file ViewRootImpl.java = file:/graphics/java/android/graphics/OWNERS
per-file ViewRootImpl.java = file:/services/core/java/com/android/server/input/OWNERS
per-file ViewRootImpl.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ViewRootImpl.java = file:/core/java/android/view/inputmethod/OWNERS
# WindowManager
per-file DisplayCutout.aidl = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/view/textservice/OWNERS b/core/java/android/view/textservice/OWNERS
index a637754..582be8d 100644
--- a/core/java/android/view/textservice/OWNERS
+++ b/core/java/android/view/textservice/OWNERS
@@ -1,5 +1,3 @@
# Bug component: 34867
-ogunwale@google.com
-roosa@google.com
-yukawa@google.com
+include ../inputmethod/OWNERS
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index fbb975b..718076b 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -7,3 +7,5 @@
siyamed@google.com
per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com
+
+per-file SpellChecker.java = file:../view/inputmethod/OWNERS
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 108ab92..382b49e 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -1 +1,4 @@
per-file *AppOp* = file:/core/java/android/permission/OWNERS
+per-file *Resolver* = file:/packages/SystemUI/OWNERS
+per-file *Chooser* = file:/packages/SystemUI/OWNERS
+per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
diff --git a/core/java/com/android/internal/app/chooser/OWNERS b/core/java/com/android/internal/app/chooser/OWNERS
new file mode 100644
index 0000000..a6f1632
--- /dev/null
+++ b/core/java/com/android/internal/app/chooser/OWNERS
@@ -0,0 +1 @@
+file:/packages/SystemUI/OWNERS
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index e595db3..c416814 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -78,13 +78,16 @@
boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists();
if (useSystemSuspend) {
- // Get both kernel and native wakelock stats from SystemSuspend
- updateVersion(staleStats);
- if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
- Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend");
- return null;
+ // static read/write lock protection for sKernelWakelockUpdateVersion
+ synchronized (KernelWakelockReader.class) {
+ // Get both kernel and native wakelock stats from SystemSuspend
+ updateVersion(staleStats);
+ if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+ Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend");
+ return null;
+ }
+ return removeOldStats(staleStats);
}
- return removeOldStats(staleStats);
} else {
Arrays.fill(mKernelWakelockBuffer, (byte) 0);
int len = 0;
@@ -141,14 +144,17 @@
}
}
- updateVersion(staleStats);
- // Get native wakelock stats from SystemSuspend
- if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
- Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
+ // static read/write lock protection for sKernelWakelockUpdateVersion
+ synchronized (KernelWakelockReader.class) {
+ updateVersion(staleStats);
+ // Get native wakelock stats from SystemSuspend
+ if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+ Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
+ }
+ // Get kernel wakelock stats
+ parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats);
+ return removeOldStats(staleStats);
}
- // Get kernel wakelock stats
- parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats);
- return removeOldStats(staleStats);
}
}
diff --git a/core/java/com/android/internal/util/AnnotationValidations.java b/core/java/com/android/internal/util/AnnotationValidations.java
index 2d3b450..cf5e48f 100644
--- a/core/java/com/android/internal/util/AnnotationValidations.java
+++ b/core/java/com/android/internal/util/AnnotationValidations.java
@@ -182,7 +182,7 @@
Annotation ignored, int value, Object... params) {}
public static void validate(Class<? extends Annotation> annotation,
Annotation ignored, int value) {
- if (("android.annotation".equals(annotation.getPackageName$())
+ if (("android.annotation".equals(annotation.getPackageName())
&& annotation.getSimpleName().endsWith("Res"))
|| ColorInt.class.equals(annotation)) {
if (value < 0) {
@@ -192,7 +192,7 @@
}
public static void validate(Class<? extends Annotation> annotation,
Annotation ignored, long value) {
- if ("android.annotation".equals(annotation.getPackageName$())
+ if ("android.annotation".equals(annotation.getPackageName())
&& annotation.getSimpleName().endsWith("Long")) {
if (value < 0L) {
invalid(annotation, value);
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 9ad1502..931ec64 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -734,6 +734,25 @@
}
/**
+ * Throws {@link ArrayIndexOutOfBoundsException} if the range is out of bounds.
+ * @param len length of the array. Must be non-negative
+ * @param offset start index of the range. Must be non-negative
+ * @param count length of the range. Must be non-negative
+ * @throws ArrayIndexOutOfBoundsException if the range from {@code offset} with length
+ * {@code count} is out of bounds of the array
+ */
+ public static void throwsIfOutOfBounds(int len, int offset, int count) {
+ if (len < 0) {
+ throw new ArrayIndexOutOfBoundsException("Negative length: " + len);
+ }
+
+ if ((offset | count) < 0 || offset > len - count) {
+ throw new ArrayIndexOutOfBoundsException(
+ "length=" + len + "; regionStart=" + offset + "; regionLength=" + count);
+ }
+ }
+
+ /**
* Returns an array with values from {@code val} minus {@code null} values
*
* @param arrayConstructor typically {@code T[]::new} e.g. {@code String[]::new}
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index 8b9acd3..a045451 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -2,3 +2,4 @@
per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
per-file Protocol* = etancohen@google.com, lorenzo@google.com
per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
+per-file DataClass* = eugenesusla@google.com
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS
new file mode 100644
index 0000000..851d1f3
--- /dev/null
+++ b/core/java/com/android/internal/view/OWNERS
@@ -0,0 +1,20 @@
+# Bug component: 25700
+
+file:/core/java/android/view/OWNERS
+
+# Autofill
+per-file IInlineSuggestions*.aidl = file:/core/java/android/service/autofill/OWNERS
+per-file InlineSuggestions*.java = file:/core/java/android/service/autofill/OWNERS
+
+# Ime
+per-file *Input* = file:/services/core/java/com/android/server/inputmethod/OWNERS
+
+# Surface
+per-file *Surface* = file:/graphics/java/android/graphics/OWNERS
+per-file *Surface* = file:/services/core/java/com/android/server/wm/OWNERS
+
+# WindowManager
+per-file AppearanceRegion = file:/services/core/java/com/android/server/wm/OWNERS
+per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 8048298..54b0340 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -20,11 +20,17 @@
per-file android_graphics_BLASTBufferQueue.cpp = file:/services/core/java/com/android/server/wm/OWNERS
per-file android_view_Surface* = file:/services/core/java/com/android/server/wm/OWNERS
+# Resources
+per-file android_content_res_* = file:/core/java/android/content/res/OWNERS
+per-file android_util_AssetManager* = file:/core/java/android/content/res/OWNERS
+per-file android_util_StringBlock* = file:/core/java/android/content/res/OWNERS
+per-file android_util_XmlBlock* = file:/core/java/android/content/res/OWNERS
+per-file com_android_internal_content_om_OverlayConfig* = file:/core/java/android/content/res/OWNERS
+
per-file *Zygote* = file:/ZYGOTE_OWNERS
per-file Android.bp = file:platform/build/soong:/OWNERS
per-file android_animation_* = file:/core/java/android/animation/OWNERS
per-file android_app_admin_* = file:/core/java/android/app/admin/OWNERS
-per-file android_content_res_* = file:/core/java/android/content/res/OWNERS
per-file android_graphics_* = file:/graphics/java/android/graphics/OWNERS
per-file android_hardware_Usb* = file:/services/usb/OWNERS
per-file android_hardware_display_* = file:/core/java/android/hardware/display/OWNERS
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index fbdd406..444bb66 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -24,6 +24,7 @@
#include "utils/misc.h"
#include "utils/Trace.h"
+#include "android_util_AssetManager_private.h"
#include "core_jni_helpers.h"
#include "jni.h"
#include "nativehelper/ScopedUtfChars.h"
@@ -347,12 +348,17 @@
return 0;
}
+ const auto buffer = asset->getIncFsBuffer(true /* aligned */);
+ const size_t length = asset->getLength();
+ if (!buffer.convert<uint8_t>().verify(length)) {
+ jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+ return 0;
+ }
+
// DynamicRefTable is only needed when looking up resource references. Opening an XML file
// directly from an ApkAssets has no notion of proper resource references.
- std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/);
- status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
- asset.reset();
-
+ auto xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/);
+ status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true);
if (err != NO_ERROR) {
jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
return 0;
diff --git a/core/jni/android_media_AudioDeviceAttributes.cpp b/core/jni/android_media_AudioDeviceAttributes.cpp
index e79c95e..2a16dce 100644
--- a/core/jni/android_media_AudioDeviceAttributes.cpp
+++ b/core/jni/android_media_AudioDeviceAttributes.cpp
@@ -31,7 +31,7 @@
const AudioDeviceTypeAddr *devTypeAddr) {
jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
jint jNativeType = (jint)devTypeAddr->mType;
- ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->mAddress.data()));
+ ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->getAddress()));
*jAudioDeviceAttributes = env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor,
jNativeType, jAddress.get());
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7493e39..ae72525 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -172,6 +172,8 @@
jmethodID postRecordConfigEventFromNative;
} gAudioPolicyEventHandlerMethods;
+static struct { jmethodID add; } gListMethods;
+
//
// JNI Initialization for OpenSLES routing
//
@@ -310,7 +312,7 @@
static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes,
jobjectArray deviceAddresses,
- Vector<AudioDeviceTypeAddr> &audioDeviceTypeAddrVector) {
+ AudioDeviceTypeAddrVector &audioDeviceTypeAddrVector) {
if (deviceTypes == nullptr || deviceAddresses == nullptr) {
return (jint)AUDIO_JAVA_BAD_VALUE;
}
@@ -337,7 +339,7 @@
}
const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL);
AudioDeviceTypeAddr dev = AudioDeviceTypeAddr((audio_devices_t)typesPtr[i], address);
- audioDeviceTypeAddrVector.add(dev);
+ audioDeviceTypeAddrVector.push_back(dev);
env->ReleaseStringUTFChars((jstring)addrJobj, address);
}
env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
@@ -2063,7 +2065,7 @@
static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz,
jint uid, jintArray deviceTypes, jobjectArray deviceAddresses) {
- Vector<AudioDeviceTypeAddr> deviceVector;
+ AudioDeviceTypeAddrVector deviceVector;
jint results = getVectorOfAudioDeviceTypeAddr(env, deviceTypes, deviceAddresses, deviceVector);
if (results != NO_ERROR) {
return results;
@@ -2081,7 +2083,7 @@
static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, jobject clazz,
jint userId, jintArray deviceTypes,
jobjectArray deviceAddresses) {
- Vector<AudioDeviceTypeAddr> deviceVector;
+ AudioDeviceTypeAddrVector deviceVector;
jint results = getVectorOfAudioDeviceTypeAddr(env, deviceTypes, deviceAddresses, deviceVector);
if (results != NO_ERROR) {
return results;
@@ -2362,48 +2364,48 @@
return AudioSystem::isCallScreenModeSupported();
}
-static jint
-android_media_AudioSystem_setPreferredDeviceForStrategy(JNIEnv *env, jobject thiz,
- jint strategy, jint deviceType, jstring deviceAddress) {
-
- const char *c_address = env->GetStringUTFChars(deviceAddress, NULL);
+static jint android_media_AudioSystem_setDevicesRoleForStrategy(JNIEnv *env, jobject thiz,
+ jint strategy, jint role,
+ jintArray jDeviceTypes,
+ jobjectArray jDeviceAddresses) {
+ AudioDeviceTypeAddrVector nDevices;
+ jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+ if (results != NO_ERROR) {
+ return results;
+ }
int status = check_AudioSystem_Command(
- AudioSystem::setPreferredDeviceForStrategy((product_strategy_t)strategy,
- AudioDeviceTypeAddr((audio_devices_t)
- deviceType,
- c_address)));
- env->ReleaseStringUTFChars(deviceAddress, c_address);
+ AudioSystem::setDevicesRoleForStrategy((product_strategy_t)strategy,
+ (device_role_t)role, nDevices));
return (jint) status;
}
-static jint
-android_media_AudioSystem_removePreferredDeviceForStrategy(JNIEnv *env, jobject thiz, jint strategy)
-{
- return (jint) check_AudioSystem_Command(
- AudioSystem::removePreferredDeviceForStrategy((product_strategy_t) strategy));
+static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz,
+ jint strategy, jint role) {
+ return (jint)check_AudioSystem_Command(
+ AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)strategy,
+ (device_role_t)role));
}
-static jint
-android_media_AudioSystem_getPreferredDeviceForStrategy(JNIEnv *env, jobject thiz,
- jint strategy, jobjectArray jDeviceArray)
-{
- if (jDeviceArray == nullptr || env->GetArrayLength(jDeviceArray) != 1) {
- ALOGE("%s invalid array to store AudioDeviceAttributes", __FUNCTION__);
- return (jint)AUDIO_JAVA_BAD_VALUE;
- }
-
- AudioDeviceTypeAddr elDevice;
+static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject thiz,
+ jint strategy, jint role,
+ jobject jDevices) {
+ AudioDeviceTypeAddrVector nDevices;
status_t status = check_AudioSystem_Command(
- AudioSystem::getPreferredDeviceForStrategy((product_strategy_t) strategy, elDevice));
+ AudioSystem::getDevicesForRoleAndStrategy((product_strategy_t)strategy,
+ (device_role_t)role, nDevices));
if (status != NO_ERROR) {
return (jint) status;
}
- jobject jAudioDeviceAttributes = NULL;
- jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &elDevice);
- if (jStatus == AUDIO_JAVA_SUCCESS) {
- env->SetObjectArrayElement(jDeviceArray, 0, jAudioDeviceAttributes);
+ for (const auto &device : nDevices) {
+ jobject jAudioDeviceAttributes = NULL;
+ jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes);
+ env->DeleteLocalRef(jAudioDeviceAttributes);
}
- return jStatus;
+ return AUDIO_JAVA_SUCCESS;
}
static jint
@@ -2551,12 +2553,12 @@
{"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids},
{"isCallScreeningModeSupported", "()Z",
(void *)android_media_AudioSystem_isCallScreeningModeSupported},
- {"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I",
- (void *)android_media_AudioSystem_setPreferredDeviceForStrategy},
- {"removePreferredDeviceForStrategy", "(I)I",
- (void *)android_media_AudioSystem_removePreferredDeviceForStrategy},
- {"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDeviceAttributes;)I",
- (void *)android_media_AudioSystem_getPreferredDeviceForStrategy},
+ {"setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_setDevicesRoleForStrategy},
+ {"removeDevicesRoleForStrategy", "(II)I",
+ (void *)android_media_AudioSystem_removeDevicesRoleForStrategy},
+ {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
+ (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy},
{"getDevicesForAttributes",
"(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I",
(void *)android_media_AudioSystem_getDevicesForAttributes},
@@ -2758,6 +2760,9 @@
gMidAudioRecordRoutingProxy_release =
android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "native_release", "()V");
+ jclass listClass = FindClassOrDie(env, "java/util/List");
+ gListMethods.add = GetMethodIDOrDie(env, listClass, "add", "(Ljava/lang/Object;)Z");
+
AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback);
RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index e6881b3..b2c69a0 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -39,10 +39,10 @@
#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeResolution.h"
#include "androidfw/MutexGuard.h"
-#include "androidfw/PosixUtils.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"
+#include "android_util_AssetManager_private.h"
#include "core_jni_helpers.h"
#include "jni.h"
#include "nativehelper/JNIPlatformHelp.h"
@@ -58,7 +58,6 @@
extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
using ::android::base::StringPrintf;
-using ::android::util::ExecuteBinary;
namespace android {
@@ -114,101 +113,17 @@
return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
}
-static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env,
- jclass /*clazz*/) {
- // --input-directory can be given multiple times, but idmap2 expects the directory to exist
- std::vector<std::string> input_dirs;
- struct stat st;
- if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::SYSTEM_EXT_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::SYSTEM_EXT_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::ODM_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::OEM_OVERLAY_DIR);
- }
-
- if (input_dirs.empty()) {
- LOG(WARNING) << "no directories for idmap2 to scan";
- return env->NewObjectArray(0, g_stringClass, nullptr);
- }
-
- if (access("/system/bin/idmap2", X_OK) == -1) {
- PLOG(WARNING) << "unable to execute idmap2";
- return nullptr;
- }
-
- std::vector<std::string> argv{"/system/bin/idmap2",
- "scan",
- "--recursive",
- "--target-package-name", "android",
- "--target-apk-path", "/system/framework/framework-res.apk",
- "--output-directory", "/data/resource-cache"};
-
- for (const auto& dir : input_dirs) {
- argv.push_back("--input-directory");
- argv.push_back(dir);
- }
-
- const auto result = ExecuteBinary(argv);
-
- if (!result) {
- LOG(ERROR) << "failed to execute idmap2";
- return nullptr;
- }
-
- if (result->status != 0) {
- LOG(ERROR) << "idmap2: " << result->stderr;
- return nullptr;
- }
-
- std::vector<std::string> idmap_paths;
- std::istringstream input(result->stdout);
- std::string path;
- while (std::getline(input, path)) {
- idmap_paths.push_back(path);
- }
-
- jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
- if (array == nullptr) {
- return nullptr;
- }
- for (size_t i = 0; i < idmap_paths.size(); i++) {
- const std::string path = idmap_paths[i];
- jstring java_string = env->NewStringUTF(path.c_str());
- if (env->ExceptionCheck()) {
- return nullptr;
- }
- env->SetObjectArrayElement(array, i, java_string);
- env->DeleteLocalRef(java_string);
- }
- return array;
-}
-
-static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
- uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
- env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
+static jint CopyValue(JNIEnv* env, const AssetManager2::SelectedValue& value,
+ jobject out_typed_value) {
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.type);
env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie,
- ApkAssetsCookieToJavaCookie(cookie));
+ ApkAssetsCookieToJavaCookie(value.cookie));
env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data);
env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr);
- env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref);
- env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags);
- if (config != nullptr) {
- env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density);
- }
- return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie));
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, value.resid);
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, value.flags);
+ env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, value.config.density);
+ return static_cast<jint>(ApkAssetsCookieToJavaCookie(value.cookie));
}
// ----------------------------------------------------------------------------
@@ -653,15 +568,15 @@
return 0;
}
- // May be nullptr.
- std::shared_ptr<const DynamicRefTable> dynamic_ref_table =
- assetmanager->GetDynamicRefTableForCookie(cookie);
+ const incfs::map_ptr<void> buffer = asset->getIncFsBuffer(true /* aligned */);
+ const size_t length = asset->getLength();
+ if (!buffer.convert<uint8_t>().verify(length)) {
+ jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+ return 0;
+ }
- std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(
- std::move(dynamic_ref_table));
- status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
- asset.reset();
-
+ auto xml_tree = util::make_unique<ResXMLTree>(assetmanager->GetDynamicRefTableForCookie(cookie));
+ status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true);
if (err != NO_ERROR) {
jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
return 0;
@@ -690,15 +605,15 @@
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
- // May be nullptr.
- std::shared_ptr<const DynamicRefTable> dynamic_ref_table =
- assetmanager->GetDynamicRefTableForCookie(cookie);
+ const incfs::map_ptr<void> buffer = asset->getIncFsBuffer(true /* aligned */);
+ const size_t length = asset->getLength();
+ if (!buffer.convert<uint8_t>().verify(length)) {
+ jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+ return 0;
+ }
- std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(
- std::move(dynamic_ref_table));
- status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
- asset.reset();
-
+ auto xml_tree = util::make_unique<ResXMLTree>(assetmanager->GetDynamicRefTableForCookie(cookie));
+ status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true);
if (err != NO_ERROR) {
jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
return 0;
@@ -710,67 +625,62 @@
jshort density, jobject typed_value,
jboolean resolve_references) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
- ApkAssetsCookie cookie =
- assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
- static_cast<uint16_t>(density), &value, &selected_config, &flags);
- if (cookie == kInvalidCookie) {
+ auto value = assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
+ static_cast<uint16_t>(density));
+ if (!value.has_value()) {
+ ThrowIfIOError(env, value);
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
}
- uint32_t ref = static_cast<uint32_t>(resid);
if (resolve_references) {
- cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
+ auto result = assetmanager->ResolveReference(value.value());
+ if (!result.has_value()) {
+ ThrowIfIOError(env, result);
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
}
}
- return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
+ return CopyValue(env, *value, typed_value);
}
static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
jint bag_entry_id, jobject typed_value) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
+ auto bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (!bag.has_value()) {
+ ThrowIfIOError(env, bag);
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
}
- uint32_t type_spec_flags = bag->type_spec_flags;
- ApkAssetsCookie cookie = kInvalidCookie;
- const Res_value* bag_value = nullptr;
- for (const ResolvedBag::Entry& entry : bag) {
- if (entry.key == static_cast<uint32_t>(bag_entry_id)) {
- cookie = entry.cookie;
- bag_value = &entry.value;
+ // The legacy would find the last entry with the target bag entry id
+ using reverse_bag_iterator = std::reverse_iterator<const ResolvedBag::Entry*>;
+ const auto rbegin = reverse_bag_iterator(end(*bag));
+ const auto rend = reverse_bag_iterator(begin(*bag));
+ auto entry = std::find_if(rbegin, rend, [bag_entry_id](auto&& e) {
+ return e.key == static_cast<uint32_t>(bag_entry_id);
+ });
- // Keep searching (the old implementation did that).
- }
- }
-
- if (cookie == kInvalidCookie) {
+ if (entry == rend) {
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
}
- Res_value value = *bag_value;
- uint32_t ref = static_cast<uint32_t>(resid);
- ResTable_config selected_config;
- cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref);
- if (cookie == kInvalidCookie) {
+ AssetManager2::SelectedValue attr_value(*bag, *entry);
+ auto result = assetmanager->ResolveReference(attr_value);
+ if (!result.has_value()) {
+ ThrowIfIOError(env, result);
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
}
- return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value);
+ return CopyValue(env, attr_value, typed_value);
}
static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
+ auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (!bag_result.has_value()) {
+ ThrowIfIOError(env, bag_result);
return nullptr;
}
+ const ResolvedBag* bag = *bag_result;
jintArray array = env->NewIntArray(bag->entry_count);
if (env->ExceptionCheck()) {
return nullptr;
@@ -786,42 +696,47 @@
static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr,
jint resid) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
+ auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (!bag_result.has_value()) {
+ ThrowIfIOError(env, bag_result);
return nullptr;
}
+ const ResolvedBag* bag = *bag_result;
jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr);
if (array == nullptr) {
return nullptr;
}
for (uint32_t i = 0; i < bag->entry_count; i++) {
- const ResolvedBag::Entry& entry = bag->entries[i];
-
// Resolve any references to their final value.
- Res_value value = entry.value;
- ResTable_config selected_config;
- uint32_t flags;
- uint32_t ref;
- ApkAssetsCookie cookie =
- assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
+ AssetManager2::SelectedValue attr_value(bag, bag->entries[i]);
+ auto result = assetmanager->ResolveReference(attr_value);
+ if (!result.has_value()) {
+ ThrowIfIOError(env, result);
return nullptr;
}
- if (value.dataType == Res_value::TYPE_STRING) {
- const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie];
+ if (attr_value.type == Res_value::TYPE_STRING) {
+ const ApkAssets* apk_assets = assetmanager->GetApkAssets()[attr_value.cookie];
const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool();
jstring java_string = nullptr;
- size_t str_len;
- const char* str_utf8 = pool->string8At(value.data, &str_len);
- if (str_utf8 != nullptr) {
- java_string = env->NewStringUTF(str_utf8);
+ auto str_utf8 = pool->string8At(attr_value.data);
+ if (UNLIKELY(ThrowIfIOError(env, str_utf8))) {
+ return nullptr;
+ }
+
+ if (str_utf8.has_value()) {
+ java_string = env->NewStringUTF(str_utf8->data());
} else {
- const char16_t* str_utf16 = pool->stringAt(value.data, &str_len);
- java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len);
+ auto str_utf16 = pool->stringAt(attr_value.data);
+ if (!str_utf16.has_value()) {
+ ThrowIfIOError(env, str_utf16);
+ return nullptr;
+ }
+ java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16->data()),
+ str_utf16->size());
}
// Check for errors creating the strings (if malformed or no memory).
@@ -842,11 +757,13 @@
static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
jint resid) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
+ auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (!bag_result.has_value()) {
+ ThrowIfIOError(env, bag_result);
return nullptr;
}
+ const ResolvedBag* bag = *bag_result;
jintArray array = env->NewIntArray(bag->entry_count * 2);
if (array == nullptr) {
return nullptr;
@@ -858,24 +775,20 @@
}
for (size_t i = 0; i < bag->entry_count; i++) {
- const ResolvedBag::Entry& entry = bag->entries[i];
- Res_value value = entry.value;
- ResTable_config selected_config;
- uint32_t flags;
- uint32_t ref;
- ApkAssetsCookie cookie =
- assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
+ AssetManager2::SelectedValue attr_value(bag, bag->entries[i]);
+ auto result = assetmanager->ResolveReference(attr_value);
+ if (!result.has_value()) {
env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
+ ThrowIfIOError(env, result);
return nullptr;
}
jint string_index = -1;
- if (value.dataType == Res_value::TYPE_STRING) {
- string_index = static_cast<jint>(value.data);
+ if (attr_value.type == Res_value::TYPE_STRING) {
+ string_index = static_cast<jint>(attr_value.data);
}
- buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
+ buffer[i * 2] = ApkAssetsCookieToJavaCookie(attr_value.cookie);
buffer[(i * 2) + 1] = string_index;
}
env->ReleasePrimitiveArrayCritical(array, buffer, 0);
@@ -884,11 +797,13 @@
static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
+ auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (!bag_result.has_value()) {
+ ThrowIfIOError(env, bag_result);
return nullptr;
}
+ const ResolvedBag* bag = *bag_result;
jintArray array = env->NewIntArray(bag->entry_count);
if (array == nullptr) {
return nullptr;
@@ -900,40 +815,39 @@
}
for (size_t i = 0; i < bag->entry_count; i++) {
- const ResolvedBag::Entry& entry = bag->entries[i];
- Res_value value = entry.value;
- ResTable_config selected_config;
- uint32_t flags;
- uint32_t ref;
- ApkAssetsCookie cookie =
- assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
- env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
+ AssetManager2::SelectedValue attr_value(bag, bag->entries[i]);
+ auto result = assetmanager->ResolveReference(attr_value);
+ if (!result.has_value()) {
+ env->ReleasePrimitiveArrayCritical(array, buffer, 0);
+ ThrowIfIOError(env, result);
return nullptr;
}
- if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) {
- buffer[i] = static_cast<jint>(value.data);
+ if (attr_value.type >= Res_value::TYPE_FIRST_INT &&
+ attr_value.type <= Res_value::TYPE_LAST_INT) {
+ buffer[i] = static_cast<jint>(attr_value.data);
}
}
env->ReleasePrimitiveArrayCritical(array, buffer, 0);
return array;
}
-static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
- return -1;
- }
- return static_cast<jint>(bag->entry_count);
+static jint NativeGetResourceArraySize(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ auto bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (!bag.has_value()) {
+ ThrowIfIOError(env, bag);
+ return -1;
+ }
+ return static_cast<jint>((*bag)->entry_count);
}
static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
jintArray out_data) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
+ auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (!bag_result.has_value()) {
+ ThrowIfIOError(env, bag_result);
return -1;
}
@@ -942,8 +856,10 @@
return -1;
}
+ const ResolvedBag* bag = *bag_result;
if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough");
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Input array is not large enough");
return -1;
}
@@ -954,31 +870,26 @@
jint* cursor = buffer;
for (size_t i = 0; i < bag->entry_count; i++) {
- const ResolvedBag::Entry& entry = bag->entries[i];
- Res_value value = entry.value;
- ResTable_config selected_config;
- selected_config.density = 0;
- uint32_t flags = bag->type_spec_flags;
- uint32_t ref = 0;
- ApkAssetsCookie cookie =
- assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
+ AssetManager2::SelectedValue attr_value(bag, bag->entries[i]);
+ auto result = assetmanager->ResolveReference(attr_value);
+ if (!result.has_value()) {
env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
+ ThrowIfIOError(env, bag_result);
return -1;
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
+ if (attr_value.type == Res_value::TYPE_REFERENCE && attr_value.data == 0) {
+ attr_value.type = Res_value::TYPE_NULL;
+ attr_value.data = Res_value::DATA_NULL_UNDEFINED;
}
- cursor[STYLE_TYPE] = static_cast<jint>(value.dataType);
- cursor[STYLE_DATA] = static_cast<jint>(value.data);
- cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
- cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref);
- cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags);
- cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density);
+ cursor[STYLE_TYPE] = static_cast<jint>(attr_value.type);
+ cursor[STYLE_DATA] = static_cast<jint>(attr_value.data);
+ cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(attr_value.cookie);
+ cursor[STYLE_RESOURCE_ID] = static_cast<jint>(attr_value.resid);
+ cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(attr_value.flags);
+ cursor[STYLE_DENSITY] = static_cast<jint>(attr_value.config.density);
cursor += STYLE_NUM_ENTRIES;
}
env->ReleasePrimitiveArrayCritical(out_data, buffer, 0);
@@ -1006,60 +917,71 @@
CHECK(package_utf8.c_str() != nullptr);
package = package_utf8.c_str();
}
+
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package));
+ auto resid = assetmanager->GetResourceId(name_utf8.c_str(), type, package);
+ if (!resid.has_value()) {
+ ThrowIfIOError(env, resid);
+ return 0;
+ }
+
+ return static_cast<jint>(*resid);
}
static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- AssetManager2::ResourceName name;
- if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid));
+ if (!name.has_value()) {
+ ThrowIfIOError(env, name);
return nullptr;
}
- std::string result = ToFormattedResourceString(&name);
+ const std::string result = ToFormattedResourceString(name.value());
return env->NewStringUTF(result.c_str());
}
static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- AssetManager2::ResourceName name;
- if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid));
+ if (!name.has_value()) {
+ ThrowIfIOError(env, name);
return nullptr;
}
- if (name.package != nullptr) {
- return env->NewStringUTF(name.package);
+ if (name->package != nullptr) {
+ return env->NewStringUTF(name->package);
}
return nullptr;
}
static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- AssetManager2::ResourceName name;
- if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid));
+ if (!name.has_value()) {
+ ThrowIfIOError(env, name);
return nullptr;
}
- if (name.type != nullptr) {
- return env->NewStringUTF(name.type);
- } else if (name.type16 != nullptr) {
- return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len);
+ if (name->type != nullptr) {
+ return env->NewStringUTF(name->type);
+ } else if (name->type16 != nullptr) {
+ return env->NewString(reinterpret_cast<const jchar*>(name->type16), name->type_len);
}
return nullptr;
}
static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- AssetManager2::ResourceName name;
- if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid));
+ if (!name.has_value()) {
+ ThrowIfIOError(env, name);
return nullptr;
}
- if (name.entry != nullptr) {
- return env->NewStringUTF(name.entry);
- } else if (name.entry16 != nullptr) {
- return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len);
+ if (name->entry != nullptr) {
+ return env->NewStringUTF(name->entry);
+ } else if (name->entry16 != nullptr) {
+ return env->NewString(reinterpret_cast<const jchar*>(name->entry16), name->entry_len);
}
return nullptr;
}
@@ -1123,17 +1045,21 @@
static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- std::set<ResTable_config> configurations =
- assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/);
+ auto configurations = assetmanager->GetResourceConfigurations(true /*exclude_system*/,
+ false /*exclude_mipmap*/);
+ if (!configurations.has_value()) {
+ ThrowIfIOError(env, configurations);
+ return nullptr;
+ }
jobjectArray array =
- env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr);
+ env->NewObjectArray(configurations->size(), gConfigurationOffsets.classObject, nullptr);
if (array == nullptr) {
return nullptr;
}
size_t idx = 0;
- for (const ResTable_config& configuration : configurations) {
+ for (const ResTable_config& configuration : *configurations) {
jobject java_configuration = ConstructConfigurationObject(env, configuration);
if (java_configuration == nullptr) {
return nullptr;
@@ -1156,13 +1082,10 @@
(void) assetmanager;
// Load default style from attribute, if specified...
- uint32_t def_style_flags = 0u;
if (def_style_attr != 0) {
- Res_value value;
- if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- def_style_resid = value.data;
- }
+ auto value = theme->GetAttribute(def_style_attr);
+ if (value.has_value() && value->type == Res_value::TYPE_REFERENCE) {
+ def_style_resid = value->data;
}
}
@@ -1203,10 +1126,11 @@
return;
}
- ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
- static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len,
- out_values, out_indices);
+ auto result = ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
+ static_cast<uint32_t>(def_style_resid),
+ reinterpret_cast<uint32_t*>(attrs), attrs_len, out_values, out_indices);
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ ThrowIfIOError(env, result);
}
static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
@@ -1267,11 +1191,12 @@
Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
CHECK(theme->GetAssetManager() == &(*assetmanager));
(void) assetmanager;
-
- bool result = ResolveAttrs(
- theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid),
- reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs),
- attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices));
+ auto result =
+ ResolveAttrs(theme, static_cast<uint32_t>(def_style_attr),
+ static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(values),
+ values_len, reinterpret_cast<uint32_t*>(attrs), attrs_len,
+ reinterpret_cast<uint32_t*>(out_values),
+ reinterpret_cast<uint32_t*>(out_indices));
if (out_indices != nullptr) {
env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
}
@@ -1280,8 +1205,13 @@
if (values != nullptr) {
env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
}
+
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
- return result ? JNI_TRUE : JNI_FALSE;
+ if (!result.has_value()) {
+ ThrowIfIOError(env, result);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
}
static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr,
@@ -1322,18 +1252,22 @@
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
-
- bool result = RetrieveAttributes(assetmanager.get(), xml_parser,
- reinterpret_cast<uint32_t*>(attrs), attrs_len,
- reinterpret_cast<uint32_t*>(out_values),
- reinterpret_cast<uint32_t*>(out_indices));
+ auto result =
+ RetrieveAttributes(assetmanager.get(), xml_parser, reinterpret_cast<uint32_t*>(attrs),
+ attrs_len, reinterpret_cast<uint32_t*>(out_values),
+ reinterpret_cast<uint32_t*>(out_indices));
if (out_indices != nullptr) {
env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
}
+
env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
- return result ? JNI_TRUE : JNI_FALSE;
+ if (!result.has_value()) {
+ ThrowIfIOError(env, result);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
}
static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
@@ -1352,7 +1286,9 @@
Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
CHECK(theme->GetAssetManager() == &(*assetmanager));
(void) assetmanager;
- theme->ApplyStyle(static_cast<uint32_t>(resid), force);
+
+ auto result = theme->ApplyStyle(static_cast<uint32_t>(resid), force);
+ ThrowIfIOError(env, result);
// TODO(adamlesinski): Consider surfacing exception when result is failure.
// CTS currently expects no exceptions from this method.
@@ -1365,19 +1301,22 @@
Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
+ ScopedLock<AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr));
+ CHECK(src_theme->GetAssetManager() == &(*src_assetmanager));
+ (void) src_assetmanager;
+
if (dst_asset_manager_ptr != src_asset_manager_ptr) {
ScopedLock<AssetManager2> dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr));
CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager));
(void) dst_assetmanager;
- ScopedLock <AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr));
- CHECK(src_theme->GetAssetManager() == &(*src_assetmanager));
- (void) src_assetmanager;
-
- dst_theme->SetTo(*src_theme);
- } else {
- dst_theme->SetTo(*src_theme);
+ auto result = dst_theme->SetTo(*src_theme);
+ ThrowIfIOError(env, result);
+ return;
}
+
+ auto result = dst_theme->SetTo(*src_theme);
+ ThrowIfIOError(env, result);
}
static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
@@ -1392,23 +1331,21 @@
CHECK(theme->GetAssetManager() == &(*assetmanager));
(void) assetmanager;
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags);
- if (cookie == kInvalidCookie) {
+ auto value = theme->GetAttribute(static_cast<uint32_t>(resid));
+ if (!value.has_value()) {
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
}
- uint32_t ref = 0u;
- if (resolve_references) {
- ResTable_config selected_config;
- cookie =
- theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
- return ApkAssetsCookieToJavaCookie(kInvalidCookie);
- }
+ if (!resolve_references) {
+ return CopyValue(env, *value, typed_value);
}
- return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value);
+
+ auto result = theme->GetAssetManager()->ResolveReference(*value);
+ if (!result.has_value()) {
+ ThrowIfIOError(env, result);
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+ return CopyValue(env, *value, typed_value);
}
static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
@@ -1563,8 +1500,6 @@
{"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
// System/idmap related methods.
- {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;",
- (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
{"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
(void*)NativeGetOverlayableMap},
{"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
diff --git a/core/jni/android_util_AssetManager_private.h b/core/jni/android_util_AssetManager_private.h
new file mode 100644
index 0000000..153509b9
--- /dev/null
+++ b/core/jni/android_util_AssetManager_private.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 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 ANDROID_UTIL_ASSETMANAGER_PRIVATE_H
+#define ANDROID_UTIL_ASSETMANAGER_PRIVATE_H
+
+#include <optional>
+
+#include <androidfw/Errors.h>
+#include <android-base/expected.h>
+
+#include "core_jni_helpers.h"
+#include "jni.h"
+#include "nativehelper/JNIHelp.h"
+
+namespace android {
+
+constexpr const char* kResourcesNotFound = "android/content/res/Resources$NotFoundException";
+constexpr const static char* kIOErrorMessage = "failed to read resources.arsc data";
+
+template <typename T, typename E>
+static bool ThrowIfIOError(JNIEnv* env, const base::expected<T, E>& result) {
+ if constexpr (std::is_same<NullOrIOError, E>::value) {
+ if (IsIOError(result)) {
+ jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+ return true;
+ }
+ return false;
+ } else {
+ if (!result.has_value()) {
+ static_assert(std::is_same<IOError, E>::value, "Unknown result error type");
+ jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+ return true;
+ }
+ return false;
+ }
+}
+
+} // namespace android
+
+#endif //ANDROID_UTIL_ASSETMANAGER_PRIVATE_H
diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp
index 760f9e3..45f6b72 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "StringBlock"
+#include "android_util_AssetManager_private.h"
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include <utils/misc.h>
@@ -31,10 +32,8 @@
// ----------------------------------------------------------------------------
-static jlong android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz,
- jbyteArray bArray,
- jint off, jint len)
-{
+static jlong android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz, jbyteArray bArray,
+ jint off, jint len) {
if (bArray == NULL) {
jniThrowNullPointerException(env, NULL);
return 0;
@@ -59,9 +58,7 @@
return reinterpret_cast<jlong>(osb);
}
-static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz, jlong token) {
ResStringPool* osb = reinterpret_cast<ResStringPool*>(token);
if (osb == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -71,76 +68,84 @@
return osb->size();
}
-static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject clazz,
- jlong token, jint idx)
-{
+static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject clazz, jlong token,
+ jint idx) {
ResStringPool* osb = reinterpret_cast<ResStringPool*>(token);
if (osb == NULL) {
jniThrowNullPointerException(env, NULL);
return 0;
}
- size_t len;
- const char* str8 = osb->string8At(idx, &len);
- if (str8 != NULL) {
- return env->NewStringUTF(str8);
+ auto str8 = osb->string8At(idx);
+ if (UNLIKELY(ThrowIfIOError(env, str8))) {
+ return 0;
+ } else if (str8.has_value()) {
+ return env->NewStringUTF(str8->data());
}
- const char16_t* str = osb->stringAt(idx, &len);
- if (str == NULL) {
+ auto str = osb->stringAt(idx);
+ if (UNLIKELY(ThrowIfIOError(env, str))) {
+ return 0;
+ } else if (UNLIKELY(!str.has_value())) {
jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
return 0;
}
- return env->NewString((const jchar*)str, len);
+ return env->NewString((const jchar*)str->data(), str->size());
}
-static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject clazz,
- jlong token, jint idx)
-{
+static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject clazz, jlong token,
+ jint idx) {
ResStringPool* osb = reinterpret_cast<ResStringPool*>(token);
if (osb == NULL) {
jniThrowNullPointerException(env, NULL);
return NULL;
}
- const ResStringPool_span* spans = osb->styleAt(idx);
- if (spans == NULL) {
+ auto spans = osb->styleAt(idx);
+ if (!spans.has_value()) {
+ ThrowIfIOError(env, spans);
return NULL;
}
- const ResStringPool_span* pos = spans;
- int num = 0;
- while (pos->name.index != ResStringPool_span::END) {
- num++;
- pos++;
- }
+ jintArray array;
+ {
+ int num = 0;
+ auto pos = *spans;
+ while (true) {
+ if (UNLIKELY(!pos)) {
+ jniThrowException(env, kResourcesNotFound, kIOErrorMessage);
+ return NULL;
+ }
+ if (pos->name.index == ResStringPool_span::END) {
+ break;
+ }
+ num++;
+ pos++;
+ }
- if (num == 0) {
- return NULL;
- }
+ if (num == 0) {
+ return NULL;
+ }
- jintArray array = env->NewIntArray((num*sizeof(ResStringPool_span))/sizeof(jint));
- if (array == NULL) { // NewIntArray already threw OutOfMemoryError.
- return NULL;
+ array = env->NewIntArray((num * sizeof(ResStringPool_span)) / sizeof(jint));
+ if (array == NULL) { // NewIntArray already threw OutOfMemoryError.
+ return NULL;
+ }
}
-
- num = 0;
- static const int numInts = sizeof(ResStringPool_span)/sizeof(jint);
- while (spans->name.index != ResStringPool_span::END) {
- env->SetIntArrayRegion(array,
- num*numInts, numInts,
- (jint*)spans);
- spans++;
- num++;
+ {
+ int num = 0;
+ static const int numInts = sizeof(ResStringPool_span) / sizeof(jint);
+ while ((*spans)->name.index != ResStringPool_span::END) {
+ env->SetIntArrayRegion(array, num * numInts, numInts, (jint*)spans->unsafe_ptr());
+ (*spans)++;
+ num++;
+ }
}
-
return array;
}
-static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz, jlong token) {
ResStringPool* osb = reinterpret_cast<ResStringPool*>(token);
if (osb == NULL) {
jniThrowNullPointerException(env, NULL);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 96a57c1..a9fe5d5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1769,6 +1769,16 @@
<permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows system APK to update Wifi coex channels to avoid.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows applications to access Wifi coex channels being avoided.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS"
+ android:protectionLevel="signature|privileged" />
+
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
<!-- ======================================= -->
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 263d638..02cf0b7 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -6,6 +6,7 @@
hackbod@google.com
jsharkey@android.com
jsharkey@google.com
+juliacr@google.com
michaelwr@google.com
nandana@google.com
narayan@google.com
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 57f2d6a..d41868e 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -20,12 +20,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="byteShort" msgid="202579285008794431">"B"</string>
+ <string name="byteShort" msgid="202579285008794431">"ባ"</string>
<string name="kilobyteShort" msgid="2214285521564195803">"ኪባ"</string>
- <string name="megabyteShort" msgid="6649361267635823443">"MB"</string>
- <string name="gigabyteShort" msgid="7515809460261287991">"GB"</string>
- <string name="terabyteShort" msgid="1822367128583886496">"TB"</string>
- <string name="petabyteShort" msgid="5651571254228534832">"PB"</string>
+ <string name="megabyteShort" msgid="6649361267635823443">"ሜባ"</string>
+ <string name="gigabyteShort" msgid="7515809460261287991">"ጊባ"</string>
+ <string name="terabyteShort" msgid="1822367128583886496">"ቴባ"</string>
+ <string name="petabyteShort" msgid="5651571254228534832">"ፔባ"</string>
<string name="fileSizeSuffix" msgid="4233671691980131257">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="3381766946944136678">"<ርዕስ አልባ>"</string>
<string name="emptyPhoneNumber" msgid="5812172618020360048">"(ምንም ስልክ ቁጥር የለም)"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7b6dea3..13f1b56 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -823,7 +823,7 @@
<string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Introduce el código PIN para desbloquear."</string>
<string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"Código PIN incorrecto"</string>
<string name="keyguard_label_text" msgid="3841953694564168384">"Para desbloquear el teléfono, pulsa la tecla de menú y, a continuación, pulsa 0."</string>
- <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Llamada de emergencia"</string>
+ <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Número de emergencia"</string>
<string name="lockscreen_carrier_default" msgid="6192313772955399160">"Sin servicio"</string>
<string name="lockscreen_screen_locked" msgid="7364905540516041817">"Pantalla bloqueada"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pulsa la tecla de menú para desbloquear el teléfono o realizar una llamada de emergencia."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index fe283ab..03a1456 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1878,7 +1878,7 @@
<string name="language_selection_title" msgid="52674936078683285">"افزودن زبان"</string>
<string name="country_selection_title" msgid="5221495687299014379">"اولویتهای منطقه"</string>
<string name="search_language_hint" msgid="7004225294308793583">"نام زبان را تایپ کنید"</string>
- <string name="language_picker_section_suggested" msgid="6556199184638990447">"پیشنهادشده"</string>
+ <string name="language_picker_section_suggested" msgid="6556199184638990447">"پیشنهادی"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"همه زبانها"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"همه منطقهها"</string>
<string name="locale_search_menu" msgid="6258090710176422934">"جستجو"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index e8d6408..29fe136 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -52,7 +52,6 @@
<string name="enablePin" msgid="2543771964137091212">"Opération infructueuse. Activez le verrouillage SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="one">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM soit verrouillée.</item>
- <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
<item quantity="other">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM soit verrouillée.</item>
</plurals>
<string name="imei" msgid="2157082351232630390">"Code IIEM"</string>
@@ -180,7 +179,6 @@
<string name="low_memory" product="default" msgid="2539532364144025569">"La mémoire du téléphone est pleine. Veuillez supprimer des fichiers pour libérer de l\'espace."</string>
<plurals name="ssl_ca_cert_warning" formatted="false" msgid="2288194355006173029">
<item quantity="one">Autorité de certification installée</item>
- <item quantity="many">Certificate authorities installed</item>
<item quantity="other">Autorités de certification installées</item>
</plurals>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Par un tiers inconnu"</string>
@@ -253,7 +251,6 @@
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"Utilisez cette option pour qu\'il y ait le moins d\'interférences système possible lorsque votre appareil ne répond pas ou qu\'il est trop lent, ou lorsque vous avez besoin de toutes les sections du rapport de bogue. Aucune capture d\'écran supplémentaire ne peut être capturée, et vous ne pouvez entrer aucune autre information."</string>
<plurals name="bugreport_countdown" formatted="false" msgid="3906120379260059206">
<item quantity="one">Saisie d\'écran pour le rapport de bogue dans <xliff:g id="NUMBER_1">%d</xliff:g> seconde.</item>
- <item quantity="many">Taking screenshot for bug report in <xliff:g id="NUMBER_1">%d</xliff:g> seconds.</item>
<item quantity="other">Saisie d\'écran pour le rapport de bogue dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes.</item>
</plurals>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Capture d\'écran prise avec le rapport de bogue"</string>
@@ -997,7 +994,6 @@
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Il y a plus d\'un mois"</string>
<plurals name="last_num_days" formatted="false" msgid="687443109145393632">
<item quantity="one">Le dernier <xliff:g id="COUNT_1">%d</xliff:g> jour</item>
- <item quantity="many">Last <xliff:g id="COUNT_1">%d</xliff:g> days</item>
<item quantity="other">Le dernier <xliff:g id="COUNT_1">%d</xliff:g> jours</item>
</plurals>
<string name="last_month" msgid="1528906781083518683">"Le mois dernier"</string>
@@ -1020,82 +1016,66 @@
<string name="now_string_shortest" msgid="3684914126941650330">"maintenant"</string>
<plurals name="duration_minutes_shortest" formatted="false" msgid="7519574894537185135">
<item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> m</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g>m</item>
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> m</item>
</plurals>
<plurals name="duration_hours_shortest" formatted="false" msgid="2838655994500499651">
<item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> h</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g>h</item>
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> h</item>
</plurals>
<plurals name="duration_days_shortest" formatted="false" msgid="3686058472983158496">
<item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> j</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g>d</item>
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> j</item>
</plurals>
<plurals name="duration_years_shortest" formatted="false" msgid="8299112348723640338">
<item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> a</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g>y</item>
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> a</item>
</plurals>
<plurals name="duration_minutes_shortest_future" formatted="false" msgid="849196137176399440">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> m</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g>m</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> m</item>
</plurals>
<plurals name="duration_hours_shortest_future" formatted="false" msgid="5386373597343170388">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> h</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g>h</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> h</item>
</plurals>
<plurals name="duration_days_shortest_future" formatted="false" msgid="814754627092787227">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> j</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g>d</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> j</item>
</plurals>
<plurals name="duration_years_shortest_future" formatted="false" msgid="7683731800140202145">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> a</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g>y</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> a</item>
</plurals>
<plurals name="duration_minutes_relative" formatted="false" msgid="6569851308583028344">
<item quantity="one">il y a <xliff:g id="COUNT_1">%d</xliff:g> minute</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> minutes ago</item>
<item quantity="other">il y a <xliff:g id="COUNT_1">%d</xliff:g> minutes</item>
</plurals>
<plurals name="duration_hours_relative" formatted="false" msgid="420434788589102019">
<item quantity="one">il y a<xliff:g id="COUNT_1">%d</xliff:g> heure</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> hours ago</item>
<item quantity="other">il y a<xliff:g id="COUNT_1">%d</xliff:g> heures</item>
</plurals>
<plurals name="duration_days_relative" formatted="false" msgid="6056425878237482431">
<item quantity="one">il y a <xliff:g id="COUNT_1">%d</xliff:g> jour</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> days ago</item>
<item quantity="other">il y a <xliff:g id="COUNT_1">%d</xliff:g> jours</item>
</plurals>
<plurals name="duration_years_relative" formatted="false" msgid="2179998228861172159">
<item quantity="one">il y a <xliff:g id="COUNT_1">%d</xliff:g> an</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> years ago</item>
<item quantity="other">il y a <xliff:g id="COUNT_1">%d</xliff:g> ans</item>
</plurals>
<plurals name="duration_minutes_relative_future" formatted="false" msgid="5759885720917567723">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> minute</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g> minutes</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> minutes</item>
</plurals>
<plurals name="duration_hours_relative_future" formatted="false" msgid="8963511608507707959">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> heure</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g> hours</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> heures</item>
</plurals>
<plurals name="duration_days_relative_future" formatted="false" msgid="1964709470979250702">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> jour</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g> days</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> jours</item>
</plurals>
<plurals name="duration_years_relative_future" formatted="false" msgid="3985129025134896371">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> ans</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g> years</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> ans</item>
</plurals>
<string name="VideoView_error_title" msgid="5750686717225068016">"Problème vidéo"</string>
@@ -1466,7 +1446,6 @@
<string name="find_on_page" msgid="5400537367077438198">"Rechercher sur la page"</string>
<plurals name="matches_found" formatted="false" msgid="1101758718194295554">
<item quantity="one"><xliff:g id="INDEX">%d</xliff:g> sur <xliff:g id="TOTAL">%d</xliff:g></item>
- <item quantity="many"><xliff:g id="INDEX">%d</xliff:g> of <xliff:g id="TOTAL">%d</xliff:g></item>
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> sur <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="2536182504764803222">"Terminé"</string>
@@ -1603,7 +1582,6 @@
<string name="kg_wrong_pin" msgid="3680925703673166482">"NIP incorrect."</string>
<plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="236717428673283568">
<item quantity="one">Réessayer dans <xliff:g id="NUMBER">%d</xliff:g> seconde.</item>
- <item quantity="many">Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds.</item>
<item quantity="other">Réessayer dans <xliff:g id="NUMBER">%d</xliff:g> secondes.</item>
</plurals>
<string name="kg_pattern_instructions" msgid="8366024510502517748">"Dessinez votre schéma."</string>
@@ -1790,7 +1768,6 @@
<string name="restr_pin_error_too_short" msgid="1547007808237941065">"Le NIP est trop court. Il doit comporter au moins 4 chiffres."</string>
<plurals name="restr_pin_countdown" formatted="false" msgid="4427486903285216153">
<item quantity="one">Réessayer dans <xliff:g id="COUNT">%d</xliff:g> seconde</item>
- <item quantity="many">Try again in <xliff:g id="COUNT">%d</xliff:g> seconds</item>
<item quantity="other">Réessayer dans <xliff:g id="COUNT">%d</xliff:g> secondes</item>
</plurals>
<string name="restr_pin_try_later" msgid="5897719962541636727">"Réessayez plus tard"</string>
@@ -1822,42 +1799,34 @@
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273">
<item quantity="one">Pendant %1$d minute (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
- <item quantity="many">For %1$d minutes (until <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Pendant %1$d minutes (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
</plurals>
<plurals name="zen_mode_duration_minutes_summary_short" formatted="false" msgid="4230730310318858312">
<item quantity="one">Pendant %1$d min (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
- <item quantity="many">For %1$d min (until <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Pendant %1$d min (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
</plurals>
<plurals name="zen_mode_duration_hours_summary" formatted="false" msgid="7725354244196466758">
<item quantity="one">Pendant %1$d heure (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
- <item quantity="many">For %1$d hours (until <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Pendant %1$d heures (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
</plurals>
<plurals name="zen_mode_duration_hours_summary_short" formatted="false" msgid="588719069121765642">
<item quantity="one">Pendant %1$d h (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
- <item quantity="many">For %1$d hr (until <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Pendant %1$d h (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
</plurals>
<plurals name="zen_mode_duration_minutes" formatted="false" msgid="1148568456958944998">
<item quantity="one">Pendant %d minute</item>
- <item quantity="many">For %d minutes</item>
<item quantity="other">Pendant %d minutes</item>
</plurals>
<plurals name="zen_mode_duration_minutes_short" formatted="false" msgid="2742377799995454859">
<item quantity="one">Pendant %d min</item>
- <item quantity="many">For %d min</item>
<item quantity="other">Pendant %d min</item>
</plurals>
<plurals name="zen_mode_duration_hours" formatted="false" msgid="525401855645490022">
<item quantity="one">Pendant %d heure</item>
- <item quantity="many">For %d hours</item>
<item quantity="other">Pendant %d heures</item>
</plurals>
<plurals name="zen_mode_duration_hours_short" formatted="false" msgid="7644653189680911640">
<item quantity="one">Pendant %d h</item>
- <item quantity="many">For %d hr</item>
<item quantity="other">Pendant %d h</item>
</plurals>
<string name="zen_mode_until" msgid="2250286190237669079">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
@@ -1898,7 +1867,6 @@
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g> : <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> élément sélectionné</item>
- <item quantity="many"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> éléments sélectionnés</item>
</plurals>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sans catégorie"</string>
@@ -1966,7 +1934,6 @@
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Aucune suggestion de remplissage automatique"</string>
<plurals name="autofill_picker_some_suggestions" formatted="false" msgid="6651883186966959978">
<item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> suggestion de remplissage automatique</item>
- <item quantity="many"><xliff:g id="COUNT">%1$s</xliff:g> autofill suggestions</item>
<item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggestions de remplissage automatique</item>
</plurals>
<string name="autofill_save_title" msgid="7719802414283739775">"Enregistrer sous "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
@@ -2060,7 +2027,6 @@
<string name="car_loading_profile" msgid="8219978381196748070">"Chargement en cours…"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="one"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> fichier</item>
- <item quantity="many"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> files</item>
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> fichiers</item>
</plurals>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Aucune recommandation de personnes avec lesquelles effectuer un partage"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index fc91c47..a5b1626 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -52,7 +52,6 @@
<string name="enablePin" msgid="2543771964137091212">"Échec de l\'opération. Veuillez activer le verrouillage de la carte SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="one">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM ne soit verrouillée.</item>
- <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
<item quantity="other">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM ne soit verrouillée.</item>
</plurals>
<string name="imei" msgid="2157082351232630390">"Code IMEI"</string>
@@ -180,7 +179,6 @@
<string name="low_memory" product="default" msgid="2539532364144025569">"La mémoire du téléphone est pleine. Veuillez supprimer des fichiers pour libérer de l\'espace."</string>
<plurals name="ssl_ca_cert_warning" formatted="false" msgid="2288194355006173029">
<item quantity="one">Autorité de certification installée</item>
- <item quantity="many">Certificate authorities installed</item>
<item quantity="other">Autorités de certification installées</item>
</plurals>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Par un tiers inconnu"</string>
@@ -253,7 +251,6 @@
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"Utilisez cette option pour qu\'il y ait le moins d\'interférences système possible lorsque votre appareil ne répond pas ou qu\'il est trop lent, ou lorsque vous avez besoin de toutes les sections du rapport de bug. Aucune capture d\'écran supplémentaire ne peut être prise, et vous ne pouvez saisir aucune autre information."</string>
<plurals name="bugreport_countdown" formatted="false" msgid="3906120379260059206">
<item quantity="one">Capture d\'écran pour le rapport de bug dans <xliff:g id="NUMBER_1">%d</xliff:g> seconde</item>
- <item quantity="many">Taking screenshot for bug report in <xliff:g id="NUMBER_1">%d</xliff:g> seconds.</item>
<item quantity="other">Capture d\'écran pour le rapport de bug dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes</item>
</plurals>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Capture d\'écran avec rapport de bug effectuée"</string>
@@ -997,7 +994,6 @@
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Il y a plus d\'un mois"</string>
<plurals name="last_num_days" formatted="false" msgid="687443109145393632">
<item quantity="one">Le dernier jour (<xliff:g id="COUNT_1">%d</xliff:g>)</item>
- <item quantity="many">Last <xliff:g id="COUNT_1">%d</xliff:g> days</item>
<item quantity="other">Les <xliff:g id="COUNT_1">%d</xliff:g> derniers jours</item>
</plurals>
<string name="last_month" msgid="1528906781083518683">"Le mois dernier"</string>
@@ -1020,82 +1016,66 @@
<string name="now_string_shortest" msgid="3684914126941650330">"mainten."</string>
<plurals name="duration_minutes_shortest" formatted="false" msgid="7519574894537185135">
<item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> m</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g>m</item>
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> m</item>
</plurals>
<plurals name="duration_hours_shortest" formatted="false" msgid="2838655994500499651">
<item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> h</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g>h</item>
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> h</item>
</plurals>
<plurals name="duration_days_shortest" formatted="false" msgid="3686058472983158496">
<item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> j</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g>d</item>
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> j</item>
</plurals>
<plurals name="duration_years_shortest" formatted="false" msgid="8299112348723640338">
<item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> a</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g>y</item>
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> a</item>
</plurals>
<plurals name="duration_minutes_shortest_future" formatted="false" msgid="849196137176399440">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> m</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g>m</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> m</item>
</plurals>
<plurals name="duration_hours_shortest_future" formatted="false" msgid="5386373597343170388">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> h</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g>h</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> h</item>
</plurals>
<plurals name="duration_days_shortest_future" formatted="false" msgid="814754627092787227">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> j</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g>d</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> j</item>
</plurals>
<plurals name="duration_years_shortest_future" formatted="false" msgid="7683731800140202145">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> a</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g>y</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> a</item>
</plurals>
<plurals name="duration_minutes_relative" formatted="false" msgid="6569851308583028344">
<item quantity="one">il y a <xliff:g id="COUNT_1">%d</xliff:g> minute</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> minutes ago</item>
<item quantity="other">il y a <xliff:g id="COUNT_1">%d</xliff:g> minutes</item>
</plurals>
<plurals name="duration_hours_relative" formatted="false" msgid="420434788589102019">
<item quantity="one">il y a <xliff:g id="COUNT_1">%d</xliff:g> heure</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> hours ago</item>
<item quantity="other">il y a <xliff:g id="COUNT_1">%d</xliff:g> heures</item>
</plurals>
<plurals name="duration_days_relative" formatted="false" msgid="6056425878237482431">
<item quantity="one">il y a <xliff:g id="COUNT_1">%d</xliff:g> jour</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> days ago</item>
<item quantity="other">il y a <xliff:g id="COUNT_1">%d</xliff:g> jours</item>
</plurals>
<plurals name="duration_years_relative" formatted="false" msgid="2179998228861172159">
<item quantity="one">il y a <xliff:g id="COUNT_1">%d</xliff:g> an</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> years ago</item>
<item quantity="other">il y a <xliff:g id="COUNT_1">%d</xliff:g> ans</item>
</plurals>
<plurals name="duration_minutes_relative_future" formatted="false" msgid="5759885720917567723">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> minute</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g> minutes</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> minutes</item>
</plurals>
<plurals name="duration_hours_relative_future" formatted="false" msgid="8963511608507707959">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> heure</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g> hours</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> heures</item>
</plurals>
<plurals name="duration_days_relative_future" formatted="false" msgid="1964709470979250702">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> jour</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g> days</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> jours</item>
</plurals>
<plurals name="duration_years_relative_future" formatted="false" msgid="3985129025134896371">
<item quantity="one">dans <xliff:g id="COUNT_1">%d</xliff:g> an</item>
- <item quantity="many">in <xliff:g id="COUNT_1">%d</xliff:g> years</item>
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> ans</item>
</plurals>
<string name="VideoView_error_title" msgid="5750686717225068016">"Problème vidéo"</string>
@@ -1466,7 +1446,6 @@
<string name="find_on_page" msgid="5400537367077438198">"Rechercher sur la page"</string>
<plurals name="matches_found" formatted="false" msgid="1101758718194295554">
<item quantity="one"><xliff:g id="INDEX">%d</xliff:g> sur <xliff:g id="TOTAL">%d</xliff:g></item>
- <item quantity="many"><xliff:g id="INDEX">%d</xliff:g> of <xliff:g id="TOTAL">%d</xliff:g></item>
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> sur <xliff:g id="TOTAL">%d</xliff:g></item>
</plurals>
<string name="action_mode_done" msgid="2536182504764803222">"OK"</string>
@@ -1603,7 +1582,6 @@
<string name="kg_wrong_pin" msgid="3680925703673166482">"Code PIN incorrect."</string>
<plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="236717428673283568">
<item quantity="one">Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> seconde.</item>
- <item quantity="many">Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds.</item>
<item quantity="other">Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> secondes.</item>
</plurals>
<string name="kg_pattern_instructions" msgid="8366024510502517748">"Dessinez votre schéma."</string>
@@ -1790,7 +1768,6 @@
<string name="restr_pin_error_too_short" msgid="1547007808237941065">"Le code PIN est trop court. Il doit comporter au moins 4 chiffres."</string>
<plurals name="restr_pin_countdown" formatted="false" msgid="4427486903285216153">
<item quantity="one">Réessayer dans <xliff:g id="COUNT">%d</xliff:g> seconde</item>
- <item quantity="many">Try again in <xliff:g id="COUNT">%d</xliff:g> seconds</item>
<item quantity="other">Réessayer dans <xliff:g id="COUNT">%d</xliff:g> secondes</item>
</plurals>
<string name="restr_pin_try_later" msgid="5897719962541636727">"Veuillez réessayer ultérieurement."</string>
@@ -1822,42 +1799,34 @@
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273">
<item quantity="one">Pendant %1$d minute (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
- <item quantity="many">For %1$d minutes (until <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Pendant %1$d minutes (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
</plurals>
<plurals name="zen_mode_duration_minutes_summary_short" formatted="false" msgid="4230730310318858312">
<item quantity="one">Pendant %1$d min (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
- <item quantity="many">For %1$d min (until <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Pendant %1$d min (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
</plurals>
<plurals name="zen_mode_duration_hours_summary" formatted="false" msgid="7725354244196466758">
<item quantity="one">Pendant %1$d heure (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
- <item quantity="many">For %1$d hours (until <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Pendant %1$d heures (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
</plurals>
<plurals name="zen_mode_duration_hours_summary_short" formatted="false" msgid="588719069121765642">
<item quantity="one">Pendant %1$d h (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
- <item quantity="many">For %1$d hr (until <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Pendant %1$d h (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
</plurals>
<plurals name="zen_mode_duration_minutes" formatted="false" msgid="1148568456958944998">
<item quantity="one">Pendant %d minute</item>
- <item quantity="many">For %d minutes</item>
<item quantity="other">Pendant %d minutes</item>
</plurals>
<plurals name="zen_mode_duration_minutes_short" formatted="false" msgid="2742377799995454859">
<item quantity="one">Pendant %d min</item>
- <item quantity="many">For %d min</item>
<item quantity="other">Pendant %d min</item>
</plurals>
<plurals name="zen_mode_duration_hours" formatted="false" msgid="525401855645490022">
<item quantity="one">Pendant %d heure</item>
- <item quantity="many">For %d hours</item>
<item quantity="other">Pendant %d heures</item>
</plurals>
<plurals name="zen_mode_duration_hours_short" formatted="false" msgid="7644653189680911640">
<item quantity="one">Pendant %d h</item>
- <item quantity="many">For %d hr</item>
<item quantity="other">Pendant %d h</item>
</plurals>
<string name="zen_mode_until" msgid="2250286190237669079">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
@@ -1898,7 +1867,6 @@
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g> : <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> élément sélectionné</item>
- <item quantity="many"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> éléments sélectionnés</item>
</plurals>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sans catégorie"</string>
@@ -1966,7 +1934,6 @@
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Aucune suggestion de saisie automatique"</string>
<plurals name="autofill_picker_some_suggestions" formatted="false" msgid="6651883186966959978">
<item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> suggestion de saisie automatique</item>
- <item quantity="many"><xliff:g id="COUNT">%1$s</xliff:g> autofill suggestions</item>
<item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggestions de saisie automatique</item>
</plurals>
<string name="autofill_save_title" msgid="7719802414283739775">"Enregistrer dans "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ?"</string>
@@ -2060,7 +2027,6 @@
<string name="car_loading_profile" msgid="8219978381196748070">"Chargement…"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="one"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> fichier</item>
- <item quantity="many"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> files</item>
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> fichiers</item>
</plurals>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Aucune recommandation de personnes avec lesquelles effectuer un partage"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 95e1a48e1..054344e 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1441,7 +1441,7 @@
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tocca per uscire dall\'app di guida."</string>
<string name="back_button_label" msgid="4078224038025043387">"Indietro"</string>
<string name="next_button_label" msgid="6040209156399907780">"Avanti"</string>
- <string name="skip_button_label" msgid="3566599811326688389">"Ignora"</string>
+ <string name="skip_button_label" msgid="3566599811326688389">"Salta"</string>
<string name="no_matches" msgid="6472699895759164599">"Nessuna corrispondenza"</string>
<string name="find_on_page" msgid="5400537367077438198">"Trova nella pagina"</string>
<plurals name="matches_found" formatted="false" msgid="1101758718194295554">
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 6723e66..e2cf7f9 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1956,7 +1956,7 @@
<string name="autofill_save_type_debit_card" msgid="3169397504133097468">"डेबिट कार्ड"</string>
<string name="autofill_save_type_payment_card" msgid="6555012156728690856">"भुक्तानी कार्ड"</string>
<string name="autofill_save_type_generic_card" msgid="1019367283921448608">"कार्ड"</string>
- <string name="autofill_save_type_username" msgid="1018816929884640882">"प्रयोगकर्ताको नाम"</string>
+ <string name="autofill_save_type_username" msgid="1018816929884640882">"युजरनेम"</string>
<string name="autofill_save_type_email_address" msgid="1303262336895591924">"इमेल ठेगाना"</string>
<string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"शान्त रहनुहोस् र नजिकै आश्रयस्थल खोज्नुहोस्।"</string>
<string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"तटीय क्षेत्र र नदीछेउका ठाउँहरू छाडी उच्च सतहमा अवस्थित कुनै अझ सुरक्षित ठाउँमा जानुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index bae44fd..594ea61 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -954,7 +954,7 @@
<string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"Hiermee kan de app de geschiedenis lezen van alle URL\'s die in de systeemeigen browser zijn bezocht, en alle bookmarks in de systeemeigen browser. Let op: deze rechten kunnen niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."</string>
<string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"webbookmarks en -geschiedenis schrijven"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bookmarks die zijn opgeslagen op je tablet. Deze rechten kunnen niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."</string>
- <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"Hiermee kan de app de browsergeschiedenis of opgeslagen bookmarks bewerken op je Android TV-apparaat. Hierdoor kan de app mogelijk browsergegevens wissen of aanpassen. Opmerking: Dit recht kan niet worden afgedwongen door andere browsers of andere apps met internetmogelijkheden."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"Hiermee kan de app de browsergeschiedenis of opgeslagen bookmarks bewerken op je Android TV-apparaat. Hierdoor kan de app mogelijk browsegegevens wissen of aanpassen. Opmerking: Dit recht kan niet worden afgedwongen door andere browsers of andere apps met internetmogelijkheden."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bookmarks die zijn opgeslagen op je telefoon. Deze rechten kunnen niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."</string>
<string name="permlab_setAlarm" msgid="1158001610254173567">"een wekker instellen"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"Hiermee kan de app een wekker instellen in een geïnstalleerde wekker-app. Deze functie wordt door sommige wekker-apps niet geïmplementeerd."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index df5cdac..624d554 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -162,7 +162,7 @@
<string name="httpErrorAuth" msgid="469553140922938968">"Nepodarilo sa overiť totožnosť."</string>
<string name="httpErrorProxyAuth" msgid="7229662162030113406">"Overenie pomocou servera proxy bolo neúspešné."</string>
<string name="httpErrorConnect" msgid="3295081579893205617">"K serveru sa nepodarilo pripojiť."</string>
- <string name="httpErrorIO" msgid="3860318696166314490">"Nepodarilo sa nadviazať komunikáciu so serverom. Skúste to znova neskôr."</string>
+ <string name="httpErrorIO" msgid="3860318696166314490">"Nepodarilo sa nadviazať komunikáciu so serverom. Skúste to neskôr."</string>
<string name="httpErrorTimeout" msgid="7446272815190334204">"Časový limit pripojenia na server vypršal."</string>
<string name="httpErrorRedirectLoop" msgid="8455757777509512098">"Stránka obsahuje príliš veľa presmerovaní servera."</string>
<string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"Protokol nie je podporovaný."</string>
@@ -563,7 +563,7 @@
<string name="fingerprint_error_timeout" msgid="2946635815726054226">"Časový limit rozpoznania odtlačku prsta vypršal. Skúste to znova."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operácia týkajúca sa odtlačku prsta bola zrušená"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Overenie odtlačku prsta zrušil používateľ."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Príliš veľa pokusov. Skúste to znova neskôr."</string>
+ <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Príliš veľa pokusov. Skúste to neskôr."</string>
<string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Príliš veľa pokusov. Senzor odtlačkov prstov bol deaktivovaný."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Skúste to znova"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neregistrovali ste žiadne odtlačky prstov."</string>
@@ -607,7 +607,7 @@
<string name="face_error_no_space" msgid="5649264057026021723">"Nové údaje o tvári sa nedajú uložiť. Najprv odstráňte jeden zo starých záznamov."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Operácia týkajúca sa tváre bola zrušená"</string>
<string name="face_error_user_canceled" msgid="8553045452825849843">"Odomknutie tvárou zrušil používateľ."</string>
- <string name="face_error_lockout" msgid="7864408714994529437">"Príliš veľa pokusov. Skúste to znova neskôr."</string>
+ <string name="face_error_lockout" msgid="7864408714994529437">"Príliš veľa pokusov. Skúste to neskôr."</string>
<string name="face_error_lockout_permanent" msgid="8277853602168960343">"Príliš veľa pokusov. Odomknutie tvárou bolo zakázané."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Nedá sa overiť tvár. Skúste to znova."</string>
<string name="face_error_not_enrolled" msgid="7369928733504691611">"Nenastavili ste odomknutie tvárou."</string>
@@ -1816,7 +1816,7 @@
<item quantity="other">Skúste to znova o <xliff:g id="COUNT">%d</xliff:g> sekúnd</item>
<item quantity="one">Skúste to znova o 1 sekundu</item>
</plurals>
- <string name="restr_pin_try_later" msgid="5897719962541636727">"Skúste to znova neskôr"</string>
+ <string name="restr_pin_try_later" msgid="5897719962541636727">"Skúste to neskôr"</string>
<string name="immersive_cling_title" msgid="2307034298721541791">"Zobrazenie na celú obrazovku"</string>
<string name="immersive_cling_description" msgid="7092737175345204832">"Ukončíte potiahnutím zhora nadol."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Dobre"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index e8449c3..2b83c82 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1765,7 +1765,7 @@
<string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Yangi PIN kodni tasdiqlash"</string>
<string name="restr_pin_create_pin" msgid="917067613896366033">"Cheklovlarni o‘zgartirish uchun PIN-kod yaratish"</string>
<string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"PIN-kod mos kelmadi. Qayta urinib ko‘ring."</string>
- <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN kod kamida 4 ta raqamdan iborat bo‘lishi shart."</string>
+ <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN kod juda qisqa, kamida 4 ta raqam kiriting."</string>
<plurals name="restr_pin_countdown" formatted="false" msgid="4427486903285216153">
<item quantity="other"><xliff:g id="COUNT">%d</xliff:g> soniyadan so‘ng qayta urinib ko‘ring</item>
<item quantity="one">1 soniyadan so‘ng qayta urinib ko‘ring</item>
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index 9246a23..f9e3bc6 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -118,6 +118,7 @@
// Wifi bugreports should not receive any progress.
assertThat(callback.hasReceivedProgress()).isFalse();
assertThat(mBugreportFile.length()).isGreaterThan(0L);
+ assertThat(callback.hasEarlyReportFinished()).isTrue();
assertFdsAreClosed(mBugreportFd);
}
@@ -135,6 +136,7 @@
// Interactive bugreports show progress updates.
assertThat(callback.hasReceivedProgress()).isTrue();
assertThat(mBugreportFile.length()).isGreaterThan(0L);
+ assertThat(callback.hasEarlyReportFinished()).isTrue();
assertFdsAreClosed(mBugreportFd);
}
@@ -246,6 +248,7 @@
private int mErrorCode = -1;
private boolean mSuccess = false;
private boolean mReceivedProgress = false;
+ private boolean mEarlyReportFinished = false;
private final Object mLock = new Object();
@Override
@@ -271,6 +274,13 @@
}
}
+ @Override
+ public void onEarlyReportFinished() {
+ synchronized (mLock) {
+ mEarlyReportFinished = true;
+ }
+ }
+
/* Indicates completion; and ended up with a success or error. */
public boolean isDone() {
synchronized (mLock) {
@@ -295,6 +305,12 @@
return mReceivedProgress;
}
}
+
+ public boolean hasEarlyReportFinished() {
+ synchronized (mLock) {
+ return mEarlyReportFinished;
+ }
+ }
}
public static BugreportManager getBugreportManager() {
diff --git a/core/tests/coretests/BstatsTestApp/OWNERS b/core/tests/coretests/BstatsTestApp/OWNERS
new file mode 100644
index 0000000..4068e2b
--- /dev/null
+++ b/core/tests/coretests/BstatsTestApp/OWNERS
@@ -0,0 +1 @@
+include /BATTERY_STATS_OWNERS
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
new file mode 100644
index 0000000..bd7da0c
--- /dev/null
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -0,0 +1 @@
+per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/app/activity/OWNERS b/core/tests/coretests/src/android/app/activity/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/app/servertransaction/OWNERS b/core/tests/coretests/src/android/app/servertransaction/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/app/time/OWNERS b/core/tests/coretests/src/android/app/time/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/app/timedetector/GnssTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/GnssTimeSuggestionTest.java
new file mode 100644
index 0000000..e248010
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timedetector/GnssTimeSuggestionTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timedetector;
+
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.TimestampedValue;
+
+import org.junit.Test;
+
+public class GnssTimeSuggestionTest {
+
+ private static final TimestampedValue<Long> ARBITRARY_TIME =
+ new TimestampedValue<>(1111L, 2222L);
+
+ @Test
+ public void testEquals() {
+ GnssTimeSuggestion one = new GnssTimeSuggestion(ARBITRARY_TIME);
+ assertEquals(one, one);
+
+ GnssTimeSuggestion two = new GnssTimeSuggestion(ARBITRARY_TIME);
+ assertEquals(one, two);
+ assertEquals(two, one);
+
+ TimestampedValue<Long> differentTime = new TimestampedValue<>(
+ ARBITRARY_TIME.getReferenceTimeMillis() + 1,
+ ARBITRARY_TIME.getValue());
+ GnssTimeSuggestion three = new GnssTimeSuggestion(differentTime);
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+
+ // DebugInfo must not be considered in equals().
+ one.addDebugInfo("Debug info 1");
+ two.addDebugInfo("Debug info 2");
+ assertEquals(one, two);
+ }
+
+ @Test
+ public void testParcelable() {
+ GnssTimeSuggestion suggestion = new GnssTimeSuggestion(ARBITRARY_TIME);
+ assertRoundTripParcelable(suggestion);
+
+ // DebugInfo should also be stored (but is not checked by equals()
+ suggestion.addDebugInfo("This is debug info");
+ GnssTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
+ assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/timedetector/OWNERS b/core/tests/coretests/src/android/app/timedetector/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timedetector/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/app/timezone/OWNERS b/core/tests/coretests/src/android/app/timezone/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezone/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/app/timezonedetector/OWNERS b/core/tests/coretests/src/android/app/timezonedetector/OWNERS
new file mode 100644
index 0000000..8f80897
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezonedetector/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
new file mode 100644
index 0000000..911efb2
--- /dev/null
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -0,0 +1 @@
+per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/os/storage/OWNERS b/core/tests/coretests/src/android/os/storage/OWNERS
new file mode 100644
index 0000000..6f9dbea
--- /dev/null
+++ b/core/tests/coretests/src/android/os/storage/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/os/storage/OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/os/OWNERS b/core/tests/coretests/src/com/android/internal/os/OWNERS
new file mode 100644
index 0000000..4068e2b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/OWNERS
@@ -0,0 +1 @@
+include /BATTERY_STATS_OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
index d026735..e8793a9 100644
--- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -118,4 +118,89 @@
assertEquals(3, ArrayUtils.unstableRemoveIf(collection, isNull));
assertEquals(0, collection.size());
}
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenRangeInsideArray() {
+ ArrayUtils.throwsIfOutOfBounds(10, 2, 6);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenRangeIsWholeArray() {
+ ArrayUtils.throwsIfOutOfBounds(10, 0, 10);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtStart() {
+ ArrayUtils.throwsIfOutOfBounds(10, 0, 0);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtEnd() {
+ ArrayUtils.throwsIfOutOfBounds(10, 10, 0);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenEmptyArray() {
+ ArrayUtils.throwsIfOutOfBounds(0, 0, 0);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenRangeStartNegative() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, -1, 5);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenCountNegative() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, 5, -1);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenRangeStartTooHigh() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, 11, 0);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenRangeEndTooHigh() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, 5, 6);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenLengthNegative() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(-1, 0, 0);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenOverflowRangeEndTooHigh() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
}
diff --git a/core/tests/mockingcoretests/src/android/app/activity/OWNERS b/core/tests/mockingcoretests/src/android/app/activity/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/app/activity/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/uwbtests/OWNERS b/core/tests/uwbtests/OWNERS
new file mode 100644
index 0000000..c7b09a2
--- /dev/null
+++ b/core/tests/uwbtests/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/uwb/OWNERS
diff --git a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java
index 077b08f..e0884e3 100644
--- a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java
+++ b/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java
@@ -48,8 +48,8 @@
builder.setAzimuthAngleMeasurement(azimuth);
AngleOfArrivalMeasurement measurement = tryBuild(builder, true);
- assertEquals(azimuth, measurement.getAzimuth());
- assertEquals(altitude, measurement.getAltitude());
+ assertEquals(azimuth, measurement.getAzimuthAngleMeasurement());
+ assertEquals(altitude, measurement.getAltitudeAngleMeasurement());
}
private AngleMeasurement getAngleMeasurement(double radian, double error, double confidence) {
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
new file mode 100644
index 0000000..6df1c3e
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Test of {@link AdapterStateListener}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RangingManagerTest {
+
+ private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class);
+ private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
+ private static final PersistableBundle PARAMS = new PersistableBundle();
+ private static final @CloseReason int CLOSE_REASON = CloseReason.UNKNOWN;
+
+ @Test
+ public void testOpenSession_StartRangingInvoked() throws RemoteException {
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback);
+ verify(ADAPTER, times(1)).startRanging(eq(rangingManager), eq(PARAMS));
+ }
+
+ @Test
+ public void testOpenSession_ErrorIfSameSessionHandleReturned() throws RemoteException {
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ SessionHandle handle = new SessionHandle(1);
+ when(ADAPTER.startRanging(any(), any())).thenReturn(handle);
+
+ rangingManager.openSession(PARAMS, EXECUTOR, callback);
+
+ // Calling openSession will cause the same session handle to be returned. The onClosed
+ // callback should be invoked
+ RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+ verify(callback, times(0)).onClosed(anyInt(), any());
+ verify(callback2, times(1)).onClosed(anyInt(), any());
+ }
+
+ @Test
+ public void testOnRangingStarted_ValidSessionHandle() throws RemoteException {
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ SessionHandle handle = new SessionHandle(1);
+ when(ADAPTER.startRanging(any(), any())).thenReturn(handle);
+
+ rangingManager.openSession(PARAMS, EXECUTOR, callback);
+ rangingManager.onRangingStarted(handle, PARAMS);
+ verify(callback, times(1)).onOpenSuccess(any(), any());
+ }
+
+ @Test
+ public void testOnRangingStarted_InvalidSessionHandle() throws RemoteException {
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+
+ rangingManager.onRangingStarted(new SessionHandle(2), PARAMS);
+ verify(callback, times(0)).onOpenSuccess(any(), any());
+ }
+
+ @Test
+ public void testOnRangingStarted_MultipleSessionsRegistered() throws RemoteException {
+ SessionHandle sessionHandle1 = new SessionHandle(1);
+ SessionHandle sessionHandle2 = new SessionHandle(2);
+ RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
+ RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
+
+ when(ADAPTER.startRanging(any(), any()))
+ .thenReturn(sessionHandle1)
+ .thenReturn(sessionHandle2);
+
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+
+ rangingManager.onRangingStarted(sessionHandle1, PARAMS);
+ verify(callback1, times(1)).onOpenSuccess(any(), any());
+ verify(callback2, times(0)).onOpenSuccess(any(), any());
+
+ rangingManager.onRangingStarted(sessionHandle2, PARAMS);
+ verify(callback1, times(1)).onOpenSuccess(any(), any());
+ verify(callback2, times(1)).onOpenSuccess(any(), any());
+ }
+
+ @Test
+ public void testOnRangingClosed_OnRangingClosedCalled() throws RemoteException {
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ SessionHandle handle = new SessionHandle(1);
+ when(ADAPTER.startRanging(any(), any())).thenReturn(handle);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback);
+
+ rangingManager.onRangingClosed(handle, CLOSE_REASON, PARAMS);
+ verify(callback, times(1)).onClosed(anyInt(), any());
+ }
+
+ @Test
+ public void testOnRangingClosed_MultipleSessionsRegistered() throws RemoteException {
+ // Verify that if multiple sessions are registered, only the session that is
+ // requested to close receives the associated callbacks
+ SessionHandle sessionHandle1 = new SessionHandle(1);
+ SessionHandle sessionHandle2 = new SessionHandle(2);
+ RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
+ RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
+
+ when(ADAPTER.startRanging(any(), any()))
+ .thenReturn(sessionHandle1)
+ .thenReturn(sessionHandle2);
+
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+
+ rangingManager.onRangingClosed(sessionHandle1, CLOSE_REASON, PARAMS);
+ verify(callback1, times(1)).onClosed(anyInt(), any());
+ verify(callback2, times(0)).onClosed(anyInt(), any());
+
+ rangingManager.onRangingClosed(sessionHandle2, CLOSE_REASON, PARAMS);
+ verify(callback1, times(1)).onClosed(anyInt(), any());
+ verify(callback2, times(1)).onClosed(anyInt(), any());
+ }
+
+ @Test
+ public void testOnRangingReport_OnReportReceived() throws RemoteException {
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ SessionHandle handle = new SessionHandle(1);
+ when(ADAPTER.startRanging(any(), any())).thenReturn(handle);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback);
+ rangingManager.onRangingStarted(handle, PARAMS);
+
+ RangingReport report = UwbTestUtils.getRangingReports(1);
+ rangingManager.onRangingResult(handle, report);
+ verify(callback, times(1)).onReportReceived(eq(report));
+ }
+
+ @Test
+ public void testOnRangingReport_MultipleSessionsRegistered() throws RemoteException {
+ SessionHandle sessionHandle1 = new SessionHandle(1);
+ SessionHandle sessionHandle2 = new SessionHandle(2);
+ RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
+ RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
+
+ when(ADAPTER.startRanging(any(), any()))
+ .thenReturn(sessionHandle1)
+ .thenReturn(sessionHandle2);
+
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+ rangingManager.onRangingStarted(sessionHandle1, PARAMS);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+ rangingManager.onRangingStarted(sessionHandle2, PARAMS);
+
+ rangingManager.onRangingResult(sessionHandle1, UwbTestUtils.getRangingReports(1));
+ verify(callback1, times(1)).onReportReceived(any());
+ verify(callback2, times(0)).onReportReceived(any());
+
+ rangingManager.onRangingResult(sessionHandle2, UwbTestUtils.getRangingReports(1));
+ verify(callback1, times(1)).onReportReceived(any());
+ verify(callback2, times(1)).onReportReceived(any());
+ }
+
+ @Test
+ public void testOnClose_Reasons() throws RemoteException {
+ runOnClose_Reason(CloseReason.LOCAL_API,
+ RangingSession.Callback.CLOSE_REASON_LOCAL_CLOSE_API);
+
+ runOnClose_Reason(CloseReason.MAX_SESSIONS_REACHED,
+ RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED);
+
+ runOnClose_Reason(CloseReason.PROTOCOL_SPECIFIC,
+ RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC);
+
+ runOnClose_Reason(CloseReason.REMOTE_REQUEST,
+ RangingSession.Callback.CLOSE_REASON_REMOTE_REQUEST);
+
+ runOnClose_Reason(CloseReason.SYSTEM_POLICY,
+ RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY);
+
+ runOnClose_Reason(CloseReason.UNKNOWN,
+ RangingSession.Callback.CLOSE_REASON_UNKNOWN);
+ }
+
+ private void runOnClose_Reason(@CloseReason int reasonIn,
+ @RangingSession.Callback.CloseReason int reasonOut) throws RemoteException {
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ SessionHandle handle = new SessionHandle(1);
+ when(ADAPTER.startRanging(any(), any())).thenReturn(handle);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback);
+
+ rangingManager.onRangingClosed(handle, reasonIn, PARAMS);
+ verify(callback, times(1)).onClosed(eq(reasonOut), eq(PARAMS));
+ }
+
+ @Test
+ public void testStartFailureReasons() throws RemoteException {
+ runOnRangingStartFailed_Reason(StartFailureReason.BAD_PARAMETERS,
+ RangingSession.Callback.CLOSE_REASON_LOCAL_BAD_PARAMETERS);
+
+ runOnRangingStartFailed_Reason(StartFailureReason.MAX_SESSIONS_REACHED,
+ RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED);
+
+ runOnRangingStartFailed_Reason(StartFailureReason.PROTOCOL_SPECIFIC,
+ RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC);
+
+ runOnRangingStartFailed_Reason(StartFailureReason.SYSTEM_POLICY,
+ RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY);
+
+ runOnRangingStartFailed_Reason(StartFailureReason.UNKNOWN,
+ RangingSession.Callback.CLOSE_REASON_UNKNOWN);
+ }
+
+ private void runOnRangingStartFailed_Reason(@StartFailureReason int reasonIn,
+ @RangingSession.Callback.CloseReason int reasonOut) throws RemoteException {
+ RangingManager rangingManager = new RangingManager(ADAPTER);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ SessionHandle handle = new SessionHandle(1);
+ when(ADAPTER.startRanging(any(), any())).thenReturn(handle);
+ rangingManager.openSession(PARAMS, EXECUTOR, callback);
+
+ rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS);
+ verify(callback, times(1)).onClosed(eq(reasonOut), eq(PARAMS));
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java
index a7559d8..edd4d08 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java
@@ -63,8 +63,8 @@
assertEquals(status, measurement.getStatus());
assertEquals(address, measurement.getRemoteDeviceAddress());
assertEquals(time, measurement.getElapsedRealtimeNanos());
- assertEquals(angleMeasurement, measurement.getAngleOfArrival());
- assertEquals(distanceMeasurement, measurement.getDistance());
+ assertEquals(angleMeasurement, measurement.getAngleOfArrivalMeasurement());
+ assertEquals(distanceMeasurement, measurement.getDistanceMeasurement());
}
private RangingMeasurement tryBuild(RangingMeasurement.Builder builder,
diff --git a/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java b/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java
new file mode 100644
index 0000000..702c68e
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Test of {@link RangingSession}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RangingSessionTest {
+ private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class);
+ private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
+ private static final PersistableBundle PARAMS = new PersistableBundle();
+ private static final @RangingSession.Callback.CloseReason int CLOSE_REASON =
+ RangingSession.Callback.CLOSE_REASON_LOCAL_GENERIC_ERROR;
+
+ @Test
+ public void testOnRangingStarted_OnOpenSuccessCalled() {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ verifyOpenState(session, false);
+
+ session.onRangingStarted(PARAMS);
+ verifyOpenState(session, true);
+
+ // Verify that the onOpenSuccess callback was invoked
+ verify(callback, times(1)).onOpenSuccess(eq(session), any());
+ verify(callback, times(0)).onClosed(anyInt(), any());
+ }
+
+ @Test
+ public void testOnRangingStarted_CannotOpenClosedSession() {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+
+ session.onRangingStarted(PARAMS);
+ verifyOpenState(session, true);
+ verify(callback, times(1)).onOpenSuccess(eq(session), any());
+ verify(callback, times(0)).onClosed(anyInt(), any());
+
+ session.onRangingClosed(CLOSE_REASON, PARAMS);
+ verifyOpenState(session, false);
+ verify(callback, times(1)).onOpenSuccess(eq(session), any());
+ verify(callback, times(1)).onClosed(anyInt(), any());
+
+ // Now invoke the ranging started callback and ensure the session remains closed
+ session.onRangingStarted(PARAMS);
+ verifyOpenState(session, false);
+ verify(callback, times(1)).onOpenSuccess(eq(session), any());
+ verify(callback, times(1)).onClosed(anyInt(), any());
+ }
+
+ @Test
+ public void testOnRangingClosed_OnClosedCalledWhenSessionNotOpen() {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ verifyOpenState(session, false);
+
+ session.onRangingClosed(CLOSE_REASON, PARAMS);
+ verifyOpenState(session, false);
+
+ // Verify that the onOpenSuccess callback was invoked
+ verify(callback, times(0)).onOpenSuccess(eq(session), any());
+ verify(callback, times(1)).onClosed(anyInt(), any());
+ }
+
+ @Test public void testOnRangingClosed_OnClosedCalled() {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ session.onRangingStarted(PARAMS);
+ session.onRangingClosed(CLOSE_REASON, PARAMS);
+ verify(callback, times(1)).onClosed(anyInt(), any());
+
+ verifyOpenState(session, false);
+ session.onRangingClosed(CLOSE_REASON, PARAMS);
+ verify(callback, times(2)).onClosed(anyInt(), any());
+ }
+
+ @Test
+ public void testOnRangingResult_OnReportReceivedCalled() {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ verifyOpenState(session, false);
+
+ session.onRangingStarted(PARAMS);
+ verifyOpenState(session, true);
+
+ RangingReport report = UwbTestUtils.getRangingReports(1);
+ session.onRangingResult(report);
+ verify(callback, times(1)).onReportReceived(eq(report));
+ }
+
+ @Test
+ public void testClose() throws RemoteException {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ session.onRangingStarted(PARAMS);
+
+ // Calling close multiple times should invoke closeRanging until the session receives
+ // the onClosed callback.
+ int totalCallsBeforeOnRangingClosed = 3;
+ for (int i = 1; i <= totalCallsBeforeOnRangingClosed; i++) {
+ session.close();
+ verifyOpenState(session, true);
+ verify(ADAPTER, times(i)).closeRanging(handle);
+ verify(callback, times(0)).onClosed(anyInt(), any());
+ }
+
+ // After onClosed is invoked, then the adapter should no longer be called for each call to
+ // the session's close.
+ final int totalCallsAfterOnRangingClosed = 2;
+ for (int i = 1; i <= totalCallsAfterOnRangingClosed; i++) {
+ session.onRangingClosed(CLOSE_REASON, PARAMS);
+ verifyOpenState(session, false);
+ verify(ADAPTER, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle);
+ verify(callback, times(i)).onClosed(anyInt(), any());
+ }
+ }
+
+ @Test
+ public void testOnRangingResult_OnReportReceivedCalledWhenOpen() {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+
+ assertFalse(session.isOpen());
+ session.onRangingStarted(PARAMS);
+ assertTrue(session.isOpen());
+
+ // Verify that the onReportReceived callback was invoked
+ RangingReport report = UwbTestUtils.getRangingReports(1);
+ session.onRangingResult(report);
+ verify(callback, times(1)).onReportReceived(report);
+ }
+
+ @Test
+ public void testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen() {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+
+ assertFalse(session.isOpen());
+
+ // Verify that the onReportReceived callback was invoked
+ RangingReport report = UwbTestUtils.getRangingReports(1);
+ session.onRangingResult(report);
+ verify(callback, times(0)).onReportReceived(report);
+ }
+
+ private void verifyOpenState(RangingSession session, boolean expected) {
+ assertEquals(expected, session.isOpen());
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
index fb75092..b4b2e30 100644
--- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
+++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
@@ -22,6 +22,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
public class UwbTestUtils {
private UwbTestUtils() {}
@@ -96,4 +97,13 @@
}
return UwbAddress.fromBytes(addressBytes);
}
+
+ public static Executor getExecutor() {
+ return new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+ };
+ }
}
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index e549271..12bd0f5 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -158,6 +158,13 @@
}
prebuilt_etc {
+ name: "privapp_allowlist_com.google.android.car.networking.preferenceupdater",
+ sub_dir: "permissions",
+ src: "com.google.android.car.networking.preferenceupdater.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_com.android.car.ui.paintbooth",
sub_dir: "permissions",
src: "com.android.car.ui.paintbooth.xml",
diff --git a/data/etc/car/com.google.android.car.networking.preferenceupdater.xml b/data/etc/car/com.google.android.car.networking.preferenceupdater.xml
new file mode 100644
index 0000000..489ce1b
--- /dev/null
+++ b/data/etc/car/com.google.android.car.networking.preferenceupdater.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.google.android.car.networking.preferenceupdater">
+ <permission name="android.permission.ACCESS_NETWORK_STATE"/>
+ <permission name="android.permission.ACCESS_WIFI_STATE"/>
+ <permission name="android.permission.ACTIVITY_EMBEDDING"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+ <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+ <permission name="android.permission.LOCATION_HARDWARE"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 6555fe9..057c012 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -436,6 +436,8 @@
<permission name="android.permission.MANAGE_DEBUGGING" />
<!-- Permissions required for CTS test - TimeManagerTest -->
<permission name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
+ <!-- Permissions required for CTS test - CtsHdmiCecHostTestCases -->
+ <permission name="android.permission.HDMI_CEC"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
index 68cbd29..c7ce8cd 100644
--- a/data/keyboards/keyboards.mk
+++ b/data/keyboards/keyboards.mk
@@ -14,13 +14,9 @@
# Warning: this is actually a product definition, to be inherited from
-include $(LOCAL_PATH)/common.mk
+PRODUCT_COPY_FILES := \
+ $(call find-copy-subdir-files,*.kl,$(LOCAL_PATH),system/usr/keylayout) \
+ $(call find-copy-subdir-files,*.kcm,$(LOCAL_PATH),system/usr/keychars) \
+ $(call find-copy-subdir-files,*.idc,$(LOCAL_PATH),system/usr/idc)
-PRODUCT_COPY_FILES := $(foreach file,$(framework_keylayouts),\
- $(file):system/usr/keylayout/$(notdir $(file)))
-PRODUCT_COPY_FILES += $(foreach file,$(framework_keycharmaps),\
- $(file):system/usr/keychars/$(notdir $(file)))
-
-PRODUCT_COPY_FILES += $(foreach file,$(framework_keyconfigs),\
- $(file):system/usr/idc/$(notdir $(file)))
diff --git a/drm/java/android/drm/DrmOutputStream.java b/drm/java/android/drm/DrmOutputStream.java
index 73e7f23..2a3f530 100644
--- a/drm/java/android/drm/DrmOutputStream.java
+++ b/drm/java/android/drm/DrmOutputStream.java
@@ -25,9 +25,10 @@
import android.system.Os;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+
import libcore.io.IoBridge;
import libcore.io.Streams;
-import libcore.util.ArrayUtils;
import java.io.FileDescriptor;
import java.io.FilterOutputStream;
diff --git a/graphics/java/android/graphics/OWNERS b/graphics/java/android/graphics/OWNERS
index c3fb6f8..9fa8f1b 100644
--- a/graphics/java/android/graphics/OWNERS
+++ b/graphics/java/android/graphics/OWNERS
@@ -4,5 +4,10 @@
jreck@google.com
njawad@google.com
sumir@google.com
+djsollen@google.com
+scroggo@google.com
per-file BLASTBufferQueue.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file FontFamily.java = file:fonts/OWNERS
+per-file FontListParser.java = file:fonts/OWNERS
+per-file Typeface.java = file:fonts/OWNERS
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index 49a4887..7ea9e14 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -17,7 +17,7 @@
package android.security;
import android.annotation.NonNull;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.security.keymaster.KeymasterDefs;
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 7c3de8b..3ef4aa5 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.app.compat.CompatChanges;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.security.keystore.BackendBusyException;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
index 69c7a25..56f7ea8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
@@ -17,7 +17,7 @@
package android.security.keystore2;
import android.annotation.NonNull;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyProperties;
@@ -41,7 +41,7 @@
*
* @hide
*/
-public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
+public abstract class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
private static final int BLOCK_SIZE_BYTES = 8;
@@ -73,12 +73,22 @@
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
+
+ @Override
+ protected final String getTransform() {
+ return "DESede/ECB/NoPadding";
+ }
}
public static class PKCS7Padding extends ECB {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
+
+ @Override
+ protected final String getTransform() {
+ return "DESede/ECB/PKCS7Padding";
+ }
}
}
@@ -91,12 +101,23 @@
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
+
+ @Override
+ protected final String getTransform() {
+ return "DESede/CBC/NoPadding";
+ }
+
}
public static class PKCS7Padding extends CBC {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
+
+ @Override
+ protected final String getTransform() {
+ return "DESede/CBC/PKCS7Padding";
+ }
}
}
@@ -288,7 +309,7 @@
if (parameters != null) {
for (KeyParameter p : parameters) {
if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
- returnedIv = p.blob;
+ returnedIv = p.value.getBlob();
break;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index 2b5f6c3..64da837 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
@@ -64,6 +64,11 @@
}
@Override
+ protected final String getTransform() {
+ return "AES/GCM/NoPadding";
+ }
+
+ @Override
protected final void resetAll() {
mTagLengthBits = DEFAULT_TAG_LENGTH_BITS;
super.resetAll();
@@ -325,7 +330,7 @@
if (parameters != null) {
for (KeyParameter p : parameters) {
if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
- returnedIv = p.blob;
+ returnedIv = p.value.getBlob();
break;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java
index dd943d4..9ad6f3a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java
@@ -254,13 +254,13 @@
private void putAsymmetricCipherImpl(String transformation, String implClass) {
put("Cipher." + transformation, implClass);
put("Cipher." + transformation + " SupportedKeyClasses",
- KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+ KEYSTORE_PRIVATE_KEY_CLASS_NAME);
}
private void putSignatureImpl(String algorithm, String implClass) {
put("Signature." + algorithm, implClass);
put("Signature." + algorithm + " SupportedKeyClasses",
- KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+ KEYSTORE_PRIVATE_KEY_CLASS_NAME);
}
public static String[] getSupportedEcdsaSignatureDigests() {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index 18d2692..2ee952c 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -19,7 +19,7 @@
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
@@ -43,6 +43,7 @@
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
+import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
@@ -57,6 +58,8 @@
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.ShortBufferException;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
/**
@@ -99,6 +102,8 @@
*/
private Exception mCachedException;
+ private Cipher mCipher;
+
AndroidKeyStoreCipherSpiBase() {
mOperation = null;
mEncrypting = false;
@@ -110,6 +115,7 @@
mAdditionalAuthenticationDataStreamer = null;
mAdditionalAuthenticationDataStreamerClosed = false;
mCachedException = null;
+ mCipher = null;
}
@Override
@@ -117,6 +123,45 @@
throws InvalidKeyException {
resetAll();
+ if (!(key instanceof AndroidKeyStorePrivateKey
+ || key instanceof AndroidKeyStoreSecretKey)) {
+ try {
+ mCipher = Cipher.getInstance(getTransform());
+ String transform = getTransform();
+
+ if ("RSA/ECB/OAEPWithSHA-224AndMGF1Padding".equals(transform)) {
+ OAEPParameterSpec spec =
+ new OAEPParameterSpec("SHA-224", "MGF1",
+ new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ mCipher.init(opmode, key, spec, random);
+ } else if ("RSA/ECB/OAEPWithSHA-256AndMGF1Padding".equals(transform)) {
+ OAEPParameterSpec spec =
+ new OAEPParameterSpec("SHA-256", "MGF1",
+ new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ mCipher.init(opmode, key, spec, random);
+
+ } else if ("RSA/ECB/OAEPWithSHA-384AndMGF1Padding".equals(transform)) {
+ OAEPParameterSpec spec =
+ new OAEPParameterSpec("SHA-384", "MGF1",
+ new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ mCipher.init(opmode, key, spec, random);
+
+ } else if ("RSA/ECB/OAEPWithSHA-512AndMGF1Padding".equals(transform)) {
+ OAEPParameterSpec spec =
+ new OAEPParameterSpec("SHA-512", "MGF1",
+ new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ mCipher.init(opmode, key, spec, random);
+ } else {
+ mCipher.init(opmode, key, random);
+ }
+ return;
+ } catch (NoSuchAlgorithmException
+ | NoSuchPaddingException
+ | InvalidAlgorithmParameterException e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
boolean success = false;
try {
init(opmode, key, random);
@@ -139,6 +184,17 @@
SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
resetAll();
+ if (!(key instanceof AndroidKeyStorePrivateKey
+ || key instanceof AndroidKeyStoreSecretKey)) {
+ try {
+ mCipher = Cipher.getInstance(getTransform());
+ mCipher.init(opmode, key, params, random);
+ return;
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
boolean success = false;
try {
init(opmode, key, random);
@@ -157,6 +213,17 @@
SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
resetAll();
+ if (!(key instanceof AndroidKeyStorePrivateKey
+ || key instanceof AndroidKeyStoreSecretKey)) {
+ try {
+ mCipher = Cipher.getInstance(getTransform());
+ mCipher.init(opmode, key, params, random);
+ return;
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
boolean success = false;
try {
init(opmode, key, random);
@@ -214,6 +281,7 @@
mAdditionalAuthenticationDataStreamer = null;
mAdditionalAuthenticationDataStreamerClosed = false;
mCachedException = null;
+ mCipher = null;
}
/**
@@ -320,6 +388,10 @@
@Override
protected final byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+ if (mCipher != null) {
+ return mCipher.update(input, inputOffset, inputLen);
+ }
+
if (mCachedException != null) {
return null;
}
@@ -371,6 +443,9 @@
@Override
protected final int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
int outputOffset) throws ShortBufferException {
+ if (mCipher != null) {
+ return mCipher.update(input, inputOffset, inputLen, output);
+ }
byte[] outputCopy = engineUpdate(input, inputOffset, inputLen);
if (outputCopy == null) {
return 0;
@@ -387,6 +462,10 @@
@Override
protected final int engineUpdate(ByteBuffer input, ByteBuffer output)
throws ShortBufferException {
+ if (mCipher != null) {
+ return mCipher.update(input, output);
+ }
+
if (input == null) {
throw new NullPointerException("input == null");
}
@@ -423,6 +502,11 @@
@Override
protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) {
+ if (mCipher != null) {
+ mCipher.updateAAD(input, inputOffset, inputLen);
+ return;
+ }
+
if (mCachedException != null) {
return;
}
@@ -459,6 +543,11 @@
@Override
protected final void engineUpdateAAD(ByteBuffer src) {
+ if (mCipher != null) {
+ mCipher.updateAAD(src);
+ return;
+ }
+
if (src == null) {
throw new IllegalArgumentException("src == null");
}
@@ -486,6 +575,10 @@
@Override
protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
+ if (mCipher != null) {
+ return mCipher.doFinal(input, inputOffset, inputLen);
+ }
+
if (mCachedException != null) {
throw (IllegalBlockSizeException)
new IllegalBlockSizeException().initCause(mCachedException);
@@ -522,6 +615,10 @@
protected final int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
+ if (mCipher != null) {
+ return mCipher.doFinal(input, inputOffset, inputLen, output);
+ }
+
byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen);
if (outputCopy == null) {
return 0;
@@ -538,6 +635,10 @@
@Override
protected final int engineDoFinal(ByteBuffer input, ByteBuffer output)
throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
+ if (mCipher != null) {
+ return mCipher.doFinal(input, output);
+ }
+
if (input == null) {
throw new NullPointerException("input == null");
}
@@ -575,6 +676,10 @@
@Override
protected final byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
+ if (mCipher != null) {
+ return mCipher.wrap(key);
+ }
+
if (mKey == null) {
throw new IllegalStateException("Not initilized");
}
@@ -656,6 +761,10 @@
@Override
protected final Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException {
+ if (mCipher != null) {
+ return mCipher.unwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType);
+ }
+
if (mKey == null) {
throw new IllegalStateException("Not initilized");
}
@@ -902,4 +1011,6 @@
*/
protected abstract void loadAlgorithmSpecificParametersFromBeginResult(
KeyParameter[] parameters);
+
+ protected abstract String getTransform();
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
index 2250c89..8289671 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -17,7 +17,7 @@
package android.security.keystore2;
import android.annotation.NonNull;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
@@ -44,6 +44,11 @@
}
@Override
+ protected String getAlgorithm() {
+ return "NONEwithECDSA";
+ }
+
+ @Override
protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
KeyStoreOperation operation) {
return new TruncateToFieldSizeMessageStreamer(
@@ -113,30 +118,50 @@
public SHA1() {
super(KeymasterDefs.KM_DIGEST_SHA1);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA1withECDSA";
+ }
}
public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi {
public SHA224() {
super(KeymasterDefs.KM_DIGEST_SHA_2_224);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA224withECDSA";
+ }
}
public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi {
public SHA256() {
super(KeymasterDefs.KM_DIGEST_SHA_2_256);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA256withECDSA";
+ }
}
public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi {
public SHA384() {
super(KeymasterDefs.KM_DIGEST_SHA_2_384);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA384withECDSA";
+ }
}
public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi {
public SHA512() {
super(KeymasterDefs.KM_DIGEST_SHA_2_512);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA512withECDSA";
+ }
}
private final int mKeymasterDigest;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index eea45c2..8475ad9 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -16,7 +16,7 @@
package android.security.keystore2;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index 479fd8a..233f352 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -16,8 +16,8 @@
package android.security.keystore2;
-import android.hardware.keymint.KeyParameter;
-import android.hardware.keymint.SecurityLevel;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.SecurityLevel;
import android.security.KeyStore2;
import android.security.KeyStoreSecurityLevel;
import android.security.keymaster.KeymasterArguments;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 61725e3..df0e146 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -18,8 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.keymint.KeyParameter;
-import android.hardware.keymint.SecurityLevel;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.SecurityLevel;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore2;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
index afb1054..0c6744f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
@@ -22,11 +22,11 @@
/**
* @hide
*/
-class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter {
+public class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter {
private final int mNamespace;
- AndroidKeyStoreLoadStoreParameter(int namespace) {
+ public AndroidKeyStoreLoadStoreParameter(int namespace) {
mNamespace = namespace;
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index b2e32a3..403da18 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -31,9 +31,7 @@
import android.system.keystore2.KeyMetadata;
import android.system.keystore2.ResponseCode;
-import java.security.KeyFactory;
import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.ProviderException;
import java.security.PublicKey;
@@ -42,8 +40,6 @@
import java.security.UnrecoverableKeyException;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@@ -237,28 +233,11 @@
throw new UnrecoverableKeyException("Failed to obtain X.509 form of public key."
+ " Keystore has no public certificate stored.");
}
- final byte[] x509EncodedPublicKey = metadata.certificate;
+ final byte[] x509PublicCert = metadata.certificate;
- String jcaKeyAlgorithm;
- try {
- jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
- algorithm);
- } catch (IllegalArgumentException e) {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to load private key")
- .initCause(e);
- }
+ PublicKey publicKey = AndroidKeyStoreSpi.toCertificate(x509PublicCert).getPublicKey();
- PublicKey publicKey;
- try {
- KeyFactory keyFactory = KeyFactory.getInstance(jcaKeyAlgorithm);
- publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedPublicKey));
- } catch (NoSuchAlgorithmException e) {
- throw new ProviderException(
- "Failed to obtain " + jcaKeyAlgorithm + " KeyFactory", e);
- } catch (InvalidKeySpecException e) {
- throw new ProviderException("Invalid X.509 encoding of public key", e);
- }
+ String jcaKeyAlgorithm = publicKey.getAlgorithm();
KeyStoreSecurityLevel securityLevel = iSecurityLevel;
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) {
@@ -358,7 +337,7 @@
KeyDescriptor descriptor = new KeyDescriptor();
if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
- descriptor.nspace = 0; // ignored;
+ descriptor.nspace = KeyProperties.NAMESPACE_APPLICATION; // ignored;
descriptor.domain = Domain.APP;
} else {
descriptor.nspace = namespace;
@@ -387,10 +366,10 @@
for (Authorization a : response.metadata.authorizations) {
switch (a.keyParameter.tag) {
case KeymasterDefs.KM_TAG_ALGORITHM:
- keymasterAlgorithm = a.keyParameter.integer;
+ keymasterAlgorithm = a.keyParameter.value.getAlgorithm();
break;
case KeymasterDefs.KM_TAG_DIGEST:
- if (keymasterDigest == -1) keymasterDigest = a.keyParameter.integer;
+ if (keymasterDigest == -1) keymasterDigest = a.keyParameter.value.getDigest();
break;
}
}
@@ -407,7 +386,7 @@
keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata,
new KeyStoreSecurityLevel(response.iSecurityLevel),
- keymasterAlgorithm);
+ keymasterAlgorithm).getPrivateKey();
} else {
throw new UnrecoverableKeyException("Key algorithm unknown");
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index 2686ddc..6ff9432 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeymasterUtils;
@@ -158,7 +158,7 @@
}
/**
- * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF.
+ * RSA cipher with OAEP encryption padding.
*/
abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi {
@@ -316,6 +316,25 @@
protected final int getAdditionalEntropyAmountForFinish() {
return (isEncrypting()) ? mDigestOutputSizeBytes : 0;
}
+
+ @Override
+ protected final String getTransform() {
+ switch (mKeymasterDigest) {
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return "RSA/ECB/OAEPWithSHA-224AndMGF1Padding";
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return "RSA/ECB/OAEPWithSHA-384AndMGF1Padding";
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return "RSA/ECB/OAEPWithSHA-512AndMGF1Padding";
+ default:
+ return "RSA/ECB/OAEPPadding";
+ }
+ }
+
}
public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding {
@@ -358,6 +377,11 @@
}
@Override
+ protected String getTransform() {
+ return "RSA/ECB/" + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding);
+ }
+
+ @Override
protected final void initKey(int opmode, Key key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("Unsupported key: null");
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
index 444dad4..931c2f8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
@@ -17,7 +17,7 @@
package android.security.keystore2;
import android.annotation.NonNull;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
@@ -48,42 +48,70 @@
public NONEWithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_NONE);
}
+ @Override
+ protected String getAlgorithm() {
+ return "NONEwithRSA";
+ }
}
public static final class MD5WithPKCS1Padding extends PKCS1Padding {
public MD5WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_MD5);
}
+ @Override
+ protected String getAlgorithm() {
+ return "MD5withRSA";
+ }
}
public static final class SHA1WithPKCS1Padding extends PKCS1Padding {
public SHA1WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_SHA1);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA1withRSA";
+ }
}
public static final class SHA224WithPKCS1Padding extends PKCS1Padding {
public SHA224WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_224);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA224withRSA";
+ }
}
public static final class SHA256WithPKCS1Padding extends PKCS1Padding {
public SHA256WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_256);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA256withRSA";
+ }
}
public static final class SHA384WithPKCS1Padding extends PKCS1Padding {
public SHA384WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_384);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA384withRSA";
+ }
}
public static final class SHA512WithPKCS1Padding extends PKCS1Padding {
public SHA512WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_512);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA512withRSA";
+ }
}
abstract static class PSSPadding extends AndroidKeyStoreRSASignatureSpi {
@@ -103,30 +131,50 @@
public SHA1WithPSSPadding() {
super(KeymasterDefs.KM_DIGEST_SHA1);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA1withRSA/PSS";
+ }
}
public static final class SHA224WithPSSPadding extends PSSPadding {
public SHA224WithPSSPadding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_224);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA224withRSA/PSS";
+ }
}
public static final class SHA256WithPSSPadding extends PSSPadding {
public SHA256WithPSSPadding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_256);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA256withRSA/PSS";
+ }
}
public static final class SHA384WithPSSPadding extends PSSPadding {
public SHA384WithPSSPadding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_384);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA384withRSA/PSS";
+ }
}
public static final class SHA512WithPSSPadding extends PSSPadding {
public SHA512WithPSSPadding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_512);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA512withRSA/PSS";
+ }
}
private final int mKeymasterDigest;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 9d3b970..74503e1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -102,7 +102,8 @@
insideSecureHardware =
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
securityLevel = a.securityLevel;
- origin = KeyProperties.Origin.fromKeymaster(a.keyParameter.integer);
+ origin = KeyProperties.Origin.fromKeymaster(
+ a.keyParameter.value.getOrigin());
break;
case KeymasterDefs.KM_TAG_KEY_SIZE:
long keySizeUnsigned = KeyStore2ParameterUtils.getUnsignedInt(a);
@@ -113,45 +114,51 @@
keySize = (int) keySizeUnsigned;
break;
case KeymasterDefs.KM_TAG_PURPOSE:
- purposes |= KeyProperties.Purpose.fromKeymaster(a.keyParameter.integer);
+ purposes |= KeyProperties.Purpose.fromKeymaster(
+ a.keyParameter.value.getKeyPurpose());
break;
case KeymasterDefs.KM_TAG_PADDING:
+ int paddingMode = a.keyParameter.value.getPaddingMode();
try {
- if (a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN
- || a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PSS) {
+ if (paddingMode == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN
+ || paddingMode == KeymasterDefs.KM_PAD_RSA_PSS) {
@KeyProperties.SignaturePaddingEnum String padding =
KeyProperties.SignaturePadding.fromKeymaster(
- a.keyParameter.integer);
+ paddingMode);
signaturePaddingsList.add(padding);
} else {
@KeyProperties.EncryptionPaddingEnum String jcaPadding =
KeyProperties.EncryptionPadding.fromKeymaster(
- a.keyParameter.integer);
+ paddingMode);
encryptionPaddingsList.add(jcaPadding);
}
} catch (IllegalArgumentException e) {
throw new ProviderException("Unsupported padding: "
- + a.keyParameter.integer);
+ + paddingMode);
}
break;
case KeymasterDefs.KM_TAG_DIGEST:
- digestsList.add(KeyProperties.Digest.fromKeymaster(a.keyParameter.integer));
+ digestsList.add(KeyProperties.Digest.fromKeymaster(
+ a.keyParameter.value.getDigest()));
break;
case KeymasterDefs.KM_TAG_BLOCK_MODE:
blockModesList.add(
- KeyProperties.BlockMode.fromKeymaster(a.keyParameter.integer)
+ KeyProperties.BlockMode.fromKeymaster(
+ a.keyParameter.value.getBlockMode())
);
break;
case KeymasterDefs.KM_TAG_USER_AUTH_TYPE:
+ int authenticatorType = a.keyParameter.value.getHardwareAuthenticatorType();
if (KeyStore2ParameterUtils.isSecureHardware(a.securityLevel)) {
- keymasterHwEnforcedUserAuthenticators = a.keyParameter.integer;
+ keymasterHwEnforcedUserAuthenticators = authenticatorType;
} else {
- keymasterSwEnforcedUserAuthenticators = a.keyParameter.integer;
+ keymasterSwEnforcedUserAuthenticators = authenticatorType;
}
break;
case KeymasterDefs.KM_TAG_USER_SECURE_ID:
keymasterSecureUserIds.add(
- KeymasterArguments.toUint64(a.keyParameter.longInteger));
+ KeymasterArguments.toUint64(
+ a.keyParameter.value.getLongInteger()));
break;
case KeymasterDefs.KM_TAG_ACTIVE_DATETIME:
keyValidityStart = KeyStore2ParameterUtils.getDate(a);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
index a168f8f..96da1e0 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
@@ -18,7 +18,7 @@
import android.annotation.CallSuper;
import android.annotation.NonNull;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
@@ -30,10 +30,12 @@
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.ProviderException;
import java.security.PublicKey;
import java.security.SecureRandom;
+import java.security.Signature;
import java.security.SignatureException;
import java.security.SignatureSpi;
import java.util.ArrayList;
@@ -76,6 +78,13 @@
*/
private Exception mCachedException;
+ /**
+ * This signature object is used for public key operations, i.e, signatrue verification.
+ * The Android Keystore backend does not perform public key operations and defers to the
+ * Highest priority provider.
+ */
+ private Signature mSignature;
+
AndroidKeyStoreSignatureSpiBase() {
mOperation = null;
mOperationChallenge = 0;
@@ -84,6 +93,7 @@
appRandom = null;
mMessageStreamer = null;
mCachedException = null;
+ mSignature = null;
}
@Override
@@ -123,27 +133,13 @@
protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
resetAll();
- boolean success = false;
try {
- if (publicKey == null) {
- throw new InvalidKeyException("Unsupported key: null");
- }
- AndroidKeyStoreKey keystoreKey;
- if (publicKey instanceof AndroidKeyStorePublicKey) {
- keystoreKey = (AndroidKeyStorePublicKey) publicKey;
- } else {
- throw new InvalidKeyException("Unsupported public key type: " + publicKey);
- }
- mSigning = false;
- initKey(keystoreKey);
- appRandom = null;
- ensureKeystoreOperationInitialized();
- success = true;
- } finally {
- if (!success) {
- resetAll();
- }
+ mSignature = Signature.getInstance(getAlgorithm());
+ } catch (NoSuchAlgorithmException e) {
+ throw new InvalidKeyException(e);
}
+
+ mSignature.initVerify(publicKey);
}
/**
@@ -251,6 +247,11 @@
@Override
protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+ if (mSignature != null) {
+ mSignature.update(b, off, len);
+ return;
+ }
+
if (mCachedException != null) {
throw new SignatureException(mCachedException);
}
@@ -337,39 +338,10 @@
@Override
protected final boolean engineVerify(byte[] signature) throws SignatureException {
- if (mCachedException != null) {
- throw new SignatureException(mCachedException);
+ if (mSignature != null) {
+ return mSignature.verify(signature);
}
-
- try {
- ensureKeystoreOperationInitialized();
- } catch (InvalidKeyException e) {
- throw new SignatureException(e);
- }
-
- boolean verified;
- try {
- byte[] output = mMessageStreamer.doFinal(
- EmptyArray.BYTE, 0, 0,
- signature);
- if (output.length != 0) {
- throw new ProviderException(
- "Signature verification unexpected produced output: " + output.length
- + " bytes");
- }
- verified = true;
- } catch (KeyStoreException e) {
- switch (e.getErrorCode()) {
- case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
- verified = false;
- break;
- default:
- throw new SignatureException(e);
- }
- }
-
- resetWhilePreservingInitState();
- return verified;
+ throw new IllegalStateException("Not initialised.");
}
@Override
@@ -392,6 +364,13 @@
}
/**
+ * Implementations need to report the algorithm they implement so that we can delegate to the
+ * highest priority provider.
+ * @return Algorithm string.
+ */
+ protected abstract String getAlgorithm();
+
+ /**
* Returns {@code true} if this signature is initialized for signing, {@code false} if this
* signature is initialized for verification.
*/
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 9790a4a..5e7f648 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -18,9 +18,9 @@
import android.annotation.NonNull;
import android.hardware.biometrics.BiometricManager;
-import android.hardware.keymint.HardwareAuthenticatorType;
-import android.hardware.keymint.KeyParameter;
-import android.hardware.keymint.SecurityLevel;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.SecurityLevel;
import android.security.GateKeeper;
import android.security.KeyStore2;
import android.security.KeyStoreParameter;
@@ -219,7 +219,7 @@
return null;
}
- private static X509Certificate toCertificate(byte[] bytes) {
+ static X509Certificate toCertificate(byte[] bytes) {
try {
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certFactory.generateCertificate(
@@ -250,13 +250,10 @@
return null;
}
-
- // TODO add modification time to key metadata.
- return null;
- // if (response.metadata.modificationTime == -1) {
- // return null;
- // }
- // return new Date(response.metadata.modificationTime);
+ if (response.metadata.modificationTimeMs == -1) {
+ return null;
+ }
+ return new Date(response.metadata.modificationTimeMs);
}
@Override
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
index a2d4528..5c048a1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyProperties;
@@ -42,7 +42,7 @@
*
* @hide
*/
-class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase {
+abstract class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase {
abstract static class ECB extends AndroidKeyStoreUnauthenticatedAESCipherSpi {
protected ECB(int keymasterPadding) {
@@ -53,12 +53,22 @@
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
+
+ @Override
+ protected final String getTransform() {
+ return "AES/ECB/NoPadding";
+ }
}
public static class PKCS7Padding extends ECB {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
+
+ @Override
+ protected final String getTransform() {
+ return "AES/ECB/PKCS7Padding";
+ }
}
}
@@ -71,12 +81,22 @@
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
+
+ @Override
+ protected final String getTransform() {
+ return "AES/CBC/NoPadding";
+ }
}
public static class PKCS7Padding extends CBC {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
+
+ @Override
+ protected final String getTransform() {
+ return "AES/CBC/PKCS7Padding";
+ }
}
}
@@ -89,6 +109,11 @@
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
+
+ @Override
+ protected final String getTransform() {
+ return "AES/CTR/NoPadding";
+ }
}
}
@@ -275,7 +300,7 @@
if (parameters != null) {
for (KeyParameter p : parameters) {
if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
- returnedIv = p.blob;
+ returnedIv = p.value.getBlob();
break;
}
}
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index 8fa532b..4c8ab8d 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -18,8 +18,10 @@
import android.annotation.NonNull;
import android.hardware.biometrics.BiometricManager;
-import android.hardware.keymint.KeyParameter;
-import android.hardware.keymint.SecurityLevel;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameterValue;
+import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.Tag;
import android.security.GateKeeper;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
@@ -50,7 +52,7 @@
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.boolValue = true;
+ p.value = KeyParameterValue.boolValue(true);
return p;
}
@@ -62,14 +64,40 @@
* @hide
*/
static @NonNull KeyParameter makeEnum(int tag, int v) {
- int type = KeymasterDefs.getTagType(tag);
- if (type != KeymasterDefs.KM_ENUM && type != KeymasterDefs.KM_ENUM_REP) {
- throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag);
+ KeyParameter kp = new KeyParameter();
+ kp.tag = tag;
+ switch (tag) {
+ case Tag.PURPOSE:
+ kp.value = KeyParameterValue.keyPurpose(v);
+ break;
+ case Tag.ALGORITHM:
+ kp.value = KeyParameterValue.algorithm(v);
+ break;
+ case Tag.BLOCK_MODE:
+ kp.value = KeyParameterValue.blockMode(v);
+ break;
+ case Tag.DIGEST:
+ kp.value = KeyParameterValue.digest(v);
+ break;
+ case Tag.EC_CURVE:
+ kp.value = KeyParameterValue.ecCurve(v);
+ break;
+ case Tag.ORIGIN:
+ kp.value = KeyParameterValue.origin(v);
+ break;
+ case Tag.PADDING:
+ kp.value = KeyParameterValue.paddingMode(v);
+ break;
+ case Tag.USER_AUTH_TYPE:
+ kp.value = KeyParameterValue.hardwareAuthenticatorType(v);
+ break;
+ case Tag.HARDWARE_TYPE:
+ kp.value = KeyParameterValue.securityLevel(v);
+ break;
+ default:
+ throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag);
}
- KeyParameter p = new KeyParameter();
- p.tag = tag;
- p.integer = v;
- return p;
+ return kp;
}
/**
@@ -86,7 +114,7 @@
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.integer = v;
+ p.value = KeyParameterValue.integer(v);
return p;
}
@@ -104,7 +132,7 @@
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.longInteger = v;
+ p.value = KeyParameterValue.longInteger(v);
return p;
}
@@ -121,7 +149,7 @@
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.blob = b;
+ p.value = KeyParameterValue.blob(b);
return p;
}
@@ -138,9 +166,10 @@
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.longInteger = date.getTime();
- if (p.longInteger < 0) {
- throw new IllegalArgumentException("Date tag value out of range: " + p.longInteger);
+ p.value = KeyParameterValue.dateTime(date.getTime());
+ if (p.value.getDateTime() < 0) {
+ throw new IllegalArgumentException("Date tag value out of range: "
+ + p.value.getDateTime());
}
return p;
}
@@ -160,24 +189,24 @@
throw new IllegalArgumentException("Not an int tag: " + param.keyParameter.tag);
}
// KM_UINT is 32 bits wide so we must suppress sign extension.
- return ((long) param.keyParameter.integer) & 0xffffffffL;
+ return ((long) param.keyParameter.value.getInteger()) & 0xffffffffL;
}
static @NonNull Date getDate(@NonNull Authorization param) {
if (KeymasterDefs.getTagType(param.keyParameter.tag) != KeymasterDefs.KM_DATE) {
throw new IllegalArgumentException("Not a date tag: " + param.keyParameter.tag);
}
- if (param.keyParameter.longInteger < 0) {
+ if (param.keyParameter.value.getDateTime() < 0) {
throw new IllegalArgumentException("Date Value too large: "
- + param.keyParameter.longInteger);
+ + param.keyParameter.value.getDateTime());
}
- return new Date(param.keyParameter.longInteger);
+ return new Date(param.keyParameter.value.getDateTime());
}
static void forEachSetFlag(int flags, Consumer<Integer> consumer) {
int offset = 0;
while (flags != 0) {
- if ((flags & 1) == 0) {
+ if ((flags & 1) == 1) {
consumer.accept(1 << offset);
}
offset += 1;
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
index 3b11854..f87a3d2 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -57,7 +57,7 @@
for (Authorization p : key.getAuthorizations()) {
switch(p.keyParameter.tag) {
case KeymasterDefs.KM_TAG_USER_SECURE_ID:
- keySids.add(p.keyParameter.longInteger);
+ keySids.add(p.keyParameter.value.getLongInteger());
break;
default:
break;
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index aa34edf..6a7df94 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -61,6 +61,9 @@
],
export_include_dirs: ["include"],
export_shared_lib_headers: ["libz"],
+ static_libs: ["libincfs-utils"],
+ whole_static_libs: ["libincfs-utils"],
+ export_static_lib_headers: ["libincfs-utils"],
target: {
android: {
srcs: [
@@ -69,13 +72,14 @@
"CursorWindow.cpp",
],
shared_libs: [
- "libziparchive",
"libbase",
"libbinder",
"liblog",
"libcutils",
+ "libincfs",
"libutils",
"libz",
+ "libziparchive",
],
static: {
enabled: false,
@@ -86,11 +90,11 @@
enabled: false,
},
static_libs: [
- "libziparchive",
"libbase",
- "liblog",
"libcutils",
+ "liblog",
"libutils",
+ "libziparchive",
],
shared_libs: [
"libz",
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index e15b42d..cb56a51 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -25,13 +25,11 @@
#include "android-base/unique_fd.h"
#include "android-base/utf8.h"
#include "utils/Compat.h"
-#include "utils/FileMap.h"
#include "ziparchive/zip_archive.h"
#include "androidfw/Asset.h"
#include "androidfw/Idmap.h"
#include "androidfw/misc.h"
-#include "androidfw/ResourceTypes.h"
#include "androidfw/Util.h"
namespace android {
@@ -161,50 +159,46 @@
}
const int fd = ::GetFileDescriptor(zip_handle_.get());
- const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
+ const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
+ incfs::IncFsFileMap asset_map;
if (entry.method == kCompressDeflated) {
- std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
- if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length,
- true /*readOnly*/)) {
+ if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) {
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
return {};
}
std::unique_ptr<Asset> asset =
- Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
+ Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
if (asset == nullptr) {
LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
return {};
}
return asset;
- } else {
- std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
- if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.uncompressed_length,
- true /*readOnly*/)) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
-
- unique_fd ufd;
- if (!GetPath()) {
- // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
- // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
- // to create new file descriptors.
- ufd = unique_fd(dup(fd));
- if (!ufd.ok()) {
- LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- }
-
- std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map),
- std::move(ufd), mode);
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- return asset;
}
+
+ if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+
+ unique_fd ufd;
+ if (!GetPath()) {
+ // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
+ // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
+ // to create new file descriptors.
+ ufd = unique_fd(dup(fd));
+ if (!ufd.ok()) {
+ LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+ }
+
+ auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+ return asset;
}
private:
@@ -446,8 +440,8 @@
}
}
- std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
- if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) {
+ incfs::IncFsFileMap file_map;
+ if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
<< SystemErrorCodeToString(errno);
return {};
@@ -456,8 +450,8 @@
// If `path` is set, do not pass ownership of the `fd` to the new Asset since
// Asset::openFileDescriptor can use `path` to create new file descriptors.
return Asset::createFromUncompressedMap(std::move(file_map),
- (path) ? base::unique_fd(-1) : std::move(fd),
- Asset::AccessMode::ACCESS_RANDOM);
+ Asset::AccessMode::ACCESS_RANDOM,
+ (path) ? base::unique_fd(-1) : std::move(fd));
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
@@ -493,15 +487,14 @@
loaded_apk->idmap_asset_ = std::move(idmap_asset);
loaded_apk->loaded_idmap_ = std::move(idmap);
- const StringPiece data(
- reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
- loaded_apk->resources_asset_->getLength());
- if (data.data() == nullptr || data.empty()) {
+ const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
+ const size_t length = loaded_apk->resources_asset_->getLength();
+ if (!data || length == 0) {
LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'.";
return {};
}
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(),
property_flags);
if (!loaded_apk->loaded_arsc_) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
@@ -525,15 +518,15 @@
new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
loaded_apk->resources_asset_ = std::move(resources_asset);
- const StringPiece data(
- reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
- loaded_apk->resources_asset_->getLength());
- if (data.data() == nullptr || data.empty()) {
+ const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
+ const size_t length = loaded_apk->resources_asset_->getLength();
+ if (!data || length == 0) {
LOG(ERROR) << "Failed to read resources table data in '" << path << "'.";
return {};
}
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */,
+ property_flags);
if (loaded_apk->loaded_arsc_ == nullptr) {
LOG(ERROR) << "Failed to read resources table in '" << path << "'.";
return {};
@@ -550,7 +543,6 @@
}
return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) &&
last_mod_time_ == getFileModDate(path_.c_str());
-
}
} // namespace android
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index cd30c18..4fbe4a3 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -298,34 +298,18 @@
/*
* Create a new Asset from a memory mapping.
*/
-/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode)
+/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(incfs::IncFsFileMap&& dataMap,
+ AccessMode mode,
+ base::unique_fd fd)
{
- _FileAsset* pAsset;
- status_t result;
+ auto pAsset = util::make_unique<_FileAsset>();
- pAsset = new _FileAsset;
- result = pAsset->openChunk(dataMap, base::unique_fd(-1));
- if (result != NO_ERROR) {
- delete pAsset;
- return NULL;
- }
-
- pAsset->mAccessMode = mode;
- return pAsset;
-}
-
-/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
- base::unique_fd fd, AccessMode mode)
-{
- std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>();
-
- status_t result = pAsset->openChunk(dataMap.get(), std::move(fd));
+ status_t result = pAsset->openChunk(std::move(dataMap), std::move(fd));
if (result != NO_ERROR) {
return NULL;
}
// We succeeded, so relinquish control of dataMap
- (void) dataMap.release();
pAsset->mAccessMode = mode;
return std::move(pAsset);
}
@@ -333,35 +317,18 @@
/*
* Create a new Asset from compressed data in a memory mapping.
*/
-/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
- size_t uncompressedLen, AccessMode mode)
+/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(incfs::IncFsFileMap&& dataMap,
+ size_t uncompressedLen,
+ AccessMode mode)
{
- _CompressedAsset* pAsset;
- status_t result;
+ auto pAsset = util::make_unique<_CompressedAsset>();
- pAsset = new _CompressedAsset;
- result = pAsset->openChunk(dataMap, uncompressedLen);
- if (result != NO_ERROR) {
- delete pAsset;
- return NULL;
- }
-
- pAsset->mAccessMode = mode;
- return pAsset;
-}
-
-/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
- size_t uncompressedLen, AccessMode mode)
-{
- std::unique_ptr<_CompressedAsset> pAsset = util::make_unique<_CompressedAsset>();
-
- status_t result = pAsset->openChunk(dataMap.get(), uncompressedLen);
+ status_t result = pAsset->openChunk(std::move(dataMap), uncompressedLen);
if (result != NO_ERROR) {
return NULL;
}
// We succeeded, so relinquish control of dataMap
- (void) dataMap.release();
pAsset->mAccessMode = mode;
return std::move(pAsset);
}
@@ -414,7 +381,7 @@
* Constructor.
*/
_FileAsset::_FileAsset(void)
- : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mMap(NULL), mBuf(NULL)
+ : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mBuf(NULL)
{
// Register the Asset with the global list here after it is fully constructed and its
// vtable pointer points to this concrete type. b/31113965
@@ -441,7 +408,7 @@
status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
{
assert(mFp == NULL); // no reopen
- assert(mMap == NULL);
+ assert(!mMap.has_value());
assert(fd >= 0);
assert(offset >= 0);
@@ -484,15 +451,15 @@
/*
* Create the chunk from the map.
*/
-status_t _FileAsset::openChunk(FileMap* dataMap, base::unique_fd fd)
+status_t _FileAsset::openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd)
{
assert(mFp == NULL); // no reopen
- assert(mMap == NULL);
+ assert(!mMap.has_value());
assert(dataMap != NULL);
- mMap = dataMap;
+ mMap = std::move(dataMap);
mStart = -1; // not used
- mLength = dataMap->getDataLength();
+ mLength = mMap->length();
mFd = std::move(fd);
assert(mOffset == 0);
@@ -528,10 +495,15 @@
if (!count)
return 0;
- if (mMap != NULL) {
+ if (mMap.has_value()) {
/* copy from mapped area */
//printf("map read\n");
- memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
+ const auto readPos = mMap->data().offset(mOffset).convert<char>();
+ if (!readPos.verify(count)) {
+ return -1;
+ }
+
+ memcpy(buf, readPos.unsafe_ptr(), count);
actual = count;
} else if (mBuf != NULL) {
/* copy from buffer */
@@ -594,10 +566,6 @@
*/
void _FileAsset::close(void)
{
- if (mMap != NULL) {
- delete mMap;
- mMap = NULL;
- }
if (mBuf != NULL) {
delete[] mBuf;
mBuf = NULL;
@@ -624,16 +592,21 @@
* level and we'd be using a different object, but we didn't, so we
* deal with it here.
*/
-const void* _FileAsset::getBuffer(bool wordAligned)
+const void* _FileAsset::getBuffer(bool aligned)
+{
+ return getIncFsBuffer(aligned).unsafe_ptr();
+}
+
+incfs::map_ptr<void> _FileAsset::getIncFsBuffer(bool aligned)
{
/* subsequent requests just use what we did previously */
if (mBuf != NULL)
return mBuf;
- if (mMap != NULL) {
- if (!wordAligned) {
- return mMap->getDataPtr();
+ if (mMap.has_value()) {
+ if (!aligned) {
+ return mMap->data();
}
- return ensureAlignment(mMap);
+ return ensureAlignment(*mMap);
}
assert(mFp != NULL);
@@ -671,47 +644,44 @@
mBuf = buf;
return mBuf;
} else {
- FileMap* map;
-
- map = new FileMap;
- if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
- delete map;
+ incfs::IncFsFileMap map;
+ if (!map.Create(fileno(mFp), mStart, mLength, NULL /* file_name */ )) {
return NULL;
}
ALOGV(" getBuffer: mapped\n");
- mMap = map;
- if (!wordAligned) {
- return mMap->getDataPtr();
+ mMap = std::move(map);
+ if (!aligned) {
+ return mMap->data();
}
- return ensureAlignment(mMap);
+ return ensureAlignment(*mMap);
}
}
int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
{
- if (mMap != NULL) {
+ if (mMap.has_value()) {
if (mFd.ok()) {
- *outStart = mMap->getDataOffset();
- *outLength = mMap->getDataLength();
- const int fd = dup(mFd);
- if (fd < 0) {
- ALOGE("Unable to dup fd (%d).", mFd.get());
- return -1;
- }
- lseek64(fd, 0, SEEK_SET);
- return fd;
+ *outStart = mMap->offset();
+ *outLength = mMap->length();
+ const int fd = dup(mFd);
+ if (fd < 0) {
+ ALOGE("Unable to dup fd (%d).", mFd.get());
+ return -1;
+ }
+ lseek64(fd, 0, SEEK_SET);
+ return fd;
}
- const char* fname = mMap->getFileName();
+ const char* fname = mMap->file_name();
if (fname == NULL) {
fname = mFileName;
}
if (fname == NULL) {
return -1;
}
- *outStart = mMap->getDataOffset();
- *outLength = mMap->getDataLength();
+ *outStart = mMap->offset();
+ *outLength = mMap->length();
return open(fname, O_RDONLY | O_BINARY);
}
if (mFileName == NULL) {
@@ -722,16 +692,21 @@
return open(mFileName, O_RDONLY | O_BINARY);
}
-const void* _FileAsset::ensureAlignment(FileMap* map)
+incfs::map_ptr<void> _FileAsset::ensureAlignment(const incfs::IncFsFileMap& map)
{
- void* data = map->getDataPtr();
- if ((((size_t)data)&0x3) == 0) {
+ const auto data = map.data();
+ if (util::IsFourByteAligned(data)) {
// We can return this directly if it is aligned on a word
// boundary.
ALOGV("Returning aligned FileAsset %p (%s).", this,
getAssetSource());
return data;
}
+
+ if (!data.convert<uint8_t>().verify(mLength)) {
+ return NULL;
+ }
+
// If not aligned on a word boundary, then we need to copy it into
// our own buffer.
ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
@@ -741,7 +716,8 @@
ALOGE("alloc of %ld bytes failed\n", (long) mLength);
return NULL;
}
- memcpy(buf, data, mLength);
+
+ memcpy(buf, data.unsafe_ptr(), mLength);
mBuf = buf;
return buf;
}
@@ -757,7 +733,7 @@
*/
_CompressedAsset::_CompressedAsset(void)
: mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
- mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
+ mFd(-1), mZipInflater(NULL), mBuf(NULL)
{
// Register the Asset with the global list here after it is fully constructed and its
// vtable pointer points to this concrete type. b/31113965
@@ -786,7 +762,7 @@
int compressionMethod, size_t uncompressedLen, size_t compressedLen)
{
assert(mFd < 0); // no re-open
- assert(mMap == NULL);
+ assert(!mMap.has_value());
assert(fd >= 0);
assert(offset >= 0);
assert(compressedLen > 0);
@@ -815,20 +791,20 @@
*
* Nothing is expanded until the first read call.
*/
-status_t _CompressedAsset::openChunk(FileMap* dataMap, size_t uncompressedLen)
+status_t _CompressedAsset::openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen)
{
assert(mFd < 0); // no re-open
- assert(mMap == NULL);
+ assert(!mMap.has_value());
assert(dataMap != NULL);
- mMap = dataMap;
+ mMap = std::move(dataMap);
mStart = -1; // not used
- mCompressedLen = dataMap->getDataLength();
+ mCompressedLen = mMap->length();
mUncompressedLen = uncompressedLen;
assert(mOffset == 0);
if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
- mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
+ mZipInflater = new StreamingZipInflater(&(*mMap), uncompressedLen);
}
return NO_ERROR;
}
@@ -901,11 +877,6 @@
*/
void _CompressedAsset::close(void)
{
- if (mMap != NULL) {
- delete mMap;
- mMap = NULL;
- }
-
delete[] mBuf;
mBuf = NULL;
@@ -940,8 +911,8 @@
goto bail;
}
- if (mMap != NULL) {
- if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
+ if (mMap.has_value()) {
+ if (!ZipUtils::inflateToBuffer(mMap->data(), buf,
mUncompressedLen, mCompressedLen))
goto bail;
} else {
@@ -976,3 +947,6 @@
return mBuf;
}
+incfs::map_ptr<void> _CompressedAsset::getIncFsBuffer(bool aligned) {
+ return incfs::map_ptr<void>(getBuffer(aligned));
+}
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index f7c8337..fb2b571 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -917,7 +917,7 @@
Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile,
const ZipEntryRO entry, AccessMode mode, const String8& entryName)
{
- Asset* pAsset = NULL;
+ std::unique_ptr<Asset> pAsset;
// TODO: look for previously-created shared memory slice?
uint16_t method;
@@ -932,28 +932,28 @@
return NULL;
}
- FileMap* dataMap = pZipFile->createEntryFileMap(entry);
- if (dataMap == NULL) {
+ std::optional<incfs::IncFsFileMap> dataMap = pZipFile->createEntryIncFsFileMap(entry);
+ if (!dataMap.has_value()) {
ALOGW("create map from entry failed\n");
return NULL;
}
if (method == ZipFileRO::kCompressStored) {
- pAsset = Asset::createFromUncompressedMap(dataMap, mode);
+ pAsset = Asset::createFromUncompressedMap(std::move(*dataMap), mode);
ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(),
- dataMap->getFileName(), mode, pAsset);
+ dataMap->file_name(), mode, pAsset.get());
} else {
- pAsset = Asset::createFromCompressedMap(dataMap,
+ pAsset = Asset::createFromCompressedMap(std::move(*dataMap),
static_cast<size_t>(uncompressedLen), mode);
ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(),
- dataMap->getFileName(), mode, pAsset);
+ dataMap->file_name(), mode, pAsset.get());
}
if (pAsset == NULL) {
/* unexpected */
ALOGW("create from segment failed\n");
}
- return pAsset;
+ return pAsset.release();
}
/*
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 99dd313..bec80a7 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -38,9 +38,43 @@
namespace android {
+namespace {
+
+using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>;
+
+base::expected<EntryValue, IOError> GetEntryValue(
+ incfs::verified_map_ptr<ResTable_entry> table_entry) {
+ const uint16_t entry_size = dtohs(table_entry->size);
+
+ // Check if the entry represents a bag value.
+ if (entry_size >= sizeof(ResTable_map_entry) &&
+ (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
+ const auto map_entry = table_entry.convert<ResTable_map_entry>();
+ if (!map_entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ return map_entry.verified();
+ }
+
+ // The entry represents a non-bag value.
+ const auto entry_value = table_entry.offset(entry_size).convert<Res_value>();
+ if (!entry_value) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ Res_value value;
+ value.copyFrom_dtoh(entry_value.value());
+ return value;
+}
+
+} // namespace
+
struct FindEntryResult {
- // A pointer to the value of the resource table entry.
- std::variant<Res_value, const ResTable_map_entry*> entry;
+ // The cookie representing the ApkAssets in which the value resides.
+ ApkAssetsCookie cookie;
+
+ // The value of the resource table entry. Either an android::Res_value for non-bag types or an
+ // incfs::verified_map_ptr<ResTable_map_entry> for bag types.
+ EntryValue entry;
// The configuration for which the resulting entry was defined. This is already swapped to host
// endianness.
@@ -265,7 +299,7 @@
}
const PackageGroup& package_group = package_groups_[idx];
- if (package_group.packages_.size() == 0) {
+ if (package_group.packages_.empty()) {
return nullptr;
}
@@ -310,14 +344,14 @@
for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) {
const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it);
if (info != nullptr) {
- ResourceName res_name;
- if (!GetResourceName(*it, &res_name)) {
+ auto res_name = GetResourceName(*it);
+ if (!res_name.has_value()) {
ANDROID_LOG(ERROR) << base::StringPrintf(
"Unable to retrieve name of overlayable resource 0x%08x", *it);
return false;
}
- const std::string name = ToFormattedResourceString(&res_name);
+ const std::string name = ToFormattedResourceString(*res_name);
output.append(base::StringPrintf(
"resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n",
name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags));
@@ -365,8 +399,8 @@
return non_system_overlays;
}
-std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
- bool exclude_mipmap) const {
+base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceConfigurations(
+ bool exclude_system, bool exclude_mipmap) const {
ATRACE_NAME("AssetManager::GetResourceConfigurations");
const auto non_system_overlays =
(exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
@@ -386,7 +420,10 @@
continue;
}
- package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
+ auto result = package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
+ if (UNLIKELY(!result.has_value())) {
+ return base::unexpected(result.error());
+ }
}
}
return configurations;
@@ -501,11 +538,11 @@
return apk_assets_[cookie]->GetAssetsProvider()->Open(filename, mode);
}
-ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
- bool /*stop_at_first_match*/,
- bool ignore_configuration,
- FindEntryResult* out_entry) const {
- if (resource_resolution_logging_enabled_) {
+base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
+ uint32_t resid, uint16_t density_override, bool stop_at_first_match,
+ bool ignore_configuration) const {
+ const bool logging_enabled = resource_resolution_logging_enabled_;
+ if (UNLIKELY(logging_enabled)) {
// Clear the last logged resource resolution.
ResetResourceResolution();
last_resolution_.resid = resid;
@@ -523,94 +560,96 @@
}
// Retrieve the package group from the package id of the resource id.
- if (!is_valid_resid(resid)) {
+ if (UNLIKELY(!is_valid_resid(resid))) {
LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
- return kInvalidCookie;
+ return base::unexpected(std::nullopt);
}
const uint32_t package_id = get_package_id(resid);
const uint8_t type_idx = get_type_id(resid) - 1;
const uint16_t entry_idx = get_entry_id(resid);
uint8_t package_idx = package_ids_[package_id];
- if (package_idx == 0xff) {
+ if (UNLIKELY(package_idx == 0xff)) {
ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
package_id, resid);
- return kInvalidCookie;
+ return base::unexpected(std::nullopt);
}
const PackageGroup& package_group = package_groups_[package_idx];
- ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
- false /* stop_at_first_match */,
- ignore_configuration, out_entry);
- if (UNLIKELY(cookie == kInvalidCookie)) {
- return kInvalidCookie;
+ auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
+ stop_at_first_match, ignore_configuration);
+ if (UNLIKELY(!result.has_value())) {
+ return base::unexpected(result.error());
}
- if (!apk_assets_[cookie]->IsLoader()) {
+ if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) {
for (const auto& id_map : package_group.overlays_) {
auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
if (!overlay_entry) {
// No id map entry exists for this target resource.
continue;
- } else if (overlay_entry.IsInlineValue()) {
+ }
+ if (overlay_entry.IsInlineValue()) {
// The target resource is overlaid by an inline value not represented by a resource.
- out_entry->entry = overlay_entry.GetInlineValue();
- out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
- cookie = id_map.cookie;
+ result->entry = overlay_entry.GetInlineValue();
+ result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ result->cookie = id_map.cookie;
continue;
}
- FindEntryResult overlay_result;
- ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override,
- false /* stop_at_first_match */,
- ignore_configuration, &overlay_result);
- if (UNLIKELY(overlay_cookie == kInvalidCookie)) {
+ auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
+ false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (UNLIKELY(IsIOError(overlay_result))) {
+ return base::unexpected(overlay_result.error());
+ }
+ if (!overlay_result.has_value()) {
continue;
}
- if (!overlay_result.config.isBetterThan(out_entry->config, desired_config)
- && overlay_result.config.compare(out_entry->config) != 0) {
+ if (!overlay_result->config.isBetterThan(result->config, desired_config)
+ && overlay_result->config.compare(result->config) != 0) {
// The configuration of the entry for the overlay must be equal to or better than the target
// configuration to be chosen as the better value.
continue;
}
- cookie = overlay_cookie;
- out_entry->entry = overlay_result.entry;
- out_entry->config = overlay_result.config;
- out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
- if (resource_resolution_logging_enabled_) {
+ result->cookie = overlay_result->cookie;
+ result->entry = overlay_result->entry;
+ result->config = overlay_result->config;
+ result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+
+ if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(
- Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
- overlay_result.package_name});
+ Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(),
+ overlay_result->package_name});
}
}
}
- if (resource_resolution_logging_enabled_) {
- last_resolution_.cookie = cookie;
- last_resolution_.type_string_ref = out_entry->type_string_ref;
- last_resolution_.entry_string_ref = out_entry->entry_string_ref;
+ if (UNLIKELY(logging_enabled)) {
+ last_resolution_.cookie = result->cookie;
+ last_resolution_.type_string_ref = result->type_string_ref;
+ last_resolution_.entry_string_ref = result->entry_string_ref;
}
- return cookie;
+ return result;
}
-ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group,
- uint8_t type_idx, uint16_t entry_idx,
- const ResTable_config& desired_config,
- bool /*stop_at_first_match*/,
- bool ignore_configuration,
- FindEntryResult* out_entry) const {
+base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
+ const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx,
+ const ResTable_config& desired_config, bool stop_at_first_match,
+ bool ignore_configuration) const {
+ const bool logging_enabled = resource_resolution_logging_enabled_;
ApkAssetsCookie best_cookie = kInvalidCookie;
const LoadedPackage* best_package = nullptr;
- const ResTable_type* best_type = nullptr;
+ incfs::verified_map_ptr<ResTable_type> best_type;
const ResTable_config* best_config = nullptr;
ResTable_config best_config_copy;
- uint32_t best_offset = 0u;
- uint32_t type_flags = 0u;
+ uint32_t best_offset = 0U;
+ uint32_t type_flags = 0U;
- Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY;
+ auto resolution_type = Resolution::Step::Type::NO_ENTRY;
std::vector<Resolution::Step> resolution_steps;
// If desired_config is the same as the set configuration, then we can use our filtered list
@@ -630,17 +669,20 @@
continue;
}
+ auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx);
+ if (UNLIKELY(!entry_flags.has_value())) {
+ return base::unexpected(entry_flags.error());
+ }
+ type_flags |= entry_flags.value();
+
// If the package is an overlay or custom loader,
// then even configurations that are the same MUST be chosen.
const bool package_is_loader = loaded_package->IsCustomLoader();
- type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx);
if (use_fast_path) {
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
- const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
- const size_t type_count = candidate_configs.size();
- for (uint32_t i = 0; i < type_count; i++) {
- const ResTable_config& this_config = candidate_configs[i];
+ for (const auto& type_config : filtered_group.type_configs) {
+ const ResTable_config& this_config = type_config.config;
// We can skip calling ResTable_config::match() because we know that all candidate
// configurations that do NOT match have been filtered-out.
@@ -652,7 +694,7 @@
} else if (package_is_loader && this_config.compare(*best_config) == 0) {
resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
} else {
- if (resource_resolution_logging_enabled_) {
+ if (UNLIKELY(logging_enabled)) {
resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER
: Resolution::Step::Type::SKIPPED;
resolution_steps.push_back(Resolution::Step{resolution_type,
@@ -664,10 +706,13 @@
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
- const ResTable_type* type = filtered_group.types[i];
- const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx);
- if (offset == ResTable_type::NO_ENTRY) {
- if (resource_resolution_logging_enabled_) {
+ const auto& type = type_config.type;
+ const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
+ if (UNLIKELY(IsIOError(offset))) {
+ return base::unexpected(offset.error());
+ }
+ if (!offset.has_value()) {
+ if (UNLIKELY(logging_enabled)) {
if (package_is_loader) {
resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER;
} else {
@@ -684,9 +729,9 @@
best_package = loaded_package;
best_type = type;
best_config = &this_config;
- best_offset = offset;
+ best_offset = offset.value();
- if (resource_resolution_logging_enabled_) {
+ if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
this_config.toString(),
&loaded_package->GetPackageName()});
@@ -700,10 +745,11 @@
// ResTable_config, we must copy it.
const auto iter_end = type_spec->types + type_spec->type_count;
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- ResTable_config this_config{};
+ const incfs::verified_map_ptr<ResTable_type>& type = *iter;
+ ResTable_config this_config{};
if (!ignore_configuration) {
- this_config.copyFromDtoH((*iter)->config);
+ this_config.copyFromDtoH(type->config);
if (!this_config.match(desired_config)) {
continue;
}
@@ -722,24 +768,27 @@
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
- const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx);
- if (offset == ResTable_type::NO_ENTRY) {
+ const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
+ if (UNLIKELY(IsIOError(offset))) {
+ return base::unexpected(offset.error());
+ }
+ if (!offset.has_value()) {
continue;
}
best_cookie = cookie;
best_package = loaded_package;
- best_type = *iter;
+ best_type = type;
best_config_copy = this_config;
best_config = &best_config_copy;
- best_offset = offset;
+ best_offset = offset.value();
- if (ignore_configuration) {
+ if (stop_at_first_match) {
// Any configuration will suffice, so break.
break;
}
- if (resource_resolution_logging_enabled_) {
+ if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
this_config.toString(),
&loaded_package->GetPackageName()});
@@ -749,36 +798,35 @@
}
if (UNLIKELY(best_cookie == kInvalidCookie)) {
- return kInvalidCookie;
+ return base::unexpected(std::nullopt);
}
- const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
- if (UNLIKELY(best_entry == nullptr)) {
- return kInvalidCookie;
+ auto best_entry_result = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
+ if (!best_entry_result.has_value()) {
+ return base::unexpected(best_entry_result.error());
}
- const uint16_t entry_size = dtohs(best_entry->size);
- if (entry_size >= sizeof(ResTable_map_entry) &&
- (dtohs(best_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
- // The entry represents a bag/map.
- out_entry->entry = reinterpret_cast<const ResTable_map_entry*>(best_entry);
- } else {
- // The entry represents a value.
- Res_value value;
- value.copyFrom_dtoh(*reinterpret_cast<const Res_value*>(
- reinterpret_cast<const uint8_t*>(best_entry) + entry_size));
- out_entry->entry = value;
+ const incfs::map_ptr<ResTable_entry> best_entry = *best_entry_result;
+ if (!best_entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
}
- out_entry->config = *best_config;
- out_entry->type_flags = type_flags;
- out_entry->package_name = &best_package->GetPackageName();
- out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
- out_entry->entry_string_ref =
- StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
- out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get();
+ const auto entry = GetEntryValue(best_entry.verified());
+ if (!entry.has_value()) {
+ return base::unexpected(entry.error());
+ }
- return best_cookie;
+ return FindEntryResult{
+ .cookie = best_cookie,
+ .entry = *entry,
+ .config = *best_config,
+ .type_flags = type_flags,
+ .package_name = &best_package->GetPackageName(),
+ .type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1),
+ .entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(),
+ best_entry->key.index),
+ .dynamic_ref_table = package_group.dynamic_ref_table.get(),
+ };
}
void AssetManager2::ResetResourceResolution() const {
@@ -799,30 +847,28 @@
std::string AssetManager2::GetLastResourceResolution() const {
if (!resource_resolution_logging_enabled_) {
LOG(ERROR) << "Must enable resource resolution logging before getting path.";
- return std::string();
+ return {};
}
auto cookie = last_resolution_.cookie;
if (cookie == kInvalidCookie) {
LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
- return std::string();
+ return {};
}
uint32_t resid = last_resolution_.resid;
std::vector<Resolution::Step>& steps = last_resolution_.steps;
-
- ResourceName resource_name;
std::string resource_name_string;
const LoadedPackage* package =
apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
if (package != nullptr) {
- ToResourceName(last_resolution_.type_string_ref,
- last_resolution_.entry_string_ref,
- package->GetPackageName(),
- &resource_name);
- resource_name_string = ToFormattedResourceString(&resource_name);
+ auto resource_name = ToResourceName(last_resolution_.type_string_ref,
+ last_resolution_.entry_string_ref,
+ package->GetPackageName());
+ resource_name_string = resource_name.has_value() ?
+ ToFormattedResourceString(resource_name.value()) : "<unknown>";
}
std::stringstream log_stream;
@@ -875,200 +921,208 @@
return log_stream.str();
}
-bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
- FindEntryResult entry;
- ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
- true /* stop_at_first_match */,
- true /* ignore_configuration */, &entry);
- if (cookie == kInvalidCookie) {
- return false;
+base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetResourceName(
+ uint32_t resid) const {
+ auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */,
+ true /* ignore_configuration */);
+ if (!result.has_value()) {
+ return base::unexpected(result.error());
}
- return ToResourceName(entry.type_string_ref,
- entry.entry_string_ref,
- *entry.package_name,
- out_name);
+ return ToResourceName(result->type_string_ref,
+ result->entry_string_ref,
+ *result->package_name);
}
-bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
- FindEntryResult entry;
- ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
- false /* stop_at_first_match */,
- true /* ignore_configuration */, &entry);
- if (cookie != kInvalidCookie) {
- *out_flags = entry.type_flags;
- return true;
- }
- return false;
-}
-
-ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
- uint16_t density_override, Res_value* out_value,
- ResTable_config* out_selected_config,
- uint32_t* out_flags) const {
- FindEntryResult entry;
- ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */,
- false /* ignore_configuration */, &entry);
- if (cookie == kInvalidCookie) {
- return kInvalidCookie;
+base::expected<AssetManager2::SelectedValue, NullOrIOError> AssetManager2::GetResource(
+ uint32_t resid, bool may_be_bag, uint16_t density_override) const {
+ auto result = FindEntry(resid, density_override, false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (!result.has_value()) {
+ return base::unexpected(result.error());
}
- auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
+ auto result_map_entry = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&result->entry);
if (result_map_entry != nullptr) {
if (!may_be_bag) {
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
- return kInvalidCookie;
+ return base::unexpected(std::nullopt);
}
// Create a reference since we can't represent this complex type as a Res_value.
- out_value->dataType = Res_value::TYPE_REFERENCE;
- out_value->data = resid;
- *out_selected_config = entry.config;
- *out_flags = entry.type_flags;
- return cookie;
+ return SelectedValue(Res_value::TYPE_REFERENCE, resid, result->cookie, result->type_flags,
+ resid, result->config);
}
// Convert the package ID to the runtime assigned package ID.
- *out_value = std::get<Res_value>(entry.entry);
- entry.dynamic_ref_table->lookupResourceValue(out_value);
+ Res_value value = std::get<Res_value>(result->entry);
+ result->dynamic_ref_table->lookupResourceValue(&value);
- *out_selected_config = entry.config;
- *out_flags = entry.type_flags;
- return cookie;
+ return SelectedValue(value.dataType, value.data, result->cookie, result->type_flags,
+ resid, result->config);
}
-ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
- ResTable_config* in_out_selected_config,
- uint32_t* in_out_flags,
- uint32_t* out_last_reference) const {
- constexpr const int kMaxIterations = 20;
+base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference(
+ AssetManager2::SelectedValue& value, bool cache_value) const {
+ if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) {
+ // Not a reference. Nothing to do.
+ return {};
+ }
- for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
- in_out_value->data != 0u && iteration < kMaxIterations;
- iteration++) {
- *out_last_reference = in_out_value->data;
- uint32_t new_flags = 0u;
- cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/,
- in_out_value, in_out_selected_config, &new_flags);
- if (cookie == kInvalidCookie) {
- return kInvalidCookie;
- }
- if (in_out_flags != nullptr) {
- *in_out_flags |= new_flags;
- }
- if (*out_last_reference == in_out_value->data) {
- // This reference can't be resolved, so exit now and let the caller deal with it.
- return cookie;
+ const uint32_t original_flags = value.flags;
+ const uint32_t original_resid = value.data;
+ if (cache_value) {
+ auto cached_value = cached_resolved_values_.find(value.data);
+ if (cached_value != cached_resolved_values_.end()) {
+ value = cached_value->second;
+ value.flags |= original_flags;
+ return {};
}
}
- return cookie;
+
+ uint32_t combined_flags = 0U;
+ uint32_t resolve_resid = original_resid;
+ constexpr const uint32_t kMaxIterations = 20;
+ for (uint32_t i = 0U;; i++) {
+ auto result = GetResource(resolve_resid, true /*may_be_bag*/);
+ if (!result.has_value()) {
+ value.resid = resolve_resid;
+ return base::unexpected(result.error());
+ }
+
+ // If resource resolution fails, the value should be set to the last reference that was able to
+ // be resolved successfully.
+ value = *result;
+ value.flags |= combined_flags;
+
+ if (result->type != Res_value::TYPE_REFERENCE ||
+ result->data == Res_value::DATA_NULL_UNDEFINED ||
+ result->data == resolve_resid || i == kMaxIterations) {
+ // This reference can't be resolved, so exit now and let the caller deal with it.
+ if (cache_value) {
+ cached_resolved_values_[original_resid] = value;
+ }
+
+ // Above value is cached without original_flags to ensure they don't get included in future
+ // queries that hit the cache
+ value.flags |= original_flags;
+ return {};
+ }
+
+ combined_flags = result->flags;
+ resolve_resid = result->data;
+ }
}
-const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) {
+const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) const {
auto cached_iter = cached_bag_resid_stacks_.find(resid);
if (cached_iter != cached_bag_resid_stacks_.end()) {
return cached_iter->second;
- } else {
- auto found_resids = std::vector<uint32_t>();
- GetBag(resid, found_resids);
- // Cache style stacks if they are not already cached.
- cached_bag_resid_stacks_[resid] = found_resids;
- return found_resids;
}
+
+ std::vector<uint32_t> found_resids;
+ GetBag(resid, found_resids);
+ cached_bag_resid_stacks_.emplace(resid, found_resids);
+ return found_resids;
}
-const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
- auto found_resids = std::vector<uint32_t>();
- auto bag = GetBag(resid, found_resids);
+base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag(
+ AssetManager2::SelectedValue& value) const {
+ if (UNLIKELY(value.type != Res_value::TYPE_REFERENCE)) {
+ return base::unexpected(std::nullopt);
+ }
- // Cache style stacks if they are not already cached.
- auto cached_iter = cached_bag_resid_stacks_.find(resid);
- if (cached_iter == cached_bag_resid_stacks_.end()) {
- cached_bag_resid_stacks_[resid] = found_resids;
+ auto bag = GetBag(value.data);
+ if (bag.has_value()) {
+ value.flags |= (*bag)->type_spec_flags;
}
return bag;
}
-static bool compare_bag_entries(const ResolvedBag::Entry& entry1,
- const ResolvedBag::Entry& entry2) {
- return entry1.key < entry2.key;
+base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const {
+ std::vector<uint32_t> found_resids;
+ const auto bag = GetBag(resid, found_resids);
+ cached_bag_resid_stacks_.emplace(resid, found_resids);
+ return bag;
}
-const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) {
- auto cached_iter = cached_bags_.find(resid);
- if (cached_iter != cached_bags_.end()) {
+base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(
+ uint32_t resid, std::vector<uint32_t>& child_resids) const {
+ if (auto cached_iter = cached_bags_.find(resid); cached_iter != cached_bags_.end()) {
return cached_iter->second.get();
}
- FindEntryResult entry;
- ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
- false /* stop_at_first_match */,
- false /* ignore_configuration */,
- &entry);
- if (cookie == kInvalidCookie) {
- return nullptr;
+ auto entry = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (!entry.has_value()) {
+ return base::unexpected(entry.error());
}
- auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
- if (result_map_entry == nullptr) {
+ auto entry_map = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&entry->entry);
+ if (entry_map == nullptr) {
// Not a bag, nothing to do.
- return nullptr;
+ return base::unexpected(std::nullopt);
}
- auto map = reinterpret_cast<const ResTable_map_entry*>(*result_map_entry);
- auto map_entry = reinterpret_cast<const ResTable_map*>(
- reinterpret_cast<const uint8_t*>(map) + map->size);
- const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
+ auto map = *entry_map;
+ auto map_entry = map.offset(dtohs(map->size)).convert<ResTable_map>();
+ const auto map_entry_end = map_entry + dtohl(map->count);
// Keep track of ids that have already been seen to prevent infinite loops caused by circular
- // dependencies between bags
+ // dependencies between bags.
child_resids.push_back(resid);
uint32_t parent_resid = dtohl(map->parent.ident);
- if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid)
- != child_resids.end()) {
- // There is no parent or a circular dependency exist, meaning there is nothing to inherit and
- // we can do a simple copy of the entries in the map.
+ if (parent_resid == 0U ||
+ std::find(child_resids.begin(), child_resids.end(), parent_resid) != child_resids.end()) {
+ // There is no parent or a circular parental dependency exist, meaning there is nothing to
+ // inherit and we can do a simple copy of the entries in the map.
const size_t entry_count = map_entry_end - map_entry;
util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
bool sort_entries = false;
- ResolvedBag::Entry* new_entry = new_bag->entries;
- for (; map_entry != map_entry_end; ++map_entry) {
+ for (auto new_entry = new_bag->entries; map_entry != map_entry_end; ++map_entry) {
+ if (UNLIKELY(!map_entry)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
uint32_t new_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(new_key)) {
// Attributes, arrays, etc don't have a resource id as the name. They specify
// other data, which would be wrong to change via a lookup.
- if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
+ if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
resid);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
}
- new_entry->cookie = cookie;
+
+ new_entry->cookie = entry->cookie;
new_entry->key = new_key;
new_entry->key_pool = nullptr;
new_entry->type_pool = nullptr;
new_entry->style = resid;
new_entry->value.copyFrom_dtoh(map_entry->value);
- status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value);
- if (err != NO_ERROR) {
+ status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
+ if (UNLIKELY(err != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf(
"Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType,
new_entry->value.data, new_key);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
+
sort_entries = sort_entries ||
(new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
++new_entry;
}
if (sort_entries) {
- std::sort(new_bag->entries, new_bag->entries + entry_count, compare_bag_entries);
+ std::sort(new_bag->entries, new_bag->entries + entry_count,
+ [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; });
}
- new_bag->type_spec_flags = entry.type_flags;
+ new_bag->type_spec_flags = entry->type_flags;
new_bag->entry_count = static_cast<uint32_t>(entry_count);
ResolvedBag* result = new_bag.get();
cached_bags_[resid] = std::move(new_bag);
@@ -1076,54 +1130,58 @@
}
// In case the parent is a dynamic reference, resolve it.
- entry.dynamic_ref_table->lookupResourceId(&parent_resid);
+ entry->dynamic_ref_table->lookupResourceId(&parent_resid);
// Get the parent and do a merge of the keys.
- const ResolvedBag* parent_bag = GetBag(parent_resid, child_resids);
- if (parent_bag == nullptr) {
+ const auto parent_bag = GetBag(parent_resid, child_resids);
+ if (UNLIKELY(!parent_bag.has_value())) {
// Failed to get the parent that should exist.
LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid,
resid);
- return nullptr;
+ return base::unexpected(parent_bag.error());
}
// Create the max possible entries we can make. Once we construct the bag,
// we will realloc to fit to size.
- const size_t max_count = parent_bag->entry_count + dtohl(map->count);
+ const size_t max_count = (*parent_bag)->entry_count + dtohl(map->count);
util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))};
ResolvedBag::Entry* new_entry = new_bag->entries;
- const ResolvedBag::Entry* parent_entry = parent_bag->entries;
- const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
+ const ResolvedBag::Entry* parent_entry = (*parent_bag)->entries;
+ const ResolvedBag::Entry* const parent_entry_end = parent_entry + (*parent_bag)->entry_count;
// The keys are expected to be in sorted order. Merge the two bags.
bool sort_entries = false;
while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
+ if (UNLIKELY(!map_entry)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
uint32_t child_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(child_key)) {
- if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) {
+ if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key,
resid);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
}
if (child_key <= parent_entry->key) {
// Use the child key if it comes before the parent
// or is equal to the parent (overrides).
- new_entry->cookie = cookie;
+ new_entry->cookie = entry->cookie;
new_entry->key = child_key;
new_entry->key_pool = nullptr;
new_entry->type_pool = nullptr;
new_entry->value.copyFrom_dtoh(map_entry->value);
new_entry->style = resid;
- status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value);
- if (err != NO_ERROR) {
+ status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
+ if (UNLIKELY(err != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf(
"Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType,
new_entry->value.data, child_key);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
++map_entry;
} else {
@@ -1143,25 +1201,29 @@
// Finish the child entries if they exist.
while (map_entry != map_entry_end) {
+ if (UNLIKELY(!map_entry)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
uint32_t new_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(new_key)) {
- if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
+ if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
resid);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
}
- new_entry->cookie = cookie;
+ new_entry->cookie = entry->cookie;
new_entry->key = new_key;
new_entry->key_pool = nullptr;
new_entry->type_pool = nullptr;
new_entry->value.copyFrom_dtoh(map_entry->value);
new_entry->style = resid;
- status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value);
- if (err != NO_ERROR) {
+ status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
+ if (UNLIKELY(err != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.",
new_entry->value.dataType, new_entry->value.data, new_key);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
sort_entries = sort_entries ||
(new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
@@ -1185,11 +1247,12 @@
}
if (sort_entries) {
- std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries);
+ std::sort(new_bag->entries, new_bag->entries + actual_count,
+ [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; });
}
// Combine flags from the parent and our own bag.
- new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags;
+ new_bag->type_spec_flags = entry->type_flags | (*parent_bag)->type_spec_flags;
new_bag->entry_count = static_cast<uint32_t>(actual_count);
ResolvedBag* result = new_bag.get();
cached_bags_[resid] = std::move(new_bag);
@@ -1208,16 +1271,16 @@
return true;
}
-uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
- const std::string& fallback_type,
- const std::string& fallback_package) const {
+base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId(
+ const std::string& resource_name, const std::string& fallback_type,
+ const std::string& fallback_package) const {
StringPiece package_name, type, entry;
if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) {
- return 0u;
+ return base::unexpected(std::nullopt);
}
if (entry.empty()) {
- return 0u;
+ return base::unexpected(std::nullopt);
}
if (package_name.empty()) {
@@ -1230,12 +1293,12 @@
std::u16string type16;
if (!Utf8ToUtf16(type, &type16)) {
- return 0u;
+ return base::unexpected(std::nullopt);
}
std::u16string entry16;
if (!Utf8ToUtf16(entry, &entry16)) {
- return 0u;
+ return base::unexpected(std::nullopt);
}
const StringPiece16 kAttr16 = u"attr";
@@ -1249,20 +1312,24 @@
break;
}
- uint32_t resid = package->FindEntryByName(type16, entry16);
- if (resid == 0u && kAttr16 == type16) {
+ base::expected<uint32_t, NullOrIOError> resid = package->FindEntryByName(type16, entry16);
+ if (UNLIKELY(IsIOError(resid))) {
+ return base::unexpected(resid.error());
+ }
+
+ if (!resid.has_value() && kAttr16 == type16) {
// Private attributes in libraries (such as the framework) are sometimes encoded
// under the type '^attr-private' in order to leave the ID space of public 'attr'
// free for future additions. Check '^attr-private' for the same name.
resid = package->FindEntryByName(kAttrPrivate16, entry16);
}
- if (resid != 0u) {
- return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId);
+ if (resid.has_value()) {
+ return fix_package_id(*resid, package_group.dynamic_ref_table->mAssignedPackageId);
}
}
}
- return 0u;
+ return base::unexpected(std::nullopt);
}
void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) {
@@ -1282,8 +1349,7 @@
ResTable_config this_config;
this_config.copyFromDtoH((*iter)->config);
if (!filter_incompatible_configs || this_config.match(configuration_)) {
- group.configurations.push_back(this_config);
- group.types.push_back(*iter);
+ group.type_configs.push_back(TypeConfig{*iter, this_config});
}
}
});
@@ -1309,6 +1375,8 @@
++iter;
}
}
+
+ cached_resolved_values_.clear();
}
uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const {
@@ -1354,16 +1422,16 @@
std::array<util::unique_cptr<ThemeType>, kTypeCount> types;
};
-bool Theme::ApplyStyle(uint32_t resid, bool force) {
+base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, bool force) {
ATRACE_NAME("Theme::ApplyStyle");
- const ResolvedBag* bag = asset_manager_->GetBag(resid);
- if (bag == nullptr) {
- return false;
+ auto bag = asset_manager_->GetBag(resid);
+ if (!bag.has_value()) {
+ return base::unexpected(bag.error());
}
// Merge the flags from this style.
- type_spec_flags_ |= bag->type_spec_flags;
+ type_spec_flags_ |= (*bag)->type_spec_flags;
int last_type_idx = -1;
int last_package_idx = -1;
@@ -1373,14 +1441,14 @@
// Iterate backwards, because each bag is sorted in ascending key ID order, meaning we will only
// need to perform one resize per type.
using reverse_bag_iterator = std::reverse_iterator<const ResolvedBag::Entry*>;
- const auto bag_iter_end = reverse_bag_iterator(begin(bag));
- for (auto bag_iter = reverse_bag_iterator(end(bag)); bag_iter != bag_iter_end; ++bag_iter) {
- const uint32_t attr_resid = bag_iter->key;
+ const auto rbegin = reverse_bag_iterator(begin(*bag));
+ for (auto it = reverse_bag_iterator(end(*bag)); it != rbegin; ++it) {
+ const uint32_t attr_resid = it->key;
// If the resource ID passed in is not a style, the key can be some other identifier that is not
// a resource ID. We should fail fast instead of operating with strange resource IDs.
if (!is_valid_resid(attr_resid)) {
- return false;
+ return base::unexpected(std::nullopt);
}
// We don't use the 0-based index for the type so that we can avoid doing ID validation
@@ -1428,20 +1496,18 @@
ThemeEntry& entry = last_type->entries[entry_idx];
if (force || (entry.value.dataType == Res_value::TYPE_NULL &&
entry.value.data != Res_value::DATA_NULL_EMPTY)) {
- entry.cookie = bag_iter->cookie;
- entry.type_spec_flags |= bag->type_spec_flags;
- entry.value = bag_iter->value;
+ entry.cookie = it->cookie;
+ entry.type_spec_flags |= (*bag)->type_spec_flags;
+ entry.value = it->value;
}
}
- return true;
+ return {};
}
-ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
- uint32_t* out_flags) const {
+std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) const {
+
int cnt = 20;
-
uint32_t type_spec_flags = 0u;
-
do {
const int package_idx = get_package_id(resid);
const Package* package = packages_[package_idx].get();
@@ -1461,43 +1527,42 @@
resid = entry.value.data;
continue;
}
- return kInvalidCookie;
+ return std::nullopt;
}
// @null is different than @empty.
if (entry.value.dataType == Res_value::TYPE_NULL &&
entry.value.data != Res_value::DATA_NULL_EMPTY) {
- return kInvalidCookie;
+ return std::nullopt;
}
- *out_value = entry.value;
- *out_flags = type_spec_flags;
- return entry.cookie;
+ return AssetManager2::SelectedValue(entry.value.dataType, entry.value.data, entry.cookie,
+ type_spec_flags, 0U /* resid */, {} /* config */);
}
}
}
break;
} while (true);
- return kInvalidCookie;
+ return std::nullopt;
}
-ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
- ResTable_config* in_out_selected_config,
- uint32_t* in_out_type_spec_flags,
- uint32_t* out_last_ref) const {
- if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) {
- uint32_t new_flags;
- cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags);
- if (cookie == kInvalidCookie) {
- return kInvalidCookie;
- }
-
- if (in_out_type_spec_flags != nullptr) {
- *in_out_type_spec_flags |= new_flags;
- }
+base::expected<std::monostate, NullOrIOError> Theme::ResolveAttributeReference(
+ AssetManager2::SelectedValue& value) const {
+ if (value.type != Res_value::TYPE_ATTRIBUTE) {
+ return asset_manager_->ResolveReference(value);
}
- return asset_manager_->ResolveReference(cookie, in_out_value, in_out_selected_config,
- in_out_type_spec_flags, out_last_ref);
+
+ std::optional<AssetManager2::SelectedValue> result = GetAttribute(value.data);
+ if (!result.has_value()) {
+ return base::unexpected(std::nullopt);
+ }
+
+ auto resolve_result = asset_manager_->ResolveReference(*result, true /* cache_value */);
+ if (resolve_result.has_value()) {
+ result->flags |= value.flags;
+ value = *result;
+ }
+ return resolve_result;
}
void Theme::Clear() {
@@ -1507,9 +1572,9 @@
}
}
-void Theme::SetTo(const Theme& o) {
+base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) {
if (this == &o) {
- return;
+ return {};
}
type_spec_flags_ = o.type_spec_flags_;
@@ -1560,10 +1625,8 @@
// Map the runtime package of the source apk asset to the destination apk asset.
if (src_asset->GetPath() == dest_asset->GetPath()) {
- const std::vector<std::unique_ptr<const LoadedPackage>>& src_packages =
- src_asset->GetLoadedArsc()->GetPackages();
- const std::vector<std::unique_ptr<const LoadedPackage>>& dest_packages =
- dest_asset->GetLoadedArsc()->GetPackages();
+ const auto& src_packages = src_asset->GetLoadedArsc()->GetPackages();
+ const auto& dest_packages = dest_asset->GetLoadedArsc()->GetPackages();
SourceToDestinationRuntimePackageMap package_map;
@@ -1660,15 +1723,20 @@
int attribute_dest_package_id = p;
if (attribute_dest_package_id != 0x01) {
// Find the cookie of the attribute resource id in the source AssetManager
- FindEntryResult attribute_entry_result;
- ApkAssetsCookie attribute_cookie =
+ base::expected<FindEntryResult, NullOrIOError> attribute_entry_result =
o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ ,
true /* stop_at_first_match */,
- true /* ignore_configuration */,
- &attribute_entry_result);
+ true /* ignore_configuration */);
+ if (UNLIKELY(IsIOError(attribute_entry_result))) {
+ return base::unexpected(GetIOError(attribute_entry_result.error()));
+ }
+ if (!attribute_entry_result.has_value()) {
+ continue;
+ }
// Determine the package id of the attribute in the destination AssetManager.
- auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie);
+ auto attribute_package_map = src_asset_cookie_id_map.find(
+ attribute_entry_result->cookie);
if (attribute_package_map == src_asset_cookie_id_map.end()) {
continue;
}
@@ -1712,6 +1780,7 @@
}
}
}
+ return {};
}
void Theme::Dump() const {
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index e62fb61..c188712 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -24,9 +24,12 @@
#include "androidfw/AttributeFinder.h"
constexpr bool kDebugStyles = false;
+#define DEBUG_LOG(...) do { if (kDebugStyles) { ALOGI(__VA_ARGS__); } } while(0)
namespace android {
+namespace {
+
// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1);
@@ -36,8 +39,7 @@
: public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
public:
explicit XmlAttributeFinder(const ResXMLParser* parser)
- : BackTrackingAttributeFinder(
- 0, parser != nullptr ? parser->getAttributeCount() : 0),
+ : BackTrackingAttributeFinder(0, parser != nullptr ? parser->getAttributeCount() : 0),
parser_(parser) {}
inline uint32_t GetAttribute(size_t index) const {
@@ -61,136 +63,149 @@
}
};
-bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
- uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
- if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme,
- def_style_attr, def_style_res);
- }
-
- AssetManager2* assetmanager = theme->GetAssetManager();
- ResTable_config config;
- Res_value value;
-
- int indices_idx = 0;
-
- // Load default style from attribute, if specified...
- uint32_t def_style_flags = 0u;
- if (def_style_attr != 0) {
- Res_value value;
- if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- def_style_res = value.data;
+base::expected<const ResolvedBag*, NullOrIOError> GetStyleBag(Theme* theme,
+ uint32_t theme_attribute_resid,
+ uint32_t fallback_resid,
+ uint32_t* out_theme_flags) {
+ // Load the style from the attribute if specified.
+ if (theme_attribute_resid != 0U) {
+ std::optional<AssetManager2::SelectedValue> value = theme->GetAttribute(theme_attribute_resid);
+ if (value.has_value()) {
+ *out_theme_flags |= value->flags;
+ auto result = theme->GetAssetManager()->ResolveBag(*value);
+ if (result.has_value() || IsIOError(result)) {
+ return result;
}
}
}
- // Retrieve the default style bag, if requested.
- const ResolvedBag* default_style_bag = nullptr;
- if (def_style_res != 0) {
- default_style_bag = assetmanager->GetBag(def_style_res);
- if (default_style_bag != nullptr) {
- def_style_flags |= default_style_bag->type_spec_flags;
+ // Fallback to loading the style from the resource id if specified.
+ if (fallback_resid != 0U) {
+ return theme->GetAssetManager()->GetBag(fallback_resid);
+ }
+
+ return base::unexpected(std::nullopt);
+}
+
+base::expected<const ResolvedBag*, NullOrIOError> GetXmlStyleBag(Theme* theme,
+ ResXMLParser* xml_parser,
+ uint32_t* out_theme_flags) {
+ if (xml_parser == nullptr) {
+ return base::unexpected(std::nullopt);
+ }
+
+ // Retrieve the style resource ID associated with the current XML tag's style attribute.
+ Res_value value;
+ const ssize_t idx = xml_parser->indexOfStyle();
+ if (idx < 0 || xml_parser->getAttributeValue(idx, &value) < 0) {
+ return base::unexpected(std::nullopt);
+ }
+
+ if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
+ // Resolve the attribute with out theme.
+ if (std::optional<AssetManager2::SelectedValue> result = theme->GetAttribute(value.data)) {
+ *out_theme_flags |= result->flags;
+ return theme->GetAssetManager()->ResolveBag(*result);
}
}
- BagAttributeFinder def_style_attr_finder(default_style_bag);
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ return theme->GetAssetManager()->GetBag(value.data);
+ }
+
+ return base::unexpected(std::nullopt);
+}
+
+} // namespace
+
+base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_style_attr,
+ uint32_t def_style_res, uint32_t* src_values,
+ size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values,
+ uint32_t* out_indices) {
+ DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr,
+ def_style_res);
+
+ int indices_idx = 0;
+ const AssetManager2* assetmanager = theme->GetAssetManager();
+
+ // Load default style from attribute or resource id, if specified...
+ uint32_t def_style_theme_flags = 0U;
+ const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_res,
+ &def_style_theme_flags);
+ if (UNLIKELY(IsIOError(default_style_bag))) {
+ return base::unexpected(GetIOError(default_style_bag.error()));
+ }
+
+ BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr));
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
-
- if (kDebugStyles) {
- ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
- }
-
- ApkAssetsCookie cookie = kInvalidCookie;
- uint32_t type_set_flags = 0;
-
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- config.density = 0;
+ DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident);
// Try to find a value for this attribute... we prioritize values
// coming from, first XML attributes, then XML style, then default
// style, and finally the theme.
// Retrieve the current input value if available.
+ AssetManager2::SelectedValue value{};
if (src_values_length > 0 && src_values[ii] != 0) {
- value.dataType = Res_value::TYPE_ATTRIBUTE;
+ value.type = Res_value::TYPE_ATTRIBUTE;
value.data = src_values[ii];
- if (kDebugStyles) {
- ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
+ DEBUG_LOG("-> From values: type=0x%x, data=0x%08x", value.type, value.data);
} else {
const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident);
if (entry != def_style_attr_finder.end()) {
- cookie = entry->cookie;
- type_set_flags = def_style_flags;
- value = entry->value;
- if (kDebugStyles) {
- ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
+ value = AssetManager2::SelectedValue(*default_style_bag, *entry);
+ value.flags |= def_style_theme_flags;
+ DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x", value.type, value.data);
}
}
- uint32_t resid = 0;
- if (value.dataType != Res_value::TYPE_NULL) {
+ if (value.type != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ApkAssetsCookie new_cookie =
- theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
+ const auto result = theme->ResolveAttributeReference(value);
+ if (UNLIKELY(IsIOError(result))) {
+ return base::unexpected(GetIOError(result.error()));
}
- if (kDebugStyles) {
- ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
+ DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data);
} else if (value.data != Res_value::DATA_NULL_EMPTY) {
// If we still don't have a value for this attribute, try to find it in the theme!
- ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
- if (new_cookie != kInvalidCookie) {
- if (kDebugStyles) {
- ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
+ if (auto attr_value = theme->GetAttribute(cur_ident)) {
+ value = *attr_value;
+ DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data);
+
+ const auto result = assetmanager->ResolveReference(value, true /* cache_value */);
+ if (UNLIKELY(IsIOError(result))) {
+ return base::unexpected(GetIOError(result.error()));
}
- new_cookie =
- assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
- }
- if (kDebugStyles) {
- ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
+ DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data);
}
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- if (kDebugStyles) {
- ALOGI("-> Setting to @null!");
- }
- value.dataType = Res_value::TYPE_NULL;
+ if (value.type == Res_value::TYPE_REFERENCE && value.data == 0) {
+ DEBUG_LOG("-> Setting to @null!");
+ value.type = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- cookie = kInvalidCookie;
+ value.cookie = kInvalidCookie;
}
- if (kDebugStyles) {
- ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
- }
+ DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data);
// Write the final value back to Java.
- out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_TYPE] = value.type;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
- out_values[STYLE_RESOURCE_ID] = resid;
- out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
- out_values[STYLE_DENSITY] = config.density;
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie);
+ out_values[STYLE_RESOURCE_ID] = value.resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags;
+ out_values[STYLE_DENSITY] = value.config.density;
if (out_indices != nullptr &&
- (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
- indices_idx++;
- out_indices[indices_idx] = ii;
+ (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
+ out_indices[++indices_idx] = ii;
}
out_values += STYLE_NUM_ENTRIES;
@@ -199,93 +214,46 @@
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}
- return true;
+ return {};
}
-void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
- uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
- uint32_t* out_values, uint32_t* out_indices) {
- if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
- def_style_attr, def_style_resid, xml_parser);
- }
-
- AssetManager2* assetmanager = theme->GetAssetManager();
- ResTable_config config;
- Res_value value;
+base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* xml_parser,
+ uint32_t def_style_attr,
+ uint32_t def_style_resid,
+ const uint32_t* attrs, size_t attrs_length,
+ uint32_t* out_values, uint32_t* out_indices) {
+ DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
+ def_style_attr, def_style_resid, xml_parser);
int indices_idx = 0;
+ const AssetManager2* assetmanager = theme->GetAssetManager();
// Load default style from attribute, if specified...
- uint32_t def_style_flags = 0u;
- if (def_style_attr != 0) {
- Res_value value;
- if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- def_style_resid = value.data;
- }
- }
+ uint32_t def_style_theme_flags = 0U;
+ const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_resid,
+ &def_style_theme_flags);
+ if (IsIOError(default_style_bag)) {
+ return base::unexpected(GetIOError(default_style_bag.error()));
}
// Retrieve the style resource ID associated with the current XML tag's style attribute.
- uint32_t style_resid = 0u;
- uint32_t style_flags = 0u;
- if (xml_parser != nullptr) {
- ssize_t idx = xml_parser->indexOfStyle();
- if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
- if (value.dataType == value.TYPE_ATTRIBUTE) {
- // Resolve the attribute with out theme.
- if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) {
- value.dataType = Res_value::TYPE_NULL;
- }
- }
-
- if (value.dataType == value.TYPE_REFERENCE) {
- style_resid = value.data;
- }
- }
+ uint32_t xml_style_theme_flags = 0U;
+ const auto xml_style_bag = GetXmlStyleBag(theme, xml_parser, &def_style_theme_flags);
+ if (IsIOError(xml_style_bag)) {
+ return base::unexpected(GetIOError(xml_style_bag.error()));
}
- // Retrieve the default style bag, if requested.
- const ResolvedBag* default_style_bag = nullptr;
- if (def_style_resid != 0) {
- default_style_bag = assetmanager->GetBag(def_style_resid);
- if (default_style_bag != nullptr) {
- def_style_flags |= default_style_bag->type_spec_flags;
- }
- }
-
- BagAttributeFinder def_style_attr_finder(default_style_bag);
-
- // Retrieve the style class bag, if requested.
- const ResolvedBag* xml_style_bag = nullptr;
- if (style_resid != 0) {
- xml_style_bag = assetmanager->GetBag(style_resid);
- if (xml_style_bag != nullptr) {
- style_flags |= xml_style_bag->type_spec_flags;
- }
- }
-
- BagAttributeFinder xml_style_attr_finder(xml_style_bag);
-
- // Retrieve the XML attributes, if requested.
+ BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr));
+ BagAttributeFinder xml_style_attr_finder(xml_style_bag.value_or(nullptr));
XmlAttributeFinder xml_attr_finder(xml_parser);
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
+ DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident);
- if (kDebugStyles) {
- ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
- }
-
- ApkAssetsCookie cookie = kInvalidCookie;
- uint32_t type_set_flags = 0u;
-
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- config.density = 0;
+ AssetManager2::SelectedValue value{};
uint32_t value_source_resid = 0;
// Try to find a value for this attribute... we prioritize values
@@ -296,178 +264,152 @@
const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
if (xml_attr_idx != xml_attr_finder.end()) {
// We found the attribute we were looking for.
- xml_parser->getAttributeValue(xml_attr_idx, &value);
- if (kDebugStyles) {
- ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
+ Res_value attribute_value{};
+ xml_parser->getAttributeValue(xml_attr_idx, &attribute_value);
+ value.type = attribute_value.dataType;
+ value.data = attribute_value.data;
value_source_resid = xml_parser->getSourceResourceId();
+ DEBUG_LOG("-> From XML: type=0x%x, data=0x%08x", value.type, value.data);
}
- if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
+ if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
// Walk through the style class values looking for the requested attribute.
const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident);
if (entry != xml_style_attr_finder.end()) {
- // We found the attribute we were looking for.
- cookie = entry->cookie;
- type_set_flags = style_flags;
- value = entry->value;
+ value = AssetManager2::SelectedValue(*xml_style_bag, *entry);
+ value.flags |= xml_style_theme_flags;
value_source_resid = entry->style;
- if (kDebugStyles) {
- ALOGI("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data,
- entry->style);
- }
+ DEBUG_LOG("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data,
+ value_source_resid);
}
}
- if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
+ if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
// Walk through the default style values looking for the requested attribute.
const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident);
if (entry != def_style_attr_finder.end()) {
- // We found the attribute we were looking for.
- cookie = entry->cookie;
- type_set_flags = def_style_flags;
- value = entry->value;
- if (kDebugStyles) {
- ALOGI("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data,
- entry->style);
- }
+ value = AssetManager2::SelectedValue(*default_style_bag, *entry);
+ value.flags |= def_style_theme_flags;
value_source_resid = entry->style;
+ DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data,
+ entry->style);
}
}
- uint32_t resid = 0u;
- if (value.dataType != Res_value::TYPE_NULL) {
+ if (value.type != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ApkAssetsCookie new_cookie =
- theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
+ auto result = theme->ResolveAttributeReference(value);
+ if (UNLIKELY(IsIOError(result))) {
+ return base::unexpected(GetIOError(result.error()));
}
-
- if (kDebugStyles) {
- ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
+ DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data);
} else if (value.data != Res_value::DATA_NULL_EMPTY) {
// If we still don't have a value for this attribute, try to find it in the theme!
- ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
- // TODO: set value_source_resid for the style in the theme that was used.
- if (new_cookie != kInvalidCookie) {
- if (kDebugStyles) {
- ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- new_cookie =
- assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
- }
+ if (auto attr_value = theme->GetAttribute(cur_ident)) {
+ value = *attr_value;
+ DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data);
- if (kDebugStyles) {
- ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
+ auto result = assetmanager->ResolveReference(value, true /* cache_value */);
+ if (UNLIKELY(IsIOError(result))) {
+ return base::unexpected(GetIOError(result.error()));
}
+ DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data);
+ // TODO: set value_source_resid for the style in the theme that was used.
}
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- if (kDebugStyles) {
- ALOGI("-> Setting to @null!");
- }
- value.dataType = Res_value::TYPE_NULL;
+ if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) {
+ DEBUG_LOG("-> Setting to @null!");
+ value.type = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- cookie = kInvalidCookie;
+ value.cookie = kInvalidCookie;
}
- if (kDebugStyles) {
- ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
- }
+ DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data);
// Write the final value back to Java.
- out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_TYPE] = value.type;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
- out_values[STYLE_RESOURCE_ID] = resid;
- out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
- out_values[STYLE_DENSITY] = config.density;
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie);
+ out_values[STYLE_RESOURCE_ID] = value.resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags;
+ out_values[STYLE_DENSITY] = value.config.density;
out_values[STYLE_SOURCE_RESOURCE_ID] = value_source_resid;
- if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
- indices_idx++;
-
+ if (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
// out_indices must NOT be nullptr.
- out_indices[indices_idx] = ii;
+ out_indices[++indices_idx] = ii;
}
out_values += STYLE_NUM_ENTRIES;
}
// out_indices must NOT be nullptr.
out_indices[0] = indices_idx;
+ return {};
}
-bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
- ResTable_config config;
- Res_value value;
-
+base::expected<std::monostate, IOError> RetrieveAttributes(AssetManager2* assetmanager,
+ ResXMLParser* xml_parser,
+ uint32_t* attrs,
+ size_t attrs_length,
+ uint32_t* out_values,
+ uint32_t* out_indices) {
int indices_idx = 0;
// Retrieve the XML attributes, if requested.
- const size_t xml_attr_count = xml_parser->getAttributeCount();
size_t ix = 0;
+ const size_t xml_attr_count = xml_parser->getAttributeCount();
uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
- ApkAssetsCookie cookie = kInvalidCookie;
- uint32_t type_set_flags = 0u;
-
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- config.density = 0;
+ AssetManager2::SelectedValue value{};
// Try to find a value for this attribute...
// Skip through XML attributes until the end or the next possible match.
while (ix < xml_attr_count && cur_ident > cur_xml_attr) {
- ix++;
- cur_xml_attr = xml_parser->getAttributeNameResID(ix);
- }
- // Retrieve the current XML attribute if it matches, and step to next.
- if (ix < xml_attr_count && cur_ident == cur_xml_attr) {
- xml_parser->getAttributeValue(ix, &value);
- ix++;
- cur_xml_attr = xml_parser->getAttributeNameResID(ix);
+ cur_xml_attr = xml_parser->getAttributeNameResID(++ix);
}
- uint32_t resid = 0u;
- if (value.dataType != Res_value::TYPE_NULL) {
+ // Retrieve the current XML attribute if it matches, and step to next.
+ if (ix < xml_attr_count && cur_ident == cur_xml_attr) {
+ Res_value attribute_value{};
+ xml_parser->getAttributeValue(ix, &attribute_value);
+ value.type = attribute_value.dataType;
+ value.data = attribute_value.data;
+ cur_xml_attr = xml_parser->getAttributeNameResID(++ix);
+ }
+
+ if (value.type != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ApkAssetsCookie new_cookie =
- assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
+ auto result = assetmanager->ResolveReference(value);
+ if (UNLIKELY(IsIOError(result))) {
+ return base::unexpected(GetIOError(result.error()));
}
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- value.dataType = Res_value::TYPE_NULL;
+ if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) {
+ value.type = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- cookie = kInvalidCookie;
+ value.cookie = kInvalidCookie;
}
// Write the final value back to Java.
- out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_TYPE] = value.type;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
- out_values[STYLE_RESOURCE_ID] = resid;
- out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
- out_values[STYLE_DENSITY] = config.density;
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie);
+ out_values[STYLE_RESOURCE_ID] = value.resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags;
+ out_values[STYLE_DENSITY] = value.config.density;
if (out_indices != nullptr &&
- (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
- indices_idx++;
- out_indices[indices_idx] = ii;
+ (value.type != Res_value::TYPE_NULL ||
+ value.data == Res_value::DATA_NULL_EMPTY)) {
+ out_indices[++indices_idx] = ii;
}
out_values += STYLE_NUM_ENTRIES;
@@ -476,7 +418,7 @@
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}
- return true;
+ return {};
}
} // namespace android
diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp
index 8fc3219..25c8aa6 100644
--- a/libs/androidfw/ChunkIterator.cpp
+++ b/libs/androidfw/ChunkIterator.cpp
@@ -15,6 +15,7 @@
*/
#include "androidfw/Chunk.h"
+#include "androidfw/Util.h"
#include "android-base/logging.h"
@@ -23,11 +24,11 @@
Chunk ChunkIterator::Next() {
CHECK(len_ != 0) << "called Next() after last chunk";
- const ResChunk_header* this_chunk = next_chunk_;
+ const incfs::map_ptr<ResChunk_header> this_chunk = next_chunk_;
+ CHECK((bool) this_chunk) << "Next() called without verifying next chunk";
// We've already checked the values of this_chunk, so safely increment.
- next_chunk_ = reinterpret_cast<const ResChunk_header*>(
- reinterpret_cast<const uint8_t*>(this_chunk) + dtohl(this_chunk->size));
+ next_chunk_ = this_chunk.offset(dtohl(this_chunk->size)).convert<ResChunk_header>();
len_ -= dtohl(this_chunk->size);
if (len_ != 0) {
@@ -36,7 +37,7 @@
VerifyNextChunk();
}
}
- return Chunk(this_chunk);
+ return Chunk(this_chunk.verified());
}
// TODO(b/111401637) remove this and have full resource file verification
@@ -47,6 +48,13 @@
last_error_was_fatal_ = false;
return false;
}
+
+ if (!next_chunk_) {
+ last_error_ = "failed to read chunk from data";
+ last_error_was_fatal_ = false;
+ return false;
+ }
+
const size_t size = dtohl(next_chunk_->size);
if (size > len_) {
last_error_ = "chunk size is bigger than given data";
@@ -58,12 +66,10 @@
// Returns false if there was an error.
bool ChunkIterator::VerifyNextChunk() {
- const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_);
-
// This data must be 4-byte aligned, since we directly
// access 32-bit words, which must be aligned on
// certain architectures.
- if (header_start & 0x03) {
+ if (!util::IsFourByteAligned(next_chunk_)) {
last_error_ = "header not aligned on 4-byte boundary";
return false;
}
@@ -73,6 +79,11 @@
return false;
}
+ if (!next_chunk_) {
+ last_error_ = "failed to read chunk from data";
+ return false;
+ }
+
const size_t header_size = dtohs(next_chunk_->headerSize);
const size_t size = dtohl(next_chunk_->size);
if (header_size < sizeof(ResChunk_header)) {
@@ -90,7 +101,7 @@
return false;
}
- if ((size | header_size) & 0x03) {
+ if ((size | header_size) & 0x03U) {
last_error_ = "header sizes are not aligned on 4-byte boundary";
return false;
}
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 4e03ce5..a613095 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -52,22 +52,22 @@
uninit();
}
-const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
+base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const {
const size_t offset = dtohl(data_header_->string_pool_index_offset);
if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
- return idmap_string_pool_->stringAt(idx - offset, outLen);
+ return idmap_string_pool_->stringAt(idx - offset);
}
- return ResStringPool::stringAt(idx, outLen);
+ return ResStringPool::stringAt(idx);
}
-const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
+base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const {
const size_t offset = dtohl(data_header_->string_pool_index_offset);
if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
- return idmap_string_pool_->string8At(idx - offset, outLen);
+ return idmap_string_pool_->string8At(idx - offset);
}
- return ResStringPool::string8At(idx, outLen);
+ return ResStringPool::string8At(idx);
}
size_t OverlayStringPool::size() const {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 70bb441..2fc3b05 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -38,7 +38,7 @@
#include "androidfw/ResourceUtils.h"
#include "androidfw/Util.h"
-using ::android::base::StringPrintf;
+using android::base::StringPrintf;
namespace android {
@@ -51,17 +51,17 @@
// the Type structs.
class TypeSpecPtrBuilder {
public:
- explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header)
+ explicit TypeSpecPtrBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header)
: header_(header) {
}
- void AddType(const ResTable_type* type) {
+ void AddType(incfs::verified_map_ptr<ResTable_type> type) {
types_.push_back(type);
}
TypeSpecPtr Build() {
// Check for overflow.
- using ElementType = const ResTable_type*;
+ using ElementType = incfs::verified_map_ptr<ResTable_type>;
if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) <
types_.size()) {
return {};
@@ -77,8 +77,8 @@
private:
DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
- const ResTable_typeSpec* header_;
- std::vector<const ResTable_type*> types_;
+ incfs::verified_map_ptr<ResTable_typeSpec> header_;
+ std::vector<incfs::verified_map_ptr<ResTable_type>> types_;
};
} // namespace
@@ -88,7 +88,7 @@
// Precondition: The header passed in has already been verified, so reading any fields and trusting
// the ResChunk_header is safe.
-static bool VerifyResTableType(const ResTable_type* header) {
+static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
if (header->id == 0) {
LOG(ERROR) << "RES_TABLE_TYPE_TYPE has invalid ID 0.";
return false;
@@ -115,89 +115,99 @@
return false;
}
- if (entries_offset & 0x03) {
+ if (entries_offset & 0x03U) {
LOG(ERROR) << "RES_TABLE_TYPE_TYPE entries start at unaligned address.";
return false;
}
return true;
}
-static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) {
+static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
+ incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) {
// Check that the offset is aligned.
- if (entry_offset & 0x03) {
+ if (UNLIKELY(entry_offset & 0x03U)) {
LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned.";
- return false;
+ return base::unexpected(std::nullopt);
}
// Check that the offset doesn't overflow.
- if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) {
+ if (UNLIKELY(entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart))) {
// Overflow in offset.
LOG(ERROR) << "Entry at offset " << entry_offset << " is too large.";
- return false;
+ return base::unexpected(std::nullopt);
}
const size_t chunk_size = dtohl(type->header.size);
entry_offset += dtohl(type->entriesStart);
- if (entry_offset > chunk_size - sizeof(ResTable_entry)) {
+ if (UNLIKELY(entry_offset > chunk_size - sizeof(ResTable_entry))) {
LOG(ERROR) << "Entry at offset " << entry_offset
<< " is too large. No room for ResTable_entry.";
- return false;
+ return base::unexpected(std::nullopt);
}
- const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
- reinterpret_cast<const uint8_t*>(type) + entry_offset);
+ auto entry = type.offset(entry_offset).convert<ResTable_entry>();
+ if (UNLIKELY(!entry)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
const size_t entry_size = dtohs(entry->size);
- if (entry_size < sizeof(*entry)) {
+ if (UNLIKELY(entry_size < sizeof(entry.value()))) {
LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
<< " is too small.";
- return false;
+ return base::unexpected(std::nullopt);
}
- if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) {
+ if (UNLIKELY(entry_size > chunk_size || entry_offset > chunk_size - entry_size)) {
LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
<< " is too large.";
- return false;
+ return base::unexpected(std::nullopt);
}
if (entry_size < sizeof(ResTable_map_entry)) {
// There needs to be room for one Res_value struct.
- if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) {
+ if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) {
LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset
<< " for type " << (int)type->id << ".";
- return false;
+ return base::unexpected(std::nullopt);
}
- const Res_value* value =
- reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size);
+ auto value = entry.offset(entry_size).convert<Res_value>();
+ if (UNLIKELY(!value)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
const size_t value_size = dtohs(value->size);
- if (value_size < sizeof(Res_value)) {
+ if (UNLIKELY(value_size < sizeof(Res_value))) {
LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small.";
- return false;
+ return base::unexpected(std::nullopt);
}
- if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) {
+ if (UNLIKELY(value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size)) {
LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset
<< " is too large.";
- return false;
+ return base::unexpected(std::nullopt);
}
} else {
- const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry);
+ auto map = entry.convert<ResTable_map_entry>();
+ if (UNLIKELY(!map)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
const size_t map_entry_count = dtohl(map->count);
size_t map_entries_start = entry_offset + entry_size;
- if (map_entries_start & 0x03) {
+ if (UNLIKELY(map_entries_start & 0x03U)) {
LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset.";
- return false;
+ return base::unexpected(std::nullopt);
}
// Each entry is sizeof(ResTable_map) big.
- if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) {
+ if (UNLIKELY(map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map)))) {
LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << ".";
- return false;
+ return base::unexpected(std::nullopt);
}
}
- return true;
+ return {};
}
LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei)
@@ -233,99 +243,125 @@
entryIndex_);
}
-const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk,
- uint16_t entry_index) {
- uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index);
- if (entry_offset == ResTable_type::NO_ENTRY) {
- return nullptr;
+base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
+ base::expected<uint32_t, NullOrIOError> entry_offset = GetEntryOffset(type_chunk, entry_index);
+ if (UNLIKELY(!entry_offset.has_value())) {
+ return base::unexpected(entry_offset.error());
}
- return GetEntryFromOffset(type_chunk, entry_offset);
+ return GetEntryFromOffset(type_chunk, entry_offset.value());
}
-uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) {
+base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
const size_t entry_count = dtohl(type_chunk->entryCount);
const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
// Check if there is the desired entry in this type.
-
if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
// This is encoded as a sparse map, so perform a binary search.
- const ResTable_sparseTypeEntry* sparse_indices =
- reinterpret_cast<const ResTable_sparseTypeEntry*>(
- reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
- const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count;
- const ResTable_sparseTypeEntry* result =
- std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
- [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) {
- return dtohs(entry.idx) < entry_idx;
- });
+ bool error = false;
+ auto sparse_indices = type_chunk.offset(offsets_offset)
+ .convert<ResTable_sparseTypeEntry>().iterator();
+ auto sparse_indices_end = sparse_indices + entry_count;
+ auto result = std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
+ [&error](const incfs::map_ptr<ResTable_sparseTypeEntry>& entry,
+ uint16_t entry_idx) {
+ if (UNLIKELY(!entry)) {
+ return error = true;
+ }
+ return dtohs(entry->idx) < entry_idx;
+ });
- if (result == sparse_indices_end || dtohs(result->idx) != entry_index) {
+ if (result == sparse_indices_end) {
// No entry found.
- return ResTable_type::NO_ENTRY;
+ return base::unexpected(std::nullopt);
+ }
+
+ const incfs::verified_map_ptr<ResTable_sparseTypeEntry> entry = (*result).verified();
+ if (dtohs(entry->idx) != entry_index) {
+ if (error) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ return base::unexpected(std::nullopt);
}
// Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
// the real offset divided by 4.
- return uint32_t{dtohs(result->offset)} * 4u;
+ return uint32_t{dtohs(entry->offset)} * 4u;
}
// This type is encoded as a dense array.
if (entry_index >= entry_count) {
// This entry cannot be here.
- return ResTable_type::NO_ENTRY;
+ return base::unexpected(std::nullopt);
}
- const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
- reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
- return dtohl(entry_offsets[entry_index]);
-}
-
-const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk,
- uint32_t offset) {
- if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) {
- return nullptr;
+ const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert<uint32_t>() + entry_index;
+ if (UNLIKELY(!entry_offset_ptr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
}
- return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) +
- offset + dtohl(type_chunk->entriesStart));
+
+ const uint32_t value = dtohl(entry_offset_ptr.value());
+ if (value == ResTable_type::NO_ENTRY) {
+ return base::unexpected(std::nullopt);
+ }
+
+ return value;
}
-void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
- std::set<ResTable_config>* out_configs) const {
- const static std::u16string kMipMap = u"mipmap";
+base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntryFromOffset(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset) {
+ auto valid = VerifyResTableEntry(type_chunk, offset);
+ if (UNLIKELY(!valid.has_value())) {
+ return base::unexpected(valid.error());
+ }
+ return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert<ResTable_entry>();
+}
+
+base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations(
+ bool exclude_mipmap, std::set<ResTable_config>* out_configs) const {
const size_t type_count = type_specs_.size();
for (size_t i = 0; i < type_count; i++) {
const TypeSpecPtr& type_spec = type_specs_[i];
- if (type_spec != nullptr) {
- if (exclude_mipmap) {
- const int type_idx = type_spec->type_spec->id - 1;
- size_t type_name_len;
- const char16_t* type_name16 = type_string_pool_.stringAt(type_idx, &type_name_len);
- if (type_name16 != nullptr) {
- if (kMipMap.compare(0, std::u16string::npos, type_name16, type_name_len) == 0) {
- // This is a mipmap type, skip collection.
- continue;
- }
- }
- const char* type_name = type_string_pool_.string8At(type_idx, &type_name_len);
- if (type_name != nullptr) {
- if (strncmp(type_name, "mipmap", type_name_len) == 0) {
- // This is a mipmap type, skip collection.
- continue;
- }
+ if (type_spec == nullptr) {
+ continue;
+ }
+ if (exclude_mipmap) {
+ const int type_idx = type_spec->type_spec->id - 1;
+ const auto type_name16 = type_string_pool_.stringAt(type_idx);
+ if (UNLIKELY(IsIOError(type_name16))) {
+ return base::unexpected(GetIOError(type_name16.error()));
+ }
+ if (type_name16.has_value()) {
+ if (strncmp16(type_name16->data(), u"mipmap", type_name16->size()) == 0) {
+ // This is a mipmap type, skip collection.
+ continue;
}
}
- const auto iter_end = type_spec->types + type_spec->type_count;
- for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- ResTable_config config;
- config.copyFromDtoH((*iter)->config);
- out_configs->insert(config);
+ const auto type_name = type_string_pool_.string8At(type_idx);
+ if (UNLIKELY(IsIOError(type_name))) {
+ return base::unexpected(GetIOError(type_name.error()));
+ }
+ if (type_name.has_value()) {
+ if (strncmp(type_name->data(), "mipmap", type_name->size()) == 0) {
+ // This is a mipmap type, skip collection.
+ continue;
+ }
}
}
+
+ const auto iter_end = type_spec->types + type_spec->type_count;
+ for (auto iter = type_spec->types; iter != iter_end; ++iter) {
+ ResTable_config config;
+ config.copyFromDtoH((*iter)->config);
+ out_configs->insert(config);
+ }
}
+ return {};
}
void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const {
@@ -348,43 +384,53 @@
}
}
-uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name,
- const std::u16string& entry_name) const {
- ssize_t type_idx = type_string_pool_.indexOfString(type_name.data(), type_name.size());
- if (type_idx < 0) {
- return 0u;
+base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName(
+ const std::u16string& type_name, const std::u16string& entry_name) const {
+ const base::expected<size_t, NullOrIOError> type_idx = type_string_pool_.indexOfString(
+ type_name.data(), type_name.size());
+ if (!type_idx.has_value()) {
+ return base::unexpected(type_idx.error());
}
- ssize_t key_idx = key_string_pool_.indexOfString(entry_name.data(), entry_name.size());
- if (key_idx < 0) {
- return 0u;
+ const base::expected<size_t, NullOrIOError> key_idx = key_string_pool_.indexOfString(
+ entry_name.data(), entry_name.size());
+ if (!key_idx.has_value()) {
+ return base::unexpected(key_idx.error());
}
- const TypeSpec* type_spec = type_specs_[type_idx].get();
+ const TypeSpec* type_spec = type_specs_[*type_idx].get();
if (type_spec == nullptr) {
- return 0u;
+ return base::unexpected(std::nullopt);
}
const auto iter_end = type_spec->types + type_spec->type_count;
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- const ResTable_type* type = *iter;
+ const incfs::verified_map_ptr<ResTable_type>& type = *iter;
+
size_t entry_count = dtohl(type->entryCount);
for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
- const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
- reinterpret_cast<const uint8_t*>(type) + dtohs(type->header.headerSize));
- const uint32_t offset = dtohl(entry_offsets[entry_idx]);
+ auto entry_offset_ptr = type.offset(dtohs(type->header.headerSize)).convert<uint32_t>() +
+ entry_idx;
+ if (!entry_offset_ptr) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ auto offset = dtohl(entry_offset_ptr.value());
if (offset != ResTable_type::NO_ENTRY) {
- const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
- reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset);
- if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) {
+ auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
+ if (!entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
// The package ID will be overridden by the caller (due to runtime assignment of package
// IDs for shared libraries).
- return make_resid(0x00, type_idx + type_id_offset_ + 1, entry_idx);
+ return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx);
}
}
}
}
- return 0u;
+ return base::unexpected(std::nullopt);
}
const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
@@ -405,8 +451,8 @@
// was added.
constexpr size_t kMinPackageSize =
sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset);
- const ResTable_package* header = chunk.header<ResTable_package, kMinPackageSize>();
- if (header == nullptr) {
+ const incfs::map_ptr<ResTable_package> header = chunk.header<ResTable_package, kMinPackageSize>();
+ if (!header) {
LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small.";
return {};
}
@@ -453,10 +499,13 @@
const Chunk child_chunk = iter.Next();
switch (child_chunk.type()) {
case RES_STRING_POOL_TYPE: {
- const uintptr_t pool_address =
- reinterpret_cast<uintptr_t>(child_chunk.header<ResChunk_header>());
- const uintptr_t header_address = reinterpret_cast<uintptr_t>(header);
- if (pool_address == header_address + dtohl(header->typeStrings)) {
+ const auto pool_address = child_chunk.header<ResChunk_header>();
+ if (!pool_address) {
+ LOG(ERROR) << "RES_STRING_POOL_TYPE is incomplete due to incremental installation.";
+ return {};
+ }
+
+ if (pool_address == header.offset(dtohl(header->typeStrings)).convert<ResChunk_header>()) {
// This string pool is the type string pool.
status_t err = loaded_package->type_string_pool_.setTo(
child_chunk.header<ResStringPool_header>(), child_chunk.size());
@@ -464,7 +513,8 @@
LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt.";
return {};
}
- } else if (pool_address == header_address + dtohl(header->keyStrings)) {
+ } else if (pool_address == header.offset(dtohl(header->keyStrings))
+ .convert<ResChunk_header>()) {
// This string pool is the key string pool.
status_t err = loaded_package->key_string_pool_.setTo(
child_chunk.header<ResStringPool_header>(), child_chunk.size());
@@ -478,8 +528,8 @@
} break;
case RES_TABLE_TYPE_SPEC_TYPE: {
- const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>();
- if (type_spec == nullptr) {
+ const auto type_spec = child_chunk.header<ResTable_typeSpec>();
+ if (!type_spec) {
LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small.";
return {};
}
@@ -514,7 +564,7 @@
std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1];
if (builder_ptr == nullptr) {
- builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec);
+ builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec.verified());
loaded_package->resource_ids_.set(type_spec->id, entry_count);
} else {
LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
@@ -523,8 +573,8 @@
} break;
case RES_TABLE_TYPE_TYPE: {
- const ResTable_type* type = child_chunk.header<ResTable_type, kResTableTypeMinSize>();
- if (type == nullptr) {
+ const auto type = child_chunk.header<ResTable_type, kResTableTypeMinSize>();
+ if (!type) {
LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small.";
return {};
}
@@ -536,7 +586,7 @@
// Type chunks must be preceded by their TypeSpec chunks.
std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type->id - 1];
if (builder_ptr != nullptr) {
- builder_ptr->AddType(type);
+ builder_ptr->AddType(type.verified());
} else {
LOG(ERROR) << StringPrintf(
"RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.",
@@ -546,8 +596,8 @@
} break;
case RES_TABLE_LIBRARY_TYPE: {
- const ResTable_lib_header* lib = child_chunk.header<ResTable_lib_header>();
- if (lib == nullptr) {
+ const auto lib = child_chunk.header<ResTable_lib_header>();
+ if (!lib) {
LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small.";
return {};
}
@@ -559,10 +609,13 @@
loaded_package->dynamic_package_map_.reserve(dtohl(lib->count));
- const ResTable_lib_entry* const entry_begin =
- reinterpret_cast<const ResTable_lib_entry*>(child_chunk.data_ptr());
- const ResTable_lib_entry* const entry_end = entry_begin + dtohl(lib->count);
+ const auto entry_begin = child_chunk.data_ptr().convert<ResTable_lib_entry>();
+ const auto entry_end = entry_begin + dtohl(lib->count);
for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
+ if (!entry_iter) {
+ return {};
+ }
+
std::string package_name;
util::ReadUtf16StringFromDevice(entry_iter->packageName,
arraysize(entry_iter->packageName), &package_name);
@@ -580,17 +633,16 @@
} break;
case RES_TABLE_OVERLAYABLE_TYPE: {
- const ResTable_overlayable_header* header =
- child_chunk.header<ResTable_overlayable_header>();
- if (header == nullptr) {
+ const auto overlayable = child_chunk.header<ResTable_overlayable_header>();
+ if (!overlayable) {
LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small.";
return {};
}
std::string name;
- util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &name);
+ util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name);
std::string actor;
- util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor);
+ util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor);
if (loaded_package->overlayable_map_.find(name) !=
loaded_package->overlayable_map_.end()) {
@@ -606,9 +658,9 @@
switch (overlayable_child_chunk.type()) {
case RES_TABLE_OVERLAYABLE_POLICY_TYPE: {
- const ResTable_overlayable_policy_header* policy_header =
+ const auto policy_header =
overlayable_child_chunk.header<ResTable_overlayable_policy_header>();
- if (policy_header == nullptr) {
+ if (!policy_header) {
LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
return {};
}
@@ -621,10 +673,12 @@
// Retrieve all the resource ids belonging to this policy chunk
std::unordered_set<uint32_t> ids;
- const auto ids_begin =
- reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr());
+ const auto ids_begin = overlayable_child_chunk.data_ptr().convert<ResTable_ref>();
const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
+ if (!id_iter) {
+ return {};
+ }
ids.insert(dtohl(id_iter->ident));
}
@@ -633,7 +687,7 @@
overlayable_info.name = name;
overlayable_info.actor = actor;
overlayable_info.policy_flags = policy_header->policy_flags;
- loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
+ loaded_package->overlayable_infos_.emplace_back(overlayable_info, ids);
loaded_package->defines_overlayable_ = true;
break;
}
@@ -683,8 +737,8 @@
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
package_property_t property_flags) {
- const ResTable_header* header = chunk.header<ResTable_header>();
- if (header == nullptr) {
+ incfs::map_ptr<ResTable_header> header = chunk.header<ResTable_header>();
+ if (!header) {
LOG(ERROR) << "RES_TABLE_TYPE too small.";
return false;
}
@@ -747,7 +801,8 @@
return true;
}
-std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
+std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
+ const size_t length,
const LoadedIdmap* loaded_idmap,
const package_property_t property_flags) {
ATRACE_NAME("LoadedArsc::Load");
@@ -755,7 +810,7 @@
// Not using make_unique because the constructor is private.
std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
- ChunkIterator iter(data.data(), data.size());
+ ChunkIterator iter(data, length);
while (iter.HasNext()) {
const Chunk chunk = iter.Next();
switch (chunk.type()) {
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index dfb4009..bce70e2 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -104,22 +104,26 @@
*dst = 0;
}
-static status_t validate_chunk(const ResChunk_header* chunk,
+static status_t validate_chunk(const incfs::map_ptr<ResChunk_header>& chunk,
size_t minSize,
- const uint8_t* dataEnd,
+ const incfs::map_ptr<uint8_t> dataEnd,
const char* name)
{
+ if (!chunk) {
+ return BAD_TYPE;
+ }
+
const uint16_t headerSize = dtohs(chunk->headerSize);
const uint32_t size = dtohl(chunk->size);
if (headerSize >= minSize) {
if (headerSize <= size) {
if (((headerSize|size)&0x3) == 0) {
- if ((size_t)size <= (size_t)(dataEnd-((const uint8_t*)chunk))) {
+ if ((size_t)size <= (size_t)(dataEnd-chunk.convert<uint8_t>())) {
return NO_ERROR;
}
ALOGW("%s data size 0x%x extends beyond resource end %p.",
- name, size, (void*)(dataEnd-((const uint8_t*)chunk)));
+ name, size, (void*)(dataEnd-chunk.convert<uint8_t>()));
return BAD_TYPE;
}
ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.",
@@ -450,7 +454,7 @@
mHeader = (const ResStringPool_header*) header;
}
-status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
+status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyData)
{
if (!data || !size) {
return (mError=BAD_TYPE);
@@ -467,8 +471,8 @@
// The data is at least as big as a ResChunk_header, so we can safely validate the other
// header fields.
// `data + size` is safe because the source of `size` comes from the kernel/filesystem.
- if (validate_chunk(reinterpret_cast<const ResChunk_header*>(data), sizeof(ResStringPool_header),
- reinterpret_cast<const uint8_t*>(data) + size,
+ const auto chunk_header = data.convert<ResChunk_header>();
+ if (validate_chunk(chunk_header, sizeof(ResStringPool_header), data.convert<uint8_t>() + size,
"ResStringPool_header") != NO_ERROR) {
ALOGW("Bad string block: malformed block dimensions");
return (mError=BAD_TYPE);
@@ -481,16 +485,25 @@
if (mOwnedData == NULL) {
return (mError=NO_MEMORY);
}
- memcpy(mOwnedData, data, size);
+
+ if (!data.convert<uint8_t>().verify(size)) {
+ return (mError=NO_MEMORY);
+ }
+
+ memcpy(mOwnedData, data.unsafe_ptr(), size);
data = mOwnedData;
}
// The size has been checked, so it is safe to read the data in the ResStringPool_header
// data structure.
- mHeader = (const ResStringPool_header*)data;
+ const auto header = data.convert<ResStringPool_header>();
+ if (!header) {
+ return (mError=BAD_TYPE);
+ }
+ mHeader = header.verified();
if (notDeviceEndian) {
- ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader);
+ ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader.unsafe_ptr());
h->header.headerSize = dtohs(mHeader->header.headerSize);
h->header.type = dtohs(mHeader->header.type);
h->header.size = dtohl(mHeader->header.size);
@@ -508,8 +521,7 @@
return (mError=BAD_TYPE);
}
mSize = mHeader->header.size;
- mEntries = (const uint32_t*)
- (((const uint8_t*)data)+mHeader->header.headerSize);
+ mEntries = data.offset(mHeader->header.headerSize).convert<uint32_t>();
if (mHeader->stringCount > 0) {
if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow?
@@ -536,9 +548,7 @@
return (mError=BAD_TYPE);
}
- mStrings = (const void*)
- (((const uint8_t*)data) + mHeader->stringsStart);
-
+ mStrings = data.offset(mHeader->stringsStart).convert<void>();
if (mHeader->styleCount == 0) {
mStringPoolSize = (mSize - mHeader->stringsStart) / charSize;
} else {
@@ -560,31 +570,37 @@
// check invariant: stringCount > 0 requires a string pool to exist
if (mStringPoolSize == 0) {
- ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount);
+ ALOGW("Bad string block: stringCount is %d but pool size is 0\n",
+ (int)mHeader->stringCount);
return (mError=BAD_TYPE);
}
if (notDeviceEndian) {
size_t i;
- uint32_t* e = const_cast<uint32_t*>(mEntries);
+ auto e = const_cast<uint32_t*>(mEntries.unsafe_ptr());
for (i=0; i<mHeader->stringCount; i++) {
- e[i] = dtohl(mEntries[i]);
+ e[i] = dtohl(e[i]);
}
if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) {
- const uint16_t* strings = (const uint16_t*)mStrings;
- uint16_t* s = const_cast<uint16_t*>(strings);
+ uint16_t* s = const_cast<uint16_t*>(mStrings.convert<uint16_t>().unsafe_ptr());
for (i=0; i<mStringPoolSize; i++) {
- s[i] = dtohs(strings[i]);
+ s[i] = dtohs(s[i]);
}
}
}
- if ((mHeader->flags&ResStringPool_header::UTF8_FLAG &&
- ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) ||
- (!(mHeader->flags&ResStringPool_header::UTF8_FLAG) &&
- ((uint16_t*)mStrings)[mStringPoolSize-1] != 0)) {
- ALOGW("Bad string block: last string is not 0-terminated\n");
- return (mError=BAD_TYPE);
+ if (mHeader->flags&ResStringPool_header::UTF8_FLAG) {
+ auto end = mStrings.convert<uint8_t>() + (mStringPoolSize-1);
+ if (!end || end.value() != 0) {
+ ALOGW("Bad string block: last string is not 0-terminated\n");
+ return (mError=BAD_TYPE);
+ }
+ } else {
+ auto end = mStrings.convert<uint16_t>() + (mStringPoolSize-1);
+ if (!end || end.value() != 0) {
+ ALOGW("Bad string block: last string is not 0-terminated\n");
+ return (mError=BAD_TYPE);
+ }
}
} else {
mStrings = NULL;
@@ -599,14 +615,13 @@
return (mError=BAD_TYPE);
}
- if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) {
+ if ((mEntryStyles.convert<uint8_t>() - mHeader.convert<uint8_t>()) > (int)size) {
ALOGW("Bad string block: entry of %d styles extends past data size %d\n",
- (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader),
+ (int)(mEntryStyles.convert<uint8_t>()-mHeader.convert<uint8_t>()),
(int)size);
return (mError=BAD_TYPE);
}
- mStyles = (const uint32_t*)
- (((const uint8_t*)data)+mHeader->stylesStart);
+ mStyles = data.offset(mHeader->stylesStart).convert<uint32_t>();
if (mHeader->stylesStart >= mHeader->header.size) {
ALOGW("Bad string block: style pool starts %d, after total size %d\n",
(int)mHeader->stylesStart, (int)mHeader->header.size);
@@ -617,13 +632,13 @@
if (notDeviceEndian) {
size_t i;
- uint32_t* e = const_cast<uint32_t*>(mEntryStyles);
+ uint32_t* e = const_cast<uint32_t*>(mEntryStyles.unsafe_ptr());
for (i=0; i<mHeader->styleCount; i++) {
- e[i] = dtohl(mEntryStyles[i]);
+ e[i] = dtohl(e[i]);
}
- uint32_t* s = const_cast<uint32_t*>(mStyles);
+ uint32_t* s = const_cast<uint32_t*>(mStyles.unsafe_ptr());
for (i=0; i<mStylePoolSize; i++) {
- s[i] = dtohl(mStyles[i]);
+ s[i] = dtohl(s[i]);
}
}
@@ -631,8 +646,9 @@
{ htodl(ResStringPool_span::END) },
htodl(ResStringPool_span::END), htodl(ResStringPool_span::END)
};
- if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))],
- &endSpan, sizeof(endSpan)) != 0) {
+
+ auto stylesEnd = mStyles + (mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t)));
+ if (!stylesEnd || memcmp(stylesEnd.unsafe_ptr(), &endSpan, sizeof(endSpan)) != 0) {
ALOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n");
return (mError=BAD_TYPE);
}
@@ -653,7 +669,7 @@
void ResStringPool::uninit()
{
mError = NO_INIT;
- if (mHeader != NULL && mCache != NULL) {
+ if (mHeader && mCache != NULL) {
for (size_t x = 0; x < mHeader->stringCount; x++) {
if (mCache[x] != NULL) {
free(mCache[x]);
@@ -679,15 +695,21 @@
* data encoded. In that case, drop the high bit of the first character and
* add it together with the next character.
*/
-static inline size_t
-decodeLength(const uint16_t** str)
+static inline base::expected<size_t, IOError> decodeLength(incfs::map_ptr<uint16_t>* str)
{
- size_t len = **str;
- if ((len & 0x8000) != 0) {
- (*str)++;
- len = ((len & 0x7FFF) << 16) | **str;
+ if (UNLIKELY(!*str)) {
+ return base::unexpected(IOError::PAGES_MISSING);
}
- (*str)++;
+
+ size_t len = str->value();
+ if ((len & 0x8000U) != 0) {
+ ++(*str);
+ if (UNLIKELY(!*str)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ len = ((len & 0x7FFFU) << 16U) | str->value();
+ }
+ ++(*str);
return len;
}
@@ -701,82 +723,119 @@
* data encoded. In that case, drop the high bit of the first character and
* add it together with the next character.
*/
-static inline size_t
-decodeLength(const uint8_t** str)
+static inline base::expected<size_t, IOError> decodeLength(incfs::map_ptr<uint8_t>* str)
{
- size_t len = **str;
- if ((len & 0x80) != 0) {
- (*str)++;
- len = ((len & 0x7F) << 8) | **str;
+ if (UNLIKELY(!*str)) {
+ return base::unexpected(IOError::PAGES_MISSING);
}
- (*str)++;
+
+ size_t len = str->value();
+ if ((len & 0x80U) != 0) {
+ ++(*str);
+ if (UNLIKELY(!*str)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ len = ((len & 0x7FU) << 8U) | str->value();
+ }
+ ++(*str);
return len;
}
-const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
+base::expected<StringPiece16, NullOrIOError> ResStringPool::stringAt(size_t idx) const
{
if (mError == NO_ERROR && idx < mHeader->stringCount) {
const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
- const uint32_t off = mEntries[idx]/(isUTF8?sizeof(uint8_t):sizeof(uint16_t));
+ auto offPtr = mEntries + idx;
+ if (UNLIKELY(!offPtr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ const uint32_t off = (offPtr.value())/(isUTF8?sizeof(uint8_t):sizeof(uint16_t));
if (off < (mStringPoolSize-1)) {
if (!isUTF8) {
- const uint16_t* strings = (uint16_t*)mStrings;
- const uint16_t* str = strings+off;
+ auto strings = mStrings.convert<uint16_t>();
+ auto str = strings+off;
- *u16len = decodeLength(&str);
+ const base::expected<size_t, IOError> u16len = decodeLength(&str);
+ if (UNLIKELY(!u16len.has_value())) {
+ return base::unexpected(u16len.error());
+ }
+
if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) {
// Reject malformed (non null-terminated) strings
- if (str[*u16len] != 0x0000) {
- ALOGW("Bad string block: string #%d is not null-terminated",
- (int)idx);
- return NULL;
+ const auto nullAddress = str + (*u16len);
+ if (UNLIKELY(!nullAddress)) {
+ return base::unexpected(IOError::PAGES_MISSING);
}
- return reinterpret_cast<const char16_t*>(str);
+
+ if (nullAddress.value() != 0x0000) {
+ ALOGW("Bad string block: string #%d is not null-terminated", (int)idx);
+ return base::unexpected(std::nullopt);
+ }
+
+ if (UNLIKELY(!str.verify(*u16len + 1U))) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ return StringPiece16(reinterpret_cast<const char16_t*>(str.unsafe_ptr()),
+ *u16len);
} else {
ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
(int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize);
}
} else {
- const uint8_t* strings = (uint8_t*)mStrings;
- const uint8_t* u8str = strings+off;
+ auto strings = mStrings.convert<uint8_t>();
+ auto u8str = strings+off;
- *u16len = decodeLength(&u8str);
- size_t u8len = decodeLength(&u8str);
+ base::expected<size_t, IOError> u16len = decodeLength(&u8str);
+ if (UNLIKELY(!u16len.has_value())) {
+ return base::unexpected(u16len.error());
+ }
+
+ const base::expected<size_t, IOError> u8len = decodeLength(&u8str);
+ if (UNLIKELY(!u8len.has_value())) {
+ return base::unexpected(u8len.error());
+ }
// encLen must be less than 0x7FFF due to encoding.
- if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) {
+ if ((uint32_t)(u8str+*u8len-strings) < mStringPoolSize) {
AutoMutex lock(mDecodeLock);
if (mCache != NULL && mCache[idx] != NULL) {
- return mCache[idx];
+ return StringPiece16(mCache[idx], *u16len);
}
// Retrieve the actual length of the utf8 string if the
// encoded length was truncated
- if (stringDecodeAt(idx, u8str, u8len, &u8len) == NULL) {
- return NULL;
+ auto decodedString = stringDecodeAt(idx, u8str, *u8len);
+ if (!decodedString.has_value()) {
+ return base::unexpected(decodedString.error());
}
// Since AAPT truncated lengths longer than 0x7FFF, check
// that the bits that remain after truncation at least match
// the bits of the actual length
- ssize_t actualLen = utf8_to_utf16_length(u8str, u8len);
- if (actualLen < 0 || ((size_t)actualLen & 0x7FFF) != *u16len) {
+ ssize_t actualLen = utf8_to_utf16_length(
+ reinterpret_cast<const uint8_t*>(decodedString->data()),
+ decodedString->size());
+
+ if (actualLen < 0 || ((size_t)actualLen & 0x7FFFU) != *u16len) {
ALOGW("Bad string block: string #%lld decoded length is not correct "
"%lld vs %llu\n",
(long long)idx, (long long)actualLen, (long long)*u16len);
- return NULL;
+ return base::unexpected(std::nullopt);
}
- *u16len = (size_t) actualLen;
- char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t));
+ u16len = (size_t) actualLen;
+ auto u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t));
if (!u16str) {
ALOGW("No memory when trying to allocate decode cache for string #%d\n",
(int)idx);
- return NULL;
+ return base::unexpected(std::nullopt);
}
- utf8_to_utf16(u8str, u8len, u16str, *u16len + 1);
+ utf8_to_utf16(reinterpret_cast<const uint8_t*>(decodedString->data()),
+ decodedString->size(), u16str, *u16len + 1);
if (mCache == NULL) {
#ifndef __ANDROID__
@@ -793,19 +852,19 @@
if (mCache == NULL) {
ALOGW("No memory trying to allocate decode cache table of %d bytes\n",
(int)(mHeader->stringCount*sizeof(char16_t**)));
- return NULL;
+ return base::unexpected(std::nullopt);
}
}
if (kDebugStringPoolNoisy) {
- ALOGI("Caching UTF8 string: %s", u8str);
+ ALOGI("Caching UTF8 string: %s", u8str.unsafe_ptr());
}
mCache[idx] = u16str;
- return u16str;
+ return StringPiece16(u16str, *u16len);
} else {
ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n",
- (long long)idx, (long long)(u8str+u8len-strings),
+ (long long)idx, (long long)(u8str+*u8len-strings),
(long long)mStringPoolSize);
}
}
@@ -815,33 +874,43 @@
(int)(mStringPoolSize*sizeof(uint16_t)));
}
}
- return NULL;
+ return base::unexpected(std::nullopt);
}
-const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
+base::expected<StringPiece, NullOrIOError> ResStringPool::string8At(size_t idx) const
{
if (mError == NO_ERROR && idx < mHeader->stringCount) {
if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) {
- return NULL;
+ return base::unexpected(std::nullopt);
}
- const uint32_t off = mEntries[idx]/sizeof(char);
+
+ auto offPtr = mEntries + idx;
+ if (UNLIKELY(!offPtr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ const uint32_t off = (offPtr.value())/sizeof(char);
if (off < (mStringPoolSize-1)) {
- const uint8_t* strings = (uint8_t*)mStrings;
- const uint8_t* str = strings+off;
+ auto strings = mStrings.convert<uint8_t>();
+ auto str = strings+off;
// Decode the UTF-16 length. This is not used if we're not
// converting to UTF-16 from UTF-8.
- decodeLength(&str);
+ const base::expected<size_t, IOError> u16len = decodeLength(&str);
+ if (UNLIKELY(!u16len.has_value())) {
+ return base::unexpected(u16len.error());
+ }
- const size_t encLen = decodeLength(&str);
- *outLen = encLen;
+ const base::expected<size_t, IOError> u8len = decodeLength(&str);
+ if (UNLIKELY(!u8len.has_value())) {
+ return base::unexpected(u8len.error());
+ }
- if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
- return stringDecodeAt(idx, str, encLen, outLen);
-
+ if ((uint32_t)(str+*u8len-strings) < mStringPoolSize) {
+ return stringDecodeAt(idx, str, *u8len);
} else {
ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
- (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
+ (int)idx, (int)(str+*u8len-strings), (int)mStringPoolSize);
}
} else {
ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
@@ -849,7 +918,7 @@
(int)(mStringPoolSize*sizeof(uint16_t)));
}
}
- return NULL;
+ return base::unexpected(std::nullopt);
}
/**
@@ -859,74 +928,93 @@
* bits. Strings that exceed the maximum encode length are not placed into
* StringPools in AAPT2.
**/
-const char* ResStringPool::stringDecodeAt(size_t idx, const uint8_t* str,
- const size_t encLen, size_t* outLen) const {
- const uint8_t* strings = (uint8_t*)mStrings;
-
+base::expected<StringPiece, NullOrIOError> ResStringPool::stringDecodeAt(
+ size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const
+{
+ const auto strings = mStrings.convert<uint8_t>();
size_t i = 0, end = encLen;
while ((uint32_t)(str+end-strings) < mStringPoolSize) {
- if (str[end] == 0x00) {
+ const auto nullAddress = str + end;
+ if (UNLIKELY(!nullAddress)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ if (nullAddress.value() == 0x00) {
if (i != 0) {
ALOGW("Bad string block: string #%d is truncated (actual length is %d)",
(int)idx, (int)end);
}
- *outLen = end;
- return (const char*)str;
+ if (UNLIKELY(!str.verify(end + 1U))) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ return StringPiece((const char*) str.unsafe_ptr(), end);
}
end = (++i << (sizeof(uint8_t) * 8 * 2 - 1)) | encLen;
}
// Reject malformed (non null-terminated) strings
- ALOGW("Bad string block: string #%d is not null-terminated",
- (int)idx);
- return NULL;
+ ALOGW("Bad string block: string #%d is not null-terminated", (int)idx);
+ return base::unexpected(std::nullopt);
}
-const String8 ResStringPool::string8ObjectAt(size_t idx) const
+base::expected<String8, IOError> ResStringPool::string8ObjectAt(size_t idx) const
{
- size_t len;
- const char *str = string8At(idx, &len);
- if (str != NULL) {
- return String8(str, len);
+ const base::expected<StringPiece, NullOrIOError> str = string8At(idx);
+ if (UNLIKELY(IsIOError(str))) {
+ return base::unexpected(GetIOError(str.error()));
+ }
+ if (str.has_value()) {
+ return String8(str->data(), str->size());
}
- const char16_t *str16 = stringAt(idx, &len);
- if (str16 != NULL) {
- return String8(str16, len);
+ const base::expected<StringPiece16, NullOrIOError> str16 = stringAt(idx);
+ if (UNLIKELY(IsIOError(str16))) {
+ return base::unexpected(GetIOError(str16.error()));
}
+ if (str16.has_value()) {
+ return String8(str16->data(), str16->size());
+ }
+
return String8();
}
-const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const
+base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool::styleAt(
+ const ResStringPool_ref& ref) const
{
return styleAt(ref.index);
}
-const ResStringPool_span* ResStringPool::styleAt(size_t idx) const
+base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool::styleAt(
+ size_t idx) const
{
if (mError == NO_ERROR && idx < mHeader->styleCount) {
- const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t));
+ auto offPtr = mEntryStyles + idx;
+ if (UNLIKELY(!offPtr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ const uint32_t off = ((offPtr.value())/sizeof(uint32_t));
if (off < mStylePoolSize) {
- return (const ResStringPool_span*)(mStyles+off);
+ return (mStyles+off).convert<ResStringPool_span>();
} else {
ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n",
(int)idx, (int)(off*sizeof(uint32_t)),
(int)(mStylePoolSize*sizeof(uint32_t)));
}
}
- return NULL;
+ return base::unexpected(std::nullopt);
}
-ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
+base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_t* str,
+ size_t strLen) const
{
if (mError != NO_ERROR) {
- return mError;
+ return base::unexpected(std::nullopt);
}
- size_t len;
-
if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) {
if (kDebugStringPoolNoisy) {
ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string());
@@ -948,17 +1036,19 @@
ssize_t mid;
while (l <= h) {
mid = l + (h - l)/2;
- const uint8_t* s = (const uint8_t*)string8At(mid, &len);
- int c;
- if (s != NULL) {
- char16_t* end = utf8_to_utf16(s, len, convBuffer, convBufferLen);
+ int c = -1;
+ const base::expected<StringPiece, NullOrIOError> s = string8At(mid);
+ if (UNLIKELY(IsIOError(s))) {
+ return base::unexpected(s.error());
+ }
+ if (s.has_value()) {
+ char16_t* end = utf8_to_utf16(reinterpret_cast<const uint8_t*>(s->data()),
+ s->size(), convBuffer, convBufferLen);
c = strzcmp16(convBuffer, end-convBuffer, str, strLen);
- } else {
- c = -1;
}
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
- (const char*)s, c, (int)l, (int)mid, (int)h);
+ s->data(), c, (int)l, (int)mid, (int)h);
}
if (c == 0) {
if (kDebugStringPoolNoisy) {
@@ -981,15 +1071,21 @@
String8 str8(str, strLen);
const size_t str8Len = str8.size();
for (int i=mHeader->stringCount-1; i>=0; i--) {
- const char* s = string8At(i, &len);
- if (kDebugStringPoolNoisy) {
- ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
+ const base::expected<StringPiece, NullOrIOError> s = string8At(i);
+ if (UNLIKELY(IsIOError(s))) {
+ return base::unexpected(s.error());
}
- if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) {
+ if (s.has_value()) {
if (kDebugStringPoolNoisy) {
- ALOGI("MATCH!");
+ ALOGI("Looking at %s, i=%d\n", s->data(), i);
}
- return i;
+ if (str8Len == s->size()
+ && memcmp(s->data(), str8.string(), str8Len) == 0) {
+ if (kDebugStringPoolNoisy) {
+ ALOGI("MATCH!");
+ }
+ return i;
+ }
}
}
}
@@ -1007,11 +1103,14 @@
ssize_t mid;
while (l <= h) {
mid = l + (h - l)/2;
- const char16_t* s = stringAt(mid, &len);
- int c = s ? strzcmp16(s, len, str, strLen) : -1;
+ const base::expected<StringPiece16, NullOrIOError> s = stringAt(mid);
+ if (UNLIKELY(IsIOError(s))) {
+ return base::unexpected(s.error());
+ }
+ int c = s.has_value() ? strzcmp16(s->data(), s->size(), str, strLen) : -1;
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
- String8(s).string(), c, (int)l, (int)mid, (int)h);
+ String8(s->data(), s->size()).string(), c, (int)l, (int)mid, (int)h);
}
if (c == 0) {
if (kDebugStringPoolNoisy) {
@@ -1030,11 +1129,15 @@
// span tags; since those always appear at the end of the string
// block, start searching at the back.
for (int i=mHeader->stringCount-1; i>=0; i--) {
- const char16_t* s = stringAt(i, &len);
- if (kDebugStringPoolNoisy) {
- ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
+ const base::expected<StringPiece16, NullOrIOError> s = stringAt(i);
+ if (UNLIKELY(IsIOError(s))) {
+ return base::unexpected(s.error());
}
- if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) {
+ if (kDebugStringPoolNoisy) {
+ ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).string(), i);
+ }
+ if (s.has_value() && strLen == s->size() &&
+ strzcmp16(s->data(), s->size(), str, strLen) == 0) {
if (kDebugStringPoolNoisy) {
ALOGI("MATCH!");
}
@@ -1043,8 +1146,7 @@
}
}
}
-
- return NAME_NOT_FOUND;
+ return base::unexpected(std::nullopt);
}
size_t ResStringPool::size() const
@@ -1062,9 +1164,10 @@
return (mError == NO_ERROR) ? mHeader->header.size : 0;
}
-const void* ResStringPool::data() const
+incfs::map_ptr<void> ResStringPool::data() const
{
- return mHeader;
+
+ return mHeader.unsafe_ptr();
}
bool ResStringPool::isSorted() const
@@ -1121,7 +1224,7 @@
const char16_t* ResXMLParser::getComment(size_t* outLen) const
{
int32_t id = getCommentID();
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
uint32_t ResXMLParser::getLineNumber() const
@@ -1140,7 +1243,7 @@
const char16_t* ResXMLParser::getText(size_t* outLen) const
{
int32_t id = getTextID();
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
ssize_t ResXMLParser::getTextValue(Res_value* outValue) const
@@ -1164,7 +1267,7 @@
{
int32_t id = getNamespacePrefixID();
//printf("prefix=%d event=%p\n", id, mEventCode);
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
int32_t ResXMLParser::getNamespaceUriID() const
@@ -1179,7 +1282,7 @@
{
int32_t id = getNamespaceUriID();
//printf("uri=%d event=%p\n", id, mEventCode);
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
int32_t ResXMLParser::getElementNamespaceID() const
@@ -1196,7 +1299,7 @@
const char16_t* ResXMLParser::getElementNamespace(size_t* outLen) const
{
int32_t id = getElementNamespaceID();
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
int32_t ResXMLParser::getElementNameID() const
@@ -1213,7 +1316,7 @@
const char16_t* ResXMLParser::getElementName(size_t* outLen) const
{
int32_t id = getElementNameID();
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
size_t ResXMLParser::getAttributeCount() const
@@ -1246,7 +1349,7 @@
if (kDebugXMLNoisy) {
printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
}
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const
@@ -1256,7 +1359,7 @@
if (kDebugXMLNoisy) {
printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
}
- return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL;
}
int32_t ResXMLParser::getAttributeNameID(size_t idx) const
@@ -1281,7 +1384,7 @@
if (kDebugXMLNoisy) {
printf("getAttributeName 0x%zx=0x%x\n", idx, id);
}
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const
@@ -1291,7 +1394,7 @@
if (kDebugXMLNoisy) {
printf("getAttributeName 0x%zx=0x%x\n", idx, id);
}
- return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL;
}
uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const
@@ -1328,7 +1431,7 @@
if (kDebugXMLNoisy) {
printf("getAttributeValue 0x%zx=0x%x\n", idx, id);
}
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
int32_t ResXMLParser::getAttributeDataType(size_t idx) const
@@ -3596,9 +3699,10 @@
ssize_t findType16(const char16_t* type, size_t len) const {
const size_t N = packages.size();
for (size_t i = 0; i < N; i++) {
- ssize_t index = packages[i]->typeStrings.indexOfString(type, len);
- if (index >= 0) {
- return index + packages[i]->typeIdOffset;
+ const base::expected<size_t, NullOrIOError> index =
+ packages[i]->typeStrings.indexOfString(type, len);
+ if (index.has_value()) {
+ return *index + packages[i]->typeIdOffset;
}
}
return -1;
@@ -4304,21 +4408,21 @@
outName->package = grp->name.string();
outName->packageLen = grp->name.size();
if (allowUtf8) {
- outName->type8 = entry.typeStr.string8(&outName->typeLen);
- outName->name8 = entry.keyStr.string8(&outName->nameLen);
+ outName->type8 = UnpackOptionalString(entry.typeStr.string8(), &outName->typeLen);
+ outName->name8 = UnpackOptionalString(entry.keyStr.string8(), &outName->nameLen);
} else {
outName->type8 = NULL;
outName->name8 = NULL;
}
if (outName->type8 == NULL) {
- outName->type = entry.typeStr.string16(&outName->typeLen);
+ outName->type = UnpackOptionalString(entry.typeStr.string16(), &outName->typeLen);
// If we have a bad index for some reason, we should abort.
if (outName->type == NULL) {
return false;
}
}
if (outName->name8 == NULL) {
- outName->name = entry.keyStr.string16(&outName->nameLen);
+ outName->name = UnpackOptionalString(entry.keyStr.string16(), &outName->nameLen);
// If we have a bad index for some reason, we should abort.
if (outName->name == NULL) {
return false;
@@ -4406,7 +4510,8 @@
entry.package->header->index,
outValue->dataType,
outValue->dataType == Res_value::TYPE_STRING ?
- String8(entry.package->header->values.stringAt(outValue->data, &len)).string() :
+ String8(UnpackOptionalString(
+ entry.package->header->values.stringAt(outValue->data), &len)).string() :
"",
outValue->data);
}
@@ -4462,7 +4567,8 @@
return NULL;
}
if (value->dataType == value->TYPE_STRING) {
- return getTableStringBlock(stringBlock)->stringAt(value->data, outLen);
+ return UnpackOptionalString(getTableStringBlock(stringBlock)->stringAt(value->data),
+ outLen);
}
// XXX do int to string conversions.
return NULL;
@@ -4978,15 +5084,13 @@
size_t targetTypeLen = typeLen;
do {
- ssize_t ti = group->packages[pi]->typeStrings.indexOfString(
- targetType, targetTypeLen);
- if (ti < 0) {
+ auto ti = group->packages[pi]->typeStrings.indexOfString(targetType, targetTypeLen);
+ if (!ti.has_value()) {
continue;
}
- ti += group->packages[pi]->typeIdOffset;
-
- const uint32_t identifier = findEntry(group, ti, name, nameLen,
+ *ti += group->packages[pi]->typeIdOffset;
+ const uint32_t identifier = findEntry(group, *ti, name, nameLen,
outTypeSpecFlags);
if (identifier != 0) {
if (fakePublic && outTypeSpecFlags) {
@@ -5009,8 +5113,9 @@
const size_t typeCount = typeList.size();
for (size_t i = 0; i < typeCount; i++) {
const Type* t = typeList[i];
- const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
- if (ei < 0) {
+ const base::expected<size_t, NullOrIOError> ei =
+ t->package->keyStrings.indexOfString(name, nameLen);
+ if (!ei.has_value()) {
continue;
}
@@ -5025,7 +5130,7 @@
continue;
}
- if (dtohl(entry->key.index) == (size_t) ei) {
+ if (dtohl(entry->key.index) == (size_t) *ei) {
uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index());
if (outTypeSpecFlags) {
Entry result;
@@ -6187,8 +6292,9 @@
for (size_t k = 0; k < numTypes; k++) {
const Type* type = typeList[k];
const ResStringPool& typeStrings = type->package->typeStrings;
- if (ignoreMipmap && typeStrings.string8ObjectAt(
- type->typeSpec->id - 1) == "mipmap") {
+ const base::expected<String8, NullOrIOError> typeStr = typeStrings.string8ObjectAt(
+ type->typeSpec->id - 1);
+ if (ignoreMipmap && typeStr.has_value() && *typeStr == "mipmap") {
continue;
}
@@ -6244,24 +6350,18 @@
StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
: mPool(pool), mIndex(index) {}
-const char* StringPoolRef::string8(size_t* outLen) const {
- if (mPool != NULL) {
- return mPool->string8At(mIndex, outLen);
+base::expected<StringPiece, NullOrIOError> StringPoolRef::string8() const {
+ if (LIKELY(mPool != NULL)) {
+ return mPool->string8At(mIndex);
}
- if (outLen != NULL) {
- *outLen = 0;
- }
- return NULL;
+ return base::unexpected(std::nullopt);
}
-const char16_t* StringPoolRef::string16(size_t* outLen) const {
- if (mPool != NULL) {
- return mPool->stringAt(mIndex, outLen);
+base::expected<StringPiece16, NullOrIOError> StringPoolRef::string16() const {
+ if (LIKELY(mPool != NULL)) {
+ return mPool->stringAt(mIndex);
}
- if (outLen != NULL) {
- *outLen = 0;
- }
- return NULL;
+ return base::unexpected(std::nullopt);
}
bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const {
@@ -7380,13 +7480,13 @@
printf("(dynamic attribute) 0x%08x\n", value.data);
} else if (value.dataType == Res_value::TYPE_STRING) {
size_t len;
- const char* str8 = pkg->header->values.string8At(
- value.data, &len);
+ const char* str8 = UnpackOptionalString(pkg->header->values.string8At(
+ value.data), &len);
if (str8 != NULL) {
printf("(string8) \"%s\"\n", normalizeForOutput(str8).string());
} else {
- const char16_t* str16 = pkg->header->values.stringAt(
- value.data, &len);
+ const char16_t* str16 = UnpackOptionalString(pkg->header->values.stringAt(
+ value.data), &len);
if (str16 != NULL) {
printf("(string16) \"%s\"\n",
normalizeForOutput(String8(str16, len).string()).string());
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index c63dff8..87fb2c0 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -48,61 +48,80 @@
!(has_type_separator && out_type->empty());
}
-bool ToResourceName(const StringPoolRef& type_string_ref,
- const StringPoolRef& entry_string_ref,
- const StringPiece& package_name,
- AssetManager2::ResourceName* out_name) {
- out_name->package = package_name.data();
- out_name->package_len = package_name.size();
+base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
+ const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref,
+ const StringPiece& package_name) {
+ AssetManager2::ResourceName name{
+ .package = package_name.data(),
+ .package_len = package_name.size(),
+ };
- out_name->type = type_string_ref.string8(&out_name->type_len);
- out_name->type16 = nullptr;
- if (out_name->type == nullptr) {
- out_name->type16 = type_string_ref.string16(&out_name->type_len);
- if (out_name->type16 == nullptr) {
- return false;
+ if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8();
+ type_str.ok()) {
+ name.type = type_str->data();
+ name.type_len = type_str->size();
+ } else if (UNLIKELY(IsIOError(type_str))) {
+ return base::unexpected(type_str.error());
+ }
+
+ if (name.type == nullptr) {
+ if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16();
+ type16_str.ok()) {
+ name.type16 = type16_str->data();
+ name.type_len = type16_str->size();
+ } else if (!type16_str.has_value()) {
+ return base::unexpected(type16_str.error());
}
}
- out_name->entry = entry_string_ref.string8(&out_name->entry_len);
- out_name->entry16 = nullptr;
- if (out_name->entry == nullptr) {
- out_name->entry16 = entry_string_ref.string16(&out_name->entry_len);
- if (out_name->entry16 == nullptr) {
- return false;
+ if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8();
+ entry_str.ok()) {
+ name.entry = entry_str->data();
+ name.entry_len = entry_str->size();
+ } else if (UNLIKELY(IsIOError(entry_str))) {
+ return base::unexpected(entry_str.error());
+ }
+
+ if (name.entry == nullptr) {
+ if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16();
+ entry16_str.ok()) {
+ name.entry16 = entry16_str->data();
+ name.entry_len = entry16_str->size();
+ } else if (!entry16_str.has_value()) {
+ return base::unexpected(entry16_str.error());
}
}
- return true;
+ return name;
}
-std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name) {
+std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name) {
std::string result;
- if (resource_name->package != nullptr) {
- result.append(resource_name->package, resource_name->package_len);
+ if (resource_name.package != nullptr) {
+ result.append(resource_name.package, resource_name.package_len);
}
- if (resource_name->type != nullptr || resource_name->type16 != nullptr) {
+ if (resource_name.type != nullptr || resource_name.type16 != nullptr) {
if (!result.empty()) {
result += ":";
}
- if (resource_name->type != nullptr) {
- result.append(resource_name->type, resource_name->type_len);
+ if (resource_name.type != nullptr) {
+ result.append(resource_name.type, resource_name.type_len);
} else {
- result += util::Utf16ToUtf8(StringPiece16(resource_name->type16, resource_name->type_len));
+ result += util::Utf16ToUtf8(StringPiece16(resource_name.type16, resource_name.type_len));
}
}
- if (resource_name->entry != nullptr || resource_name->entry16 != nullptr) {
+ if (resource_name.entry != nullptr || resource_name.entry16 != nullptr) {
if (!result.empty()) {
result += "/";
}
- if (resource_name->entry != nullptr) {
- result.append(resource_name->entry, resource_name->entry_len);
+ if (resource_name.entry != nullptr) {
+ result.append(resource_name.entry, resource_name.entry_len);
} else {
- result += util::Utf16ToUtf8(StringPiece16(resource_name->entry16, resource_name->entry_len));
+ result += util::Utf16ToUtf8(StringPiece16(resource_name.entry16, resource_name.entry_len));
}
}
diff --git a/libs/androidfw/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp
index b39b5f0..1c5e5d4 100644
--- a/libs/androidfw/StreamingZipInflater.cpp
+++ b/libs/androidfw/StreamingZipInflater.cpp
@@ -70,13 +70,13 @@
/*
* Streaming access to compressed data held in an mmapped region of memory
*/
-StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) {
+StreamingZipInflater::StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize) {
mFd = -1;
mDataMap = dataMap;
mOutTotalSize = uncompSize;
- mInTotalSize = dataMap->getDataLength();
+ mInTotalSize = dataMap->length();
- mInBuf = (uint8_t*) dataMap->getDataPtr();
+ mInBuf = (uint8_t*) dataMap->unsafe_data(); // IncFs safety handled in zlib.
mInBufSize = mInTotalSize;
mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE;
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index e77ac3d..52e7a70 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -233,6 +233,29 @@
}
/*
+ * Create a new incfs::IncFsFileMap object that spans the data in "entry".
+ */
+std::optional<incfs::IncFsFileMap> ZipFileRO::createEntryIncFsFileMap(ZipEntryRO entry) const
+{
+ const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
+ const ZipEntry& ze = zipEntry->entry;
+ int fd = GetFileDescriptor(mHandle);
+ size_t actualLen = 0;
+
+ if (ze.method == kCompressStored) {
+ actualLen = ze.uncompressed_length;
+ } else {
+ actualLen = ze.compressed_length;
+ }
+
+ incfs::IncFsFileMap newMap;
+ if (!newMap.Create(fd, ze.offset, actualLen, mFileName)) {
+ return std::nullopt;
+ }
+ return std::move(newMap);
+}
+
+/*
* Uncompress an entry, in its entirety, into the provided output buffer.
*
* This doesn't verify the data's CRC, which might be useful for
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 568e3b6..58fc5bb 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -40,7 +40,7 @@
explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
}
- bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+ bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override {
// Data is usually requested sequentially, so this helps avoid pointless
// fseeks every time we perform a read. There's an impedence mismatch
// here because the original API was designed around pread and pwrite.
@@ -71,7 +71,7 @@
explicit FdReader(int fd) : mFd(fd) {
}
- bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+ bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override {
return android::base::ReadFullyAtOffset(mFd, buf, len, offset);
}
@@ -81,22 +81,27 @@
class BufferReader : public zip_archive::Reader {
public:
- BufferReader(const void* input, size_t inputSize) : Reader(),
- mInput(reinterpret_cast<const uint8_t*>(input)),
+ BufferReader(incfs::map_ptr<void> input, size_t inputSize) : Reader(),
+ mInput(input.convert<uint8_t>()),
mInputSize(inputSize) {
}
- bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+ bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override {
if (mInputSize < len || offset > mInputSize - len) {
return false;
}
- memcpy(buf, mInput + offset, len);
+ const incfs::map_ptr<uint8_t> pos = mInput.offset(offset);
+ if (!pos.verify(len)) {
+ return false;
+ }
+
+ memcpy(buf, pos.unsafe_ptr(), len);
return true;
}
private:
- const uint8_t* mInput;
+ const incfs::map_ptr<uint8_t> mInput;
const size_t mInputSize;
};
@@ -138,7 +143,7 @@
return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0);
}
-/*static*/ bool ZipUtils::inflateToBuffer(const void* in, void* buf,
+/*static*/ bool ZipUtils::inflateToBuffer(incfs::map_ptr<void> in, void* buf,
long uncompressedLen, long compressedLen)
{
BufferReader reader(in, compressedLen);
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
index 96d44ab..5309ab2 100644
--- a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
@@ -31,9 +31,6 @@
using android::StringPiece;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-
- std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(reinterpret_cast<const char*>(data), size));
-
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(data, size);
return 0;
}
\ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 298509e..80bae20 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -23,18 +23,18 @@
#include <stdio.h>
#include <sys/types.h>
-
#include <memory>
+#include <optional>
#include <android-base/unique_fd.h>
+#include <util/map_ptr.h>
+
#include <utils/Compat.h>
#include <utils/Errors.h>
#include <utils/String8.h>
namespace android {
-class FileMap;
-
/*
* Instances of this class provide read-only operations on a byte stream.
*
@@ -49,6 +49,8 @@
class Asset {
public:
virtual ~Asset(void) = default;
+ Asset(const Asset& src) = delete;
+ Asset& operator=(const Asset& src) = delete;
static int32_t getGlobalCount();
static String8 getAssetAllocations();
@@ -87,8 +89,19 @@
/*
* Get a pointer to a buffer with the entire contents of the file.
+ * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary.
+ *
+ * Use this function if the asset can never reside on IncFs.
*/
- virtual const void* getBuffer(bool wordAligned) = 0;
+ virtual const void* getBuffer(bool aligned) = 0;
+
+ /*
+ * Get a incfs::map_ptr<void> to a buffer with the entire contents of the file.
+ * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary.
+ *
+ * Use this function if the asset can potentially reside on IncFs.
+ */
+ virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned) = 0;
/*
* Get the total amount of data that can be read.
@@ -152,10 +165,6 @@
AccessMode getAccessMode(void) const { return mAccessMode; }
private:
- /* these operations are not implemented */
- Asset(const Asset& src);
- Asset& operator=(const Asset& src);
-
/* AssetManager needs access to our "create" functions */
friend class AssetManager;
friend class ApkAssets;
@@ -169,8 +178,7 @@
/*
* Create the asset from a named, compressed file on disk (e.g. ".gz").
*/
- static Asset* createFromCompressedFile(const char* fileName,
- AccessMode mode);
+ static Asset* createFromCompressedFile(const char* fileName, AccessMode mode);
#if 0
/*
@@ -200,31 +208,21 @@
/*
* Create the asset from a memory-mapped file segment.
*
- * The asset takes ownership of the FileMap.
+ * The asset takes ownership of the incfs::IncFsFileMap and the file descriptor "fd". The
+ * file descriptor is used to request new file descriptors using "openFileDescriptor".
*/
- static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode);
-
- /*
- * Create the asset from a memory-mapped file segment.
- *
- * The asset takes ownership of the FileMap and the file descriptor "fd". The file descriptor is
- * used to request new file descriptors using "openFileDescriptor".
- */
- static std::unique_ptr<Asset> createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
- base::unique_fd fd, AccessMode mode);
+ static std::unique_ptr<Asset> createFromUncompressedMap(incfs::IncFsFileMap&& dataMap,
+ AccessMode mode,
+ base::unique_fd fd = {});
/*
* Create the asset from a memory-mapped file segment with compressed
* data.
*
- * The asset takes ownership of the FileMap.
+ * The asset takes ownership of the incfs::IncFsFileMap.
*/
- static Asset* createFromCompressedMap(FileMap* dataMap,
- size_t uncompressedLen, AccessMode mode);
-
- static std::unique_ptr<Asset> createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
- size_t uncompressedLen, AccessMode mode);
-
+ static std::unique_ptr<Asset> createFromCompressedMap(incfs::IncFsFileMap&& dataMap,
+ size_t uncompressedLen, AccessMode mode);
/*
* Create from a reference-counted chunk of shared memory.
@@ -252,7 +250,7 @@
class _FileAsset : public Asset {
public:
_FileAsset(void);
- virtual ~_FileAsset(void);
+ ~_FileAsset(void) override;
/*
* Use a piece of an already-open file.
@@ -266,21 +264,24 @@
*
* On success, the object takes ownership of "dataMap" and "fd".
*/
- status_t openChunk(FileMap* dataMap, base::unique_fd fd);
+ status_t openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd);
/*
* Standard Asset interfaces.
*/
- virtual ssize_t read(void* buf, size_t count);
- virtual off64_t seek(off64_t offset, int whence);
- virtual void close(void);
- virtual const void* getBuffer(bool wordAligned);
- virtual off64_t getLength(void) const { return mLength; }
- virtual off64_t getRemainingLength(void) const { return mLength-mOffset; }
- virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const;
- virtual bool isAllocated(void) const { return mBuf != NULL; }
+ ssize_t read(void* buf, size_t count) override;
+ off64_t seek(off64_t offset, int whence) override;
+ void close(void) override;
+ const void* getBuffer(bool aligned) override;
+ incfs::map_ptr<void> getIncFsBuffer(bool aligned) override;
+ off64_t getLength(void) const override { return mLength; }
+ off64_t getRemainingLength(void) const override { return mLength-mOffset; }
+ int openFileDescriptor(off64_t* outStart, off64_t* outLength) const override;
+ bool isAllocated(void) const override { return mBuf != NULL; }
private:
+ incfs::map_ptr<void> ensureAlignment(const incfs::IncFsFileMap& map);
+
off64_t mStart; // absolute file offset of start of chunk
off64_t mLength; // length of the chunk
off64_t mOffset; // current local offset, 0 == mStart
@@ -295,10 +296,8 @@
*/
enum { kReadVsMapThreshold = 4096 };
- FileMap* mMap; // for memory map
- unsigned char* mBuf; // for read
-
- const void* ensureAlignment(FileMap* map);
+ unsigned char* mBuf; // for read
+ std::optional<incfs::IncFsFileMap> mMap; // for memory map
};
@@ -323,7 +322,7 @@
*
* On success, the object takes ownership of "fd".
*/
- status_t openChunk(FileMap* dataMap, size_t uncompressedLen);
+ status_t openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen);
/*
* Standard Asset interfaces.
@@ -331,24 +330,23 @@
virtual ssize_t read(void* buf, size_t count);
virtual off64_t seek(off64_t offset, int whence);
virtual void close(void);
- virtual const void* getBuffer(bool wordAligned);
+ virtual const void* getBuffer(bool aligned);
+ virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned);
virtual off64_t getLength(void) const { return mUncompressedLen; }
virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; }
virtual int openFileDescriptor(off64_t* /* outStart */, off64_t* /* outLength */) const { return -1; }
virtual bool isAllocated(void) const { return mBuf != NULL; }
private:
- off64_t mStart; // offset to start of compressed data
- off64_t mCompressedLen; // length of the compressed data
- off64_t mUncompressedLen; // length of the uncompressed data
- off64_t mOffset; // current offset, 0 == start of uncomp data
+ off64_t mStart; // offset to start of compressed data
+ off64_t mCompressedLen; // length of the compressed data
+ off64_t mUncompressedLen; // length of the uncompressed data
+ off64_t mOffset; // current offset, 0 == start of uncomp data
+ int mFd; // for file input
- FileMap* mMap; // for memory-mapped input
- int mFd; // for file input
-
- class StreamingZipInflater* mZipInflater; // for streaming large compressed assets
-
- unsigned char* mBuf; // for getBuffer()
+ class StreamingZipInflater* mZipInflater; // for streaming large compressed assets
+ unsigned char* mBuf; // for getBuffer()
+ std::optional<incfs::IncFsFileMap> mMap; // for memory-mapped input
};
// need: shared mmap version?
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 30ef25c..a92694c 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -131,8 +131,8 @@
bool GetOverlayablesToString(const android::StringPiece& package_name,
std::string* out) const;
- const std::unordered_map<std::string, std::string>*
- GetOverlayableMapForPackage(uint32_t package_id) const;
+ const std::unordered_map<std::string, std::string>* GetOverlayableMapForPackage(
+ uint32_t package_id) const;
// Returns whether the resources.arsc of any loaded apk assets is allocated in RAM (not mmapped).
bool ContainsAllocatedTable() const;
@@ -145,14 +145,16 @@
return configuration_;
}
- // Returns all configurations for which there are resources defined. This includes resource
- // configurations in all the ApkAssets set for this AssetManager.
+ // Returns all configurations for which there are resources defined, or an I/O error if reading
+ // resource data failed.
+ //
+ // This includes resource configurations in all the ApkAssets set for this AssetManager.
// If `exclude_system` is set to true, resource configurations from system APKs
// ('android' package, other libraries) will be excluded from the list.
// If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
// will be excluded from the list.
- std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false,
- bool exclude_mipmap = false) const;
+ base::expected<std::set<ResTable_config>, IOError> GetResourceConfigurations(
+ bool exclude_system = false, bool exclude_mipmap = false) const;
// Returns all the locales for which there are resources defined. This includes resource
// locales in all the ApkAssets set for this AssetManager.
@@ -194,53 +196,109 @@
std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
Asset::AccessMode mode) const;
- // Populates the `out_name` parameter with resource name information.
- // Utf8 strings are preferred, and only if they are unavailable are
- // the Utf16 variants populated.
- // Returns false if the resource was not found or the name was missing/corrupt.
- bool GetResourceName(uint32_t resid, ResourceName* out_name) const;
-
- // Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
- // See ResTable_config for the list of configuration axis.
- // Returns false if the resource was not found.
- bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const;
+ // Returns the resource name of the specified resource ID.
+ //
+ // Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated.
+ //
+ // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data
+ // failed.
+ base::expected<ResourceName, NullOrIOError> GetResourceName(uint32_t resid) const;
// Finds the resource ID assigned to `resource_name`.
+ //
// `resource_name` must be of the form '[package:][type/]entry'.
// If no package is specified in `resource_name`, then `fallback_package` is used as the package.
// If no type is specified in `resource_name`, then `fallback_type` is used as the type.
- // Returns 0x0 if no resource by that name was found.
- uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {},
- const std::string& fallback_package = {}) const;
-
- // Retrieves the best matching resource with ID `resid`. The resource value is filled into
- // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
- // `out_flags` holds the same flags as retrieved with GetResourceFlags().
- // If `density_override` is non-zero, the configuration to match against is overridden with that
- // density.
//
- // Returns a valid cookie if the resource was found. If the resource was not found, or if the
- // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false,
- // this function logs if the resource was a map/bag type before returning kInvalidCookie.
- ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
- Res_value* out_value, ResTable_config* out_selected_config,
- uint32_t* out_flags) const;
+ // Returns a null error if no resource by that name was found, or an I/O error if reading resource
+ // data failed.
+ base::expected<uint32_t, NullOrIOError> GetResourceId(
+ const std::string& resource_name, const std::string& fallback_type = {},
+ const std::string& fallback_package = {}) const;
- // Resolves the resource reference in `in_out_value` if the data type is
- // Res_value::TYPE_REFERENCE.
- // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`.
- // `in_out_value` is the reference to resolve. The result is placed back into this object.
- // `in_out_flags` is the type spec flags returned from calls to GetResource() or
- // GetResourceFlags(). Configuration flags of the values pointed to by the reference
- // are OR'd together with `in_out_flags`.
- // `in_out_config` is populated with the configuration for which the resolved value was defined.
- // `out_last_reference` is populated with the last reference ID before resolving to an actual
- // value. This is only initialized if the passed in `in_out_value` is a reference.
- // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
- // it was not found.
- ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
- ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
- uint32_t* out_last_reference) const;
+ struct SelectedValue {
+ friend AssetManager2;
+ friend Theme;
+ SelectedValue() = default;
+ SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) :
+ cookie(entry.cookie), data(entry.value.data), type(entry.value.dataType),
+ flags(bag->type_spec_flags), resid(0U), config({}) {};
+
+ // The cookie representing the ApkAssets in which the value resides.
+ ApkAssetsCookie cookie = kInvalidCookie;
+
+ // The data for this value, as interpreted according to `type`.
+ Res_value::data_type data;
+
+ // Type of the data value.
+ uint8_t type;
+
+ // The bitmask of configuration axis that this resource varies with.
+ // See ResTable_config::CONFIG_*.
+ uint32_t flags;
+
+ // The resource ID from which this value was resolved.
+ uint32_t resid;
+
+ // The configuration for which the resolved value was defined.
+ ResTable_config config;
+
+ private:
+ SelectedValue(uint8_t value_type, Res_value::data_type value_data, ApkAssetsCookie cookie,
+ uint32_t type_flags, uint32_t resid, const ResTable_config& config) :
+ cookie(cookie), data(value_data), type(value_type), flags(type_flags),
+ resid(resid), config(config) {};
+ };
+
+ // Retrieves the best matching resource value with ID `resid`.
+ //
+ // If `may_be_bag` is false, this function logs if the resource was a map/bag type and returns a
+ // null result. If `density_override` is non-zero, the configuration to match against is
+ // overridden with that density.
+ //
+ // Returns a null error if a best match could not be found, or an I/O error if reading resource
+ // data failed.
+ base::expected<SelectedValue, NullOrIOError> GetResource(uint32_t resid, bool may_be_bag = false,
+ uint16_t density_override = 0U) const;
+
+ // Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE.
+ //
+ // If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the
+ // values pointed to by the reference are OR'd into `value.flags`. If `cache_value` is true, then
+ // the resolved value will be cached and used when attempting to resolve the resource id specified
+ // in `value`.
+ //
+ // Returns a null error if the resource could not be resolved, or an I/O error if reading
+ // resource data failed.
+ base::expected<std::monostate, NullOrIOError> ResolveReference(SelectedValue& value,
+ bool cache_value = false) const;
+
+ // Retrieves the best matching bag/map resource with ID `resid`.
+ //
+ // This method will resolve all parent references for this bag and merge keys with the child.
+ // To iterate over the keys, use the following idiom:
+ //
+ // base::expected<const ResolvedBag*, NullOrIOError> bag = asset_manager->GetBag(id);
+ // if (bag.has_value()) {
+ // for (auto iter = begin(*bag); iter != end(*bag); ++iter) {
+ // ...
+ // }
+ // }
+ //
+ // Returns a null error if a best match could not be found, or an I/O error if reading resource
+ // data failed.
+ base::expected<const ResolvedBag*, NullOrIOError> GetBag(uint32_t resid) const;
+
+ // Retrieves the best matching bag/map resource of the resource referenced in `value`.
+ //
+ // If `value.type` is not Res_value::TYPE_REFERENCE, a null result is returned.
+ // Configuration flags of the bag pointed to by the reference are OR'd into `value.flags`.
+ //
+ // Returns a null error if a best match could not be found, or an I/O error if reading resource
+ // data failed.
+ base::expected<const ResolvedBag*, NullOrIOError> ResolveBag(SelectedValue& value) const;
+
+ const std::vector<uint32_t> GetBagResIdStack(uint32_t resid) const;
// Resets the resource resolution structures in preparation for the next resource retrieval.
void ResetResourceResolution() const;
@@ -252,20 +310,6 @@
// resolved yet.
std::string GetLastResourceResolution() const;
- const std::vector<uint32_t> GetBagResIdStack(uint32_t resid);
-
- // Retrieves the best matching bag/map resource with ID `resid`.
- // This method will resolve all parent references for this bag and merge keys with the child.
- // To iterate over the keys, use the following idiom:
- //
- // const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id);
- // if (bag != nullptr) {
- // for (auto iter = begin(bag); iter != end(bag); ++iter) {
- // ...
- // }
- // }
- const ResolvedBag* GetBag(uint32_t resid);
-
// Creates a new Theme from this AssetManager.
std::unique_ptr<Theme> NewTheme();
@@ -286,11 +330,15 @@
private:
DISALLOW_COPY_AND_ASSIGN(AssetManager2);
+ struct TypeConfig {
+ incfs::verified_map_ptr<ResTable_type> type;
+ ResTable_config config;
+ };
+
// A collection of configurations and their associated ResTable_type that match the current
// AssetManager configuration.
struct FilteredConfigGroup {
- std::vector<ResTable_config> configurations;
- std::vector<const ResTable_type*> types;
+ std::vector<TypeConfig> type_configs;
};
// Represents an single package.
@@ -331,9 +379,7 @@
};
// Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
- // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
- // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
- // the ApkAssets in which the entry was found.
+ // Res_value, or a complex map/bag type. Returns a null result if a best entry cannot be found.
//
// `density_override` overrides the density of the current configuration when doing a search.
//
@@ -347,13 +393,15 @@
//
// NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
// bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
- ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
- bool ignore_configuration, FindEntryResult* out_entry) const;
+ base::expected<FindEntryResult, NullOrIOError> FindEntry(uint32_t resid,
+ uint16_t density_override,
+ bool stop_at_first_match,
+ bool ignore_configuration) const;
- ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx,
- uint16_t entry_idx, const ResTable_config& desired_config,
- bool /*stop_at_first_match*/,
- bool ignore_configuration, FindEntryResult* out_entry) const;
+ base::expected<FindEntryResult, NullOrIOError> FindEntryInternal(
+ const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx,
+ const ResTable_config& desired_config, bool stop_at_first_match,
+ bool ignore_configuration) const;
// Assigns package IDs to all shared library ApkAssets.
// Should be called whenever the ApkAssets are changed.
@@ -372,7 +420,8 @@
// AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
// been seen while traversing bag parents.
- const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
+ base::expected<const ResolvedBag*, NullOrIOError> GetBag(
+ uint32_t resid, std::vector<uint32_t>& child_resids) const;
// The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
// have a longer lifetime.
@@ -394,19 +443,20 @@
// Cached set of bags. These are cached because they can inherit keys from parent bags,
// which involves some calculation.
- std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
+ mutable std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
// Cached set of bag resid stacks for each bag. These are cached because they might be requested
// a number of times for each view during View inspection.
- std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_;
+ mutable std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_;
+
+ // Cached set of resolved resource values.
+ mutable std::unordered_map<uint32_t, SelectedValue> cached_resolved_values_;
// Whether or not to save resource resolution steps
bool resource_resolution_logging_enabled_ = false;
struct Resolution {
-
struct Step {
-
enum class Type {
INITIAL,
BETTER_MATCH,
@@ -455,55 +505,53 @@
public:
~Theme();
- // Applies the style identified by `resid` to this theme. This can be called
- // multiple times with different styles. By default, any theme attributes that
- // are already defined before this call are not overridden. If `force` is set
- // to true, this behavior is changed and all theme attributes from the style at
- // `resid` are applied.
- // Returns false if the style failed to apply.
- bool ApplyStyle(uint32_t resid, bool force = false);
+ // Applies the style identified by `resid` to this theme.
+ //
+ // This can be called multiple times with different styles. By default, any theme attributes that
+ // are already defined before this call are not overridden. If `force` is set to true, this
+ // behavior is changed and all theme attributes from the style at `resid` are applied.
+ //
+ // Returns a null error if the style could not be applied, or an I/O error if reading resource
+ // data failed.
+ base::expected<std::monostate, NullOrIOError> ApplyStyle(uint32_t resid, bool force = false);
- // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
- // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded
- // into both AssetManagers will be copied to this theme.
- void SetTo(const Theme& o);
+ // Sets this Theme to be a copy of `other` if `other` has the same AssetManager as this Theme.
+ //
+ // If `other` does not have the same AssetManager as this theme, only attributes from ApkAssets
+ // loaded into both AssetManagers will be copied to this theme.
+ //
+ // Returns an I/O error if reading resource data failed.
+ base::expected<std::monostate, IOError> SetTo(const Theme& other);
void Clear();
- void Dump() const;
+ // Retrieves the value of attribute ID `resid` in the theme.
+ //
+ // NOTE: This function does not do reference traversal. If you want to follow references to other
+ // resources to get the "real" value to use, you need to call ResolveReference() after this
+ // function.
+ std::optional<AssetManager2::SelectedValue> GetAttribute(uint32_t resid) const;
- inline const AssetManager2* GetAssetManager() const {
+ // This is like AssetManager2::ResolveReference(), but also takes care of resolving attribute
+ // references to the theme.
+ base::expected<std::monostate, NullOrIOError> ResolveAttributeReference(
+ AssetManager2::SelectedValue& value) const;
+
+ AssetManager2* GetAssetManager() {
return asset_manager_;
}
- inline AssetManager2* GetAssetManager() {
+ const AssetManager2* GetAssetManager() const {
return asset_manager_;
}
// Returns a bit mask of configuration changes that will impact this
// theme (and thus require completely reloading it).
- inline uint32_t GetChangingConfigurations() const {
+ uint32_t GetChangingConfigurations() const {
return type_spec_flags_;
}
- // Retrieve a value in the theme. If the theme defines this value, returns an asset cookie
- // indicating which ApkAssets it came from and populates `out_value` with the value.
- // `out_flags` is populated with a bitmask of the configuration axis with which the resource
- // varies.
- //
- // If the attribute is not found, returns kInvalidCookie.
- //
- // NOTE: This function does not do reference traversal. If you want to follow references to other
- // resources to get the "real" value to use, you need to call ResolveReference() after this
- // function.
- ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const;
-
- // This is like AssetManager2::ResolveReference(), but also takes
- // care of resolving attribute references to the theme.
- ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
- ResTable_config* in_out_selected_config = nullptr,
- uint32_t* in_out_type_spec_flags = nullptr,
- uint32_t* out_last_ref = nullptr) const;
+ void Dump() const;
private:
DISALLOW_COPY_AND_ASSIGN(Theme);
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
index d71aad2..1a69a30 100644
--- a/libs/androidfw/include/androidfw/AttributeResolution.h
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -45,20 +45,28 @@
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
-bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid,
- uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
+base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_style_attr,
+ uint32_t def_style_resid, uint32_t* src_values,
+ size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values,
+ uint32_t* out_indices);
// `out_values` must NOT be nullptr.
// `out_indices` is NOT optional and must NOT be nullptr.
-void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
- uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
- uint32_t* out_values, uint32_t* out_indices);
+base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* xml_parser,
+ uint32_t def_style_attr,
+ uint32_t def_style_resid,
+ const uint32_t* attrs, size_t attrs_length,
+ uint32_t* out_values, uint32_t* out_indices);
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
-bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
+base::expected<std::monostate, IOError> RetrieveAttributes(AssetManager2* assetmanager,
+ ResXMLParser* xml_parser,
+ uint32_t* attrs,
+ size_t attrs_length,
+ uint32_t* out_values,
+ uint32_t* out_indices);
} // namespace android
diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
index a0f2343..f1c43b2 100644
--- a/libs/androidfw/include/androidfw/Chunk.h
+++ b/libs/androidfw/include/androidfw/Chunk.h
@@ -36,7 +36,7 @@
// of the chunk.
class Chunk {
public:
- explicit Chunk(const ResChunk_header* chunk) : device_chunk_(chunk) {}
+ explicit Chunk(incfs::verified_map_ptr<ResChunk_header> chunk) : device_chunk_(chunk) {}
// Returns the type of the chunk. Caller need not worry about endianness.
inline int type() const { return dtohs(device_chunk_->type); }
@@ -49,21 +49,18 @@
inline size_t header_size() const { return dtohs(device_chunk_->headerSize); }
template <typename T, size_t MinSize = sizeof(T)>
- inline const T* header() const {
- if (header_size() >= MinSize) {
- return reinterpret_cast<const T*>(device_chunk_);
- }
- return nullptr;
+ inline incfs::map_ptr<T> header() const {
+ return (header_size() >= MinSize) ? device_chunk_.convert<T>() : nullptr;
}
- inline const void* data_ptr() const {
- return reinterpret_cast<const uint8_t*>(device_chunk_) + header_size();
+ inline incfs::map_ptr<void> data_ptr() const {
+ return device_chunk_.offset(header_size());
}
inline size_t data_size() const { return size() - header_size(); }
private:
- const ResChunk_header* device_chunk_;
+ const incfs::verified_map_ptr<ResChunk_header> device_chunk_;
};
// Provides a Java style iterator over an array of ResChunk_header's.
@@ -84,11 +81,11 @@
//
class ChunkIterator {
public:
- ChunkIterator(const void* data, size_t len)
- : next_chunk_(reinterpret_cast<const ResChunk_header*>(data)),
+ ChunkIterator(incfs::map_ptr<void> data, size_t len)
+ : next_chunk_(data.convert<ResChunk_header>()),
len_(len),
last_error_(nullptr) {
- CHECK(next_chunk_ != nullptr) << "data can't be nullptr";
+ CHECK((bool) next_chunk_) << "data can't be null";
if (len_ != 0) {
VerifyNextChunk();
}
@@ -113,7 +110,7 @@
// Returns false if there was an error. For legacy purposes.
bool VerifyNextChunkNonFatal();
- const ResChunk_header* next_chunk_;
+ incfs::map_ptr<ResChunk_header> next_chunk_;
size_t len_;
const char* last_error_;
bool last_error_was_fatal_ = true;
diff --git a/libs/androidfw/include/androidfw/Errors.h b/libs/androidfw/include/androidfw/Errors.h
new file mode 100644
index 0000000..948162d
--- /dev/null
+++ b/libs/androidfw/include/androidfw/Errors.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROIDFW_ERRORS_H_
+#define ANDROIDFW_ERRORS_H_
+
+#include <optional>
+#include <variant>
+
+#include <android-base/result.h>
+
+namespace android {
+
+enum class IOError {
+ // Used when reading a file residing on an IncFs file-system times out.
+ PAGES_MISSING = -1,
+};
+
+// Represents an absent result or an I/O error.
+using NullOrIOError = std::variant<std::nullopt_t, IOError>;
+
+// Checks whether the result holds an unexpected I/O error.
+template <typename T>
+static inline bool IsIOError(const base::expected<T, NullOrIOError> result) {
+ return !result.has_value() && std::holds_alternative<IOError>(result.error());
+}
+
+static inline IOError GetIOError(const NullOrIOError& error) {
+ return std::get<IOError>(error);
+}
+
+} // namespace android
+
+#endif //ANDROIDFW_ERRORS_H_
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index ab0f47f..fdab03b 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -40,8 +40,8 @@
class OverlayStringPool : public ResStringPool {
public:
virtual ~OverlayStringPool();
- const char16_t* stringAt(size_t idx, size_t* outLen) const override;
- const char* string8At(size_t idx, size_t* outLen) const override;
+ base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const override;
+ base::expected<StringPiece, NullOrIOError> string8At(size_t idx) const override;
size_t size() const override;
explicit OverlayStringPool(const LoadedIdmap* loaded_idmap);
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 89ff9f5..17d97a2 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -23,7 +23,8 @@
#include <unordered_map>
#include <unordered_set>
-#include "android-base/macros.h"
+#include <android-base/macros.h>
+#include <android-base/result.h>
#include "androidfw/ByteBucketArray.h"
#include "androidfw/Chunk.h"
@@ -49,7 +50,7 @@
// Pointer to the mmapped data where flags are kept.
// Flags denote whether the resource entry is public
// and under which configurations it varies.
- const ResTable_typeSpec* type_spec;
+ incfs::verified_map_ptr<ResTable_typeSpec> type_spec;
// The number of types that follow this struct.
// There is a type for each configuration that entries are defined for.
@@ -57,15 +58,17 @@
// Trick to easily access a variable number of Type structs
// proceeding this struct, and to ensure their alignment.
- const ResTable_type* types[0];
+ incfs::verified_map_ptr<ResTable_type> types[0];
- inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const {
+ base::expected<uint32_t, NullOrIOError> GetFlagsForEntryIndex(uint16_t entry_index) const {
if (entry_index >= dtohl(type_spec->entryCount)) {
- return 0u;
+ return 0U;
}
-
- const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1);
- return flags[entry_index];
+ const auto entry_flags_ptr = ((type_spec + 1).convert<uint32_t>() + entry_index);
+ if (!entry_flags_ptr) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ return entry_flags_ptr.value();
}
};
@@ -161,13 +164,17 @@
// the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
// Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible
// for patching the correct package ID to the resource ID.
- uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const;
+ base::expected<uint32_t, NullOrIOError> FindEntryByName(const std::u16string& type_name,
+ const std::u16string& entry_name) const;
- static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index);
+ static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntry(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
- static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index);
+ static base::expected<uint32_t, NullOrIOError> GetEntryOffset(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
- static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset);
+ static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntryFromOffset(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
// Returns the string pool where type names are stored.
inline const ResStringPool* GetTypeStringPool() const {
@@ -220,7 +227,8 @@
// Populates a set of ResTable_config structs, possibly excluding configurations defined for
// the mipmap type.
- void CollectConfigurations(bool exclude_mipmap, std::set<ResTable_config>* out_configs) const;
+ base::expected<std::monostate, IOError> CollectConfigurations(
+ bool exclude_mipmap, std::set<ResTable_config>* out_configs) const;
// Populates a set of strings representing locales.
// If `canonicalize` is set to true, each locale is transformed into its canonical format
@@ -300,7 +308,8 @@
// If `load_as_shared_library` is set to true, the application package (0x7f) is treated
// as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an
// ID.
- static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data,
+ static std::unique_ptr<const LoadedArsc> Load(incfs::map_ptr<void> data,
+ size_t length,
const LoadedIdmap* loaded_idmap = nullptr,
package_property_t property_flags = 0U);
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 04ba78b..fb5f864 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -20,7 +20,10 @@
#ifndef _LIBS_UTILS_RESOURCE_TYPES_H
#define _LIBS_UTILS_RESOURCE_TYPES_H
+#include <android-base/expected.h>
+
#include <androidfw/Asset.h>
+#include <androidfw/Errors.h>
#include <androidfw/LocaleData.h>
#include <androidfw/StringPiece.h>
#include <utils/Errors.h>
@@ -497,7 +500,7 @@
virtual ~ResStringPool();
void setToEmpty();
- status_t setTo(const void* data, size_t size, bool copyData=false);
+ status_t setTo(incfs::map_ptr<void> data, size_t size, bool copyData=false);
status_t getError() const;
@@ -505,48 +508,49 @@
// Return string entry as UTF16; if the pool is UTF8, the string will
// be converted before returning.
- inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const {
- return stringAt(ref.index, outLen);
+ inline base::expected<StringPiece16, NullOrIOError> stringAt(
+ const ResStringPool_ref& ref) const {
+ return stringAt(ref.index);
}
- virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
+ virtual base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const;
// Note: returns null if the string pool is not UTF8.
- virtual const char* string8At(size_t idx, size_t* outLen) const;
+ virtual base::expected<StringPiece, NullOrIOError> string8At(size_t idx) const;
// Return string whether the pool is UTF8 or UTF16. Does not allow you
// to distinguish null.
- const String8 string8ObjectAt(size_t idx) const;
+ base::expected<String8, IOError> string8ObjectAt(size_t idx) const;
- const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const;
- const ResStringPool_span* styleAt(size_t idx) const;
+ base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> styleAt(
+ const ResStringPool_ref& ref) const;
+ base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> styleAt(size_t idx) const;
- ssize_t indexOfString(const char16_t* str, size_t strLen) const;
+ base::expected<size_t, NullOrIOError> indexOfString(const char16_t* str, size_t strLen) const;
virtual size_t size() const;
size_t styleCount() const;
size_t bytes() const;
- const void* data() const;
-
+ incfs::map_ptr<void> data() const;
bool isSorted() const;
bool isUTF8() const;
private:
- status_t mError;
- void* mOwnedData;
- const ResStringPool_header* mHeader;
- size_t mSize;
- mutable Mutex mDecodeLock;
- const uint32_t* mEntries;
- const uint32_t* mEntryStyles;
- const void* mStrings;
- char16_t mutable** mCache;
- uint32_t mStringPoolSize; // number of uint16_t
- const uint32_t* mStyles;
- uint32_t mStylePoolSize; // number of uint32_t
+ status_t mError;
+ void* mOwnedData;
+ incfs::verified_map_ptr<ResStringPool_header> mHeader;
+ size_t mSize;
+ mutable Mutex mDecodeLock;
+ incfs::map_ptr<uint32_t> mEntries;
+ incfs::map_ptr<uint32_t> mEntryStyles;
+ incfs::map_ptr<void> mStrings;
+ char16_t mutable** mCache;
+ uint32_t mStringPoolSize; // number of uint16_t
+ incfs::map_ptr<uint32_t> mStyles;
+ uint32_t mStylePoolSize; // number of uint32_t
- const char* stringDecodeAt(size_t idx, const uint8_t* str, const size_t encLen,
- size_t* outLen) const;
+ base::expected<StringPiece, NullOrIOError> stringDecodeAt(
+ size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const;
};
/**
@@ -558,8 +562,8 @@
StringPoolRef() = default;
StringPoolRef(const ResStringPool* pool, uint32_t index);
- const char* string8(size_t* outLen) const;
- const char16_t* string16(size_t* outLen) const;
+ base::expected<StringPiece, NullOrIOError> string8() const;
+ base::expected<StringPiece16, NullOrIOError> string16() const;
private:
const ResStringPool* mPool = nullptr;
@@ -1797,6 +1801,16 @@
bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
+template<typename TChar, typename E>
+static const TChar* UnpackOptionalString(base::expected<BasicStringPiece<TChar>, E>&& result,
+ size_t* outLen) {
+ if (result.has_value()) {
+ *outLen = result->size();
+ return result->data();
+ }
+ return NULL;
+}
+
/**
* Convenience class for accessing data in a ResTable resource.
*/
diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h
index e649940..bd1c440 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -30,13 +30,12 @@
// Convert a type_string_ref, entry_string_ref, and package to AssetManager2::ResourceName.
// Useful for getting resource name without re-running AssetManager2::FindEntry searches.
-bool ToResourceName(const StringPoolRef& type_string_ref,
- const StringPoolRef& entry_string_ref,
- const StringPiece& package_name,
- AssetManager2::ResourceName* out_name);
+base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
+ const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref,
+ const StringPiece& package_name);
// Formats a ResourceName to "package:type/entry_name".
-std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name);
+std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name);
inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) {
return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24);
diff --git a/libs/androidfw/include/androidfw/StreamingZipInflater.h b/libs/androidfw/include/androidfw/StreamingZipInflater.h
index 3ace5d5..472b794b 100644
--- a/libs/androidfw/include/androidfw/StreamingZipInflater.h
+++ b/libs/androidfw/include/androidfw/StreamingZipInflater.h
@@ -19,6 +19,8 @@
#include <unistd.h>
#include <inttypes.h>
+
+#include <util/map_ptr.h>
#include <zlib.h>
#include <utils/Compat.h>
@@ -34,7 +36,7 @@
StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize);
// Flavor that gets the compressed data from an in-memory buffer
- StreamingZipInflater(class FileMap* dataMap, size_t uncompSize);
+ StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize);
~StreamingZipInflater();
@@ -54,7 +56,7 @@
// where to find the uncompressed data
int mFd;
off64_t mInFileStart; // where the compressed data lives in the file
- class FileMap* mDataMap;
+ const incfs::IncFsFileMap* mDataMap;
z_stream mInflateState;
bool mStreamNeedsInit;
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index 9a3646b..aceeecc 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -22,7 +22,8 @@
#include <sstream>
#include <vector>
-#include "android-base/macros.h"
+#include <android-base/macros.h>
+#include <util/map_ptr.h>
#include "androidfw/StringPiece.h"
@@ -126,6 +127,11 @@
std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
+template <typename T>
+bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
+ return ((size_t)data.unsafe_ptr() & 0x3U) == 0;
+}
+
} // namespace util
} // namespace android
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index c221e3b..10f6d06 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -30,17 +30,20 @@
#ifndef __LIBS_ZIPFILERO_H
#define __LIBS_ZIPFILERO_H
-#include <utils/Compat.h>
-#include <utils/Errors.h>
-#include <utils/FileMap.h>
-#include <utils/threads.h>
-
+#include <optional>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
+#include <util/map_ptr.h>
+
+#include <utils/Compat.h>
+#include <utils/Errors.h>
+#include <utils/FileMap.h>
+#include <utils/threads.h>
+
struct ZipArchive;
typedef ZipArchive* ZipArchiveHandle;
@@ -136,14 +139,26 @@
uint32_t* pCrc32) const;
/*
- * Create a new FileMap object that maps a subset of the archive. For
+ * Create a new FileMap object that maps a subset of the archive. For
* an uncompressed entry this effectively provides a pointer to the
* actual data, for a compressed entry this provides the input buffer
* for inflate().
+ *
+ * Use this function if the archive can never reside on IncFs.
*/
FileMap* createEntryFileMap(ZipEntryRO entry) const;
/*
+ * Create a new incfs::IncFsFileMap object that maps a subset of the archive. For
+ * an uncompressed entry this effectively provides a pointer to the
+ * actual data, for a compressed entry this provides the input buffer
+ * for inflate().
+ *
+ * Use this function if the archive can potentially reside on IncFs.
+ */
+ std::optional<incfs::IncFsFileMap> createEntryIncFsFileMap(ZipEntryRO entry) const;
+
+ /*
* Uncompress the data into a buffer. Depending on the compression
* format, this is either an "inflate" operation or a memcpy.
*
diff --git a/libs/androidfw/include/androidfw/ZipUtils.h b/libs/androidfw/include/androidfw/ZipUtils.h
index 4d35e99..dbfec34 100644
--- a/libs/androidfw/include/androidfw/ZipUtils.h
+++ b/libs/androidfw/include/androidfw/ZipUtils.h
@@ -25,6 +25,8 @@
#include <stdio.h>
#include <time.h>
+#include "util/map_ptr.h"
+
namespace android {
/*
@@ -40,8 +42,8 @@
long compressedLen);
static bool inflateToBuffer(int fd, void* buf, long uncompressedLen,
long compressedLen);
- static bool inflateToBuffer(const void *in, void* buf, long uncompressedLen,
- long compressedLen);
+ static bool inflateToBuffer(incfs::map_ptr<void> in, void* buf,
+ long uncompressedLen, long compressedLen);
/*
* Someday we might want to make this generic and handle bzip2 ".bz2"
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 437e147..c7ae618 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -139,9 +139,13 @@
assets.SetApkAssets({apk.get()});
while (state.KeepRunning()) {
- const ResolvedBag* bag = assets.GetBag(app::R::style::StyleTwo);
- const auto bag_end = end(bag);
- for (auto iter = begin(bag); iter != bag_end; ++iter) {
+ auto bag = assets.GetBag(app::R::style::StyleTwo);
+ if (!bag.has_value()) {
+ state.SkipWithError("Failed to load get bag");
+ return;
+ }
+ const auto bag_end = end(*bag);
+ for (auto iter = begin(*bag); iter != bag_end; ++iter) {
uint32_t key = iter->key;
Res_value value = iter->value;
benchmark::DoNotOptimize(key);
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 8c255d1..471b0ee 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -108,24 +108,18 @@
assetmanager.SetConfiguration(desired_config);
assetmanager.SetApkAssets({basic_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
// Came from our ApkAssets.
- EXPECT_EQ(0, cookie);
+ EXPECT_EQ(0, value->cookie);
// It is the default config.
- EXPECT_EQ(0, selected_config.language[0]);
- EXPECT_EQ(0, selected_config.language[1]);
+ EXPECT_EQ(0, value->config.language[0]);
+ EXPECT_EQ(0, value->config.language[1]);
// It is a string.
- EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+ EXPECT_EQ(Res_value::TYPE_STRING, value->type);
}
TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) {
@@ -138,24 +132,18 @@
assetmanager.SetConfiguration(desired_config);
assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
// Came from our de_fr ApkAssets.
- EXPECT_EQ(1, cookie);
+ EXPECT_EQ(1, value->cookie);
// The configuration is German.
- EXPECT_EQ('d', selected_config.language[0]);
- EXPECT_EQ('e', selected_config.language[1]);
+ EXPECT_EQ('d', value->config.language[0]);
+ EXPECT_EQ('e', value->config.language[1]);
// It is a string.
- EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+ EXPECT_EQ(Res_value::TYPE_STRING, value->type);
}
TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) {
@@ -166,44 +154,35 @@
assetmanager.SetApkAssets(
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(libclient::R::string::foo_one, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(libclient::R::string::foo_one);
+ ASSERT_TRUE(value.has_value());
// Reference comes from libclient.
- EXPECT_EQ(2, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+ EXPECT_EQ(2, value->cookie);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
// Lookup the reference.
- cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/,
- &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(1, cookie);
- EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+ value = assetmanager.GetResource(value->data);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(1, value->cookie);
+ EXPECT_EQ(Res_value::TYPE_STRING, value->type);
EXPECT_EQ(std::string("Foo from lib_one"),
- GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data));
+ GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data));
- cookie = assetmanager.GetResource(libclient::R::string::foo_two, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ value = assetmanager.GetResource(libclient::R::string::foo_two);
+ ASSERT_TRUE(value.has_value());
// Reference comes from libclient.
- EXPECT_EQ(2, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+ EXPECT_EQ(2, value->cookie);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
// Lookup the reference.
- cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/,
- &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(0, cookie);
- EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+ value = assetmanager.GetResource(value->data);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(0, value->cookie);
+ EXPECT_EQ(Res_value::TYPE_STRING, value->type);
EXPECT_EQ(std::string("Foo from lib_two"),
- GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data));
+ GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data));
}
TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) {
@@ -211,16 +190,10 @@
assetmanager.SetApkAssets({appaslib_assets_.get()});
// The appaslib package will have been assigned the package ID 0x02.
-
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
- ApkAssetsCookie cookie = assetmanager.GetResource(
- fix_package_id(appaslib::R::integer::number1, 0x02), false /*may_be_bag*/,
- 0u /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data);
+ auto value = assetmanager.GetResource(fix_package_id(appaslib::R::integer::number1, 0x02));
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value->data);
}
TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) {
@@ -238,40 +211,40 @@
return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get());
};
- ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f);
- ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03);
- ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02);
+ ASSERT_EQ(0x7f, get_first_package_id(overlayable_assets_.get()));
+ ASSERT_EQ(0x03, get_first_package_id(overlay_assets_.get()));
+ ASSERT_EQ(0x02, get_first_package_id(lib_one_assets_.get()));
}
TEST_F(AssetManager2Test, GetSharedLibraryResourceName) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({lib_one_assets_.get()});
- AssetManager2::ResourceName name;
- ASSERT_TRUE(assetmanager.GetResourceName(lib_one::R::string::foo, &name));
- std::string formatted_name = ToFormattedResourceString(&name);
- ASSERT_EQ(formatted_name, "com.android.lib_one:string/foo");
+ auto name = assetmanager.GetResourceName(lib_one::R::string::foo);
+ ASSERT_TRUE(name.has_value());
+ ASSERT_EQ("com.android.lib_one:string/foo", ToFormattedResourceString(*name));
}
TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
- const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1);
- ASSERT_NE(nullptr, bag);
- ASSERT_EQ(3u, bag->entry_count);
+ auto bag = assetmanager.GetBag(basic::R::array::integerArray1);
+ ASSERT_TRUE(bag.has_value());
- EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[0].value.dataType);
- EXPECT_EQ(1u, bag->entries[0].value.data);
- EXPECT_EQ(0, bag->entries[0].cookie);
+ ASSERT_EQ(3u, (*bag)->entry_count);
- EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[1].value.dataType);
- EXPECT_EQ(2u, bag->entries[1].value.data);
- EXPECT_EQ(0, bag->entries[1].cookie);
+ EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[0].value.dataType);
+ EXPECT_EQ(1u, (*bag)->entries[0].value.data);
+ EXPECT_EQ(0, (*bag)->entries[0].cookie);
- EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[2].value.dataType);
- EXPECT_EQ(3u, bag->entries[2].value.data);
- EXPECT_EQ(0, bag->entries[2].cookie);
+ EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[1].value.dataType);
+ EXPECT_EQ(2u, (*bag)->entries[1].value.data);
+ EXPECT_EQ(0, (*bag)->entries[1].cookie);
+
+ EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[2].value.dataType);
+ EXPECT_EQ(3u, (*bag)->entries[2].value.data);
+ EXPECT_EQ(0, (*bag)->entries[2].cookie);
}
TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {}
@@ -284,15 +257,16 @@
assetmanager.SetApkAssets(
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
- const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03));
- ASSERT_NE(nullptr, bag);
- ASSERT_GE(bag->entry_count, 2u);
+ auto bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03));
+ ASSERT_TRUE(bag.has_value());
+
+ ASSERT_GE((*bag)->entry_count, 2u);
// First two attributes come from lib_one.
- EXPECT_EQ(1, bag->entries[0].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[0].key));
- EXPECT_EQ(1, bag->entries[1].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+ EXPECT_EQ(1, (*bag)->entries[0].cookie);
+ EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key));
+ EXPECT_EQ(1, (*bag)->entries[1].cookie);
+ EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
}
TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) {
@@ -303,17 +277,17 @@
assetmanager.SetApkAssets(
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
- const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib);
- ASSERT_NE(nullptr, bag);
- ASSERT_EQ(bag->entry_count, 2u);
+ auto bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib);
+ ASSERT_TRUE(bag.has_value());
+ ASSERT_EQ((*bag)->entry_count, 2u);
// First attribute comes from lib_two.
- EXPECT_EQ(2, bag->entries[0].cookie);
- EXPECT_EQ(0x02, get_package_id(bag->entries[0].key));
+ EXPECT_EQ(2, (*bag)->entries[0].cookie);
+ EXPECT_EQ(0x02, get_package_id((*bag)->entries[0].key));
// The next two attributes come from lib_one.
- EXPECT_EQ(2, bag->entries[1].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+ EXPECT_EQ(2, (*bag)->entries[1].cookie);
+ EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
}
TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
@@ -324,79 +298,79 @@
assetmanager.SetApkAssets(
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
- const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme);
- ASSERT_NE(nullptr, bag);
- ASSERT_GE(bag->entry_count, 2u);
+ auto bag = assetmanager.GetBag(libclient::R::style::Theme);
+ ASSERT_TRUE(bag.has_value());
+ ASSERT_GE((*bag)->entry_count, 2u);
// First two attributes come from lib_one.
- EXPECT_EQ(1, bag->entries[0].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[0].key));
- EXPECT_EQ(1, bag->entries[1].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+ EXPECT_EQ(1, (*bag)->entries[0].cookie);
+ EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key));
+ EXPECT_EQ(1, (*bag)->entries[1].cookie);
+ EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
}
TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({style_assets_.get()});
- const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleOne);
- ASSERT_NE(nullptr, bag_one);
- ASSERT_EQ(2u, bag_one->entry_count);
+ auto bag_one = assetmanager.GetBag(app::R::style::StyleOne);
+ ASSERT_TRUE(bag_one.has_value());
+ ASSERT_EQ(2u, (*bag_one)->entry_count);
- EXPECT_EQ(app::R::attr::attr_one, bag_one->entries[0].key);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[0].value.dataType);
- EXPECT_EQ(1u, bag_one->entries[0].value.data);
- EXPECT_EQ(0, bag_one->entries[0].cookie);
+ EXPECT_EQ(app::R::attr::attr_one, (*bag_one)->entries[0].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[0].value.dataType);
+ EXPECT_EQ(1u, (*bag_one)->entries[0].value.data);
+ EXPECT_EQ(0, (*bag_one)->entries[0].cookie);
- EXPECT_EQ(app::R::attr::attr_two, bag_one->entries[1].key);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[1].value.dataType);
- EXPECT_EQ(2u, bag_one->entries[1].value.data);
- EXPECT_EQ(0, bag_one->entries[1].cookie);
+ EXPECT_EQ(app::R::attr::attr_two, (*bag_one)->entries[1].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[1].value.dataType);
+ EXPECT_EQ(2u, (*bag_one)->entries[1].value.data);
+ EXPECT_EQ(0, (*bag_one)->entries[1].cookie);
- const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
- ASSERT_NE(nullptr, bag_two);
- ASSERT_EQ(6u, bag_two->entry_count);
+ auto bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
+ ASSERT_TRUE(bag_two.has_value());
+ ASSERT_EQ(6u, (*bag_two)->entry_count);
// attr_one is inherited from StyleOne.
- EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[0].value.dataType);
- EXPECT_EQ(1u, bag_two->entries[0].value.data);
- EXPECT_EQ(0, bag_two->entries[0].cookie);
- EXPECT_EQ(app::R::style::StyleOne, bag_two->entries[0].style);
+ EXPECT_EQ(app::R::attr::attr_one, (*bag_two)->entries[0].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[0].value.dataType);
+ EXPECT_EQ(1u, (*bag_two)->entries[0].value.data);
+ EXPECT_EQ(0, (*bag_two)->entries[0].cookie);
+ EXPECT_EQ(app::R::style::StyleOne, (*bag_two)->entries[0].style);
// attr_two should be overridden from StyleOne by StyleTwo.
- EXPECT_EQ(app::R::attr::attr_two, bag_two->entries[1].key);
- EXPECT_EQ(Res_value::TYPE_STRING, bag_two->entries[1].value.dataType);
- EXPECT_EQ(0, bag_two->entries[1].cookie);
- EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[1].style);
+ EXPECT_EQ(app::R::attr::attr_two, (*bag_two)->entries[1].key);
+ EXPECT_EQ(Res_value::TYPE_STRING, (*bag_two)->entries[1].value.dataType);
+ EXPECT_EQ(0, (*bag_two)->entries[1].cookie);
+ EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[1].style);
EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0),
- bag_two->entries[1].value.data));
+ (*bag_two)->entries[1].value.data));
// The rest are new attributes.
- EXPECT_EQ(app::R::attr::attr_three, bag_two->entries[2].key);
- EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, bag_two->entries[2].value.dataType);
- EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[2].value.data);
- EXPECT_EQ(0, bag_two->entries[2].cookie);
- EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[2].style);
+ EXPECT_EQ(app::R::attr::attr_three, (*bag_two)->entries[2].key);
+ EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, (*bag_two)->entries[2].value.dataType);
+ EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[2].value.data);
+ EXPECT_EQ(0, (*bag_two)->entries[2].cookie);
+ EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[2].style);
- EXPECT_EQ(app::R::attr::attr_five, bag_two->entries[3].key);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, bag_two->entries[3].value.dataType);
- EXPECT_EQ(app::R::string::string_one, bag_two->entries[3].value.data);
- EXPECT_EQ(0, bag_two->entries[3].cookie);
- EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[3].style);
+ EXPECT_EQ(app::R::attr::attr_five, (*bag_two)->entries[3].key);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, (*bag_two)->entries[3].value.dataType);
+ EXPECT_EQ(app::R::string::string_one, (*bag_two)->entries[3].value.data);
+ EXPECT_EQ(0, (*bag_two)->entries[3].cookie);
+ EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[3].style);
- EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[4].key);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType);
- EXPECT_EQ(3u, bag_two->entries[4].value.data);
- EXPECT_EQ(0, bag_two->entries[4].cookie);
- EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[4].style);
+ EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[4].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[4].value.dataType);
+ EXPECT_EQ(3u, (*bag_two)->entries[4].value.data);
+ EXPECT_EQ(0, (*bag_two)->entries[4].cookie);
+ EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[4].style);
- EXPECT_EQ(app::R::attr::attr_empty, bag_two->entries[5].key);
- EXPECT_EQ(Res_value::TYPE_NULL, bag_two->entries[5].value.dataType);
- EXPECT_EQ(Res_value::DATA_NULL_EMPTY, bag_two->entries[5].value.data);
- EXPECT_EQ(0, bag_two->entries[5].cookie);
- EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[5].style);
+ EXPECT_EQ(app::R::attr::attr_empty, (*bag_two)->entries[5].key);
+ EXPECT_EQ(Res_value::TYPE_NULL, (*bag_two)->entries[5].value.dataType);
+ EXPECT_EQ(Res_value::DATA_NULL_EMPTY, (*bag_two)->entries[5].value.data);
+ EXPECT_EQ(0, (*bag_two)->entries[5].cookie);
+ EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[5].style);
}
TEST_F(AssetManager2Test, MergeStylesCircularDependency) {
@@ -405,55 +379,41 @@
// GetBag should stop traversing the parents of styles when a circular
// dependency is detected
- const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleFour);
- ASSERT_NE(nullptr, bag_one);
- ASSERT_EQ(3u, bag_one->entry_count);
+ auto bag = assetmanager.GetBag(app::R::style::StyleFour);
+ ASSERT_TRUE(bag.has_value());
+ ASSERT_EQ(3u, (*bag)->entry_count);
}
TEST_F(AssetManager2Test, ResolveReferenceToResource) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::integer::ref1, false /*may_be_bag*/,
- 0u /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::integer::ref1);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(basic::R::integer::ref2, value->data);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(basic::R::integer::ref2, value.data);
-
- uint32_t last_ref = 0u;
- cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(12000u, value.data);
- EXPECT_EQ(basic::R::integer::ref2, last_ref);
+ auto result = assetmanager.ResolveReference(*value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(12000u, value->data);
+ EXPECT_EQ(basic::R::integer::ref2, value->resid);
}
TEST_F(AssetManager2Test, ResolveReferenceToBag) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/,
- 0u /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(basic::R::array::integerArray1, value->data);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(basic::R::array::integerArray1, value.data);
-
- uint32_t last_ref = 0u;
- cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(basic::R::array::integerArray1, value.data);
- EXPECT_EQ(basic::R::array::integerArray1, last_ref);
+ auto result = assetmanager.ResolveReference(*value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(basic::R::array::integerArray1, value->data);
+ EXPECT_EQ(basic::R::array::integerArray1, value->resid);
}
TEST_F(AssetManager2Test, ResolveDeepIdReference) {
@@ -461,50 +421,107 @@
assetmanager.SetApkAssets({basic_assets_.get()});
// Set up the resource ids
- const uint32_t high_ref = assetmanager
- .GetResourceId("@id/high_ref", "values", "com.android.basic");
- ASSERT_NE(high_ref, 0u);
- const uint32_t middle_ref = assetmanager
- .GetResourceId("@id/middle_ref", "values", "com.android.basic");
- ASSERT_NE(middle_ref, 0u);
- const uint32_t low_ref = assetmanager
- .GetResourceId("@id/low_ref", "values", "com.android.basic");
- ASSERT_NE(low_ref, 0u);
+ auto high_ref = assetmanager.GetResourceId("@id/high_ref", "values", "com.android.basic");
+ ASSERT_TRUE(high_ref.has_value());
+
+ auto middle_ref = assetmanager.GetResourceId("@id/middle_ref", "values", "com.android.basic");
+ ASSERT_TRUE(middle_ref.has_value());
+
+ auto low_ref = assetmanager.GetResourceId("@id/low_ref", "values", "com.android.basic");
+ ASSERT_TRUE(low_ref.has_value());
// Retrieve the most shallow resource
- Res_value value;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = assetmanager.GetResource(high_ref, false /*may_be_bag*/,
- 0 /*density_override*/,
- &value, &config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(middle_ref, value.data);
+ auto value = assetmanager.GetResource(*high_ref);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(*middle_ref, value->data);;
// Check that resolving the reference resolves to the deepest id
- uint32_t last_ref = high_ref;
- assetmanager.ResolveReference(cookie, &value, &config, &flags, &last_ref);
- EXPECT_EQ(last_ref, low_ref);
+ auto result = assetmanager.ResolveReference(*value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(*low_ref, value->resid);
}
TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
- ResTable_config selected_config;
- memset(&selected_config, 0, sizeof(selected_config));
+ // Create some kind of value that is NOT a reference.
+ AssetManager2::SelectedValue value{};
+ value.cookie = 1;
+ value.type = Res_value::TYPE_STRING;
+ value.resid = basic::R::string::test1;
- uint32_t flags = 0u;
+ auto result = assetmanager.ResolveReference(value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(1, value.cookie);
+ EXPECT_EQ(basic::R::string::test1, value.resid);
+}
- // Create some kind of Res_value that is NOT a reference.
- Res_value value;
- value.dataType = Res_value::TYPE_STRING;
- value.data = 0;
+TEST_F(AssetManager2Test, ResolveReferenceMissingResourceDoNotCacheFlags) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({basic_assets_.get()});
+ {
+ AssetManager2::SelectedValue value{};
+ value.data = basic::R::string::test1;
+ value.type = Res_value::TYPE_REFERENCE;
+ value.flags = ResTable_config::CONFIG_KEYBOARD;
- uint32_t last_ref = basic::R::string::test1;
- EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref));
- EXPECT_EQ(basic::R::string::test1, last_ref);
+ auto result = assetmanager.ResolveReference(value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_STRING, value.type);
+ EXPECT_EQ(0, value.cookie);
+ EXPECT_EQ(basic::R::string::test1, value.resid);
+ EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_KEYBOARD, value.flags);
+ }
+ {
+ AssetManager2::SelectedValue value{};
+ value.data = basic::R::string::test1;
+ value.type = Res_value::TYPE_REFERENCE;
+ value.flags = ResTable_config::CONFIG_COLOR_MODE;
+
+ auto result = assetmanager.ResolveReference(value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_STRING, value.type);
+ EXPECT_EQ(0, value.cookie);
+ EXPECT_EQ(basic::R::string::test1, value.resid);
+ EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_COLOR_MODE, value.flags);
+ }
+}
+
+TEST_F(AssetManager2Test, ResolveReferenceMissingResource) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({basic_assets_.get()});
+
+ const uint32_t kMissingResId = 0x8001ffff;
+ AssetManager2::SelectedValue value{};
+ value.type = Res_value::TYPE_REFERENCE;
+ value.data = kMissingResId;
+
+ auto result = assetmanager.ResolveReference(value);
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value.type);
+ EXPECT_EQ(kMissingResId, value.data);
+ EXPECT_EQ(kMissingResId, value.resid);
+ EXPECT_EQ(-1, value.cookie);
+ EXPECT_EQ(0, value.flags);
+}
+
+TEST_F(AssetManager2Test, ResolveReferenceMissingResourceLib) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({libclient_assets_.get()});
+
+ AssetManager2::SelectedValue value{};
+ value.type = Res_value::TYPE_REFERENCE;
+ value.data = libclient::R::string::foo_one;
+
+ auto result = assetmanager.ResolveReference(value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_DYNAMIC_REFERENCE, value.type);
+ EXPECT_EQ(lib_one::R::string::foo, value.data);
+ EXPECT_EQ(libclient::R::string::foo_one, value.resid);
+ EXPECT_EQ(0, value.cookie);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value.flags);
}
static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations,
@@ -516,43 +533,45 @@
AssetManager2 assetmanager;
assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()});
- std::set<ResTable_config> configurations = assetmanager.GetResourceConfigurations();
+ auto configurations = assetmanager.GetResourceConfigurations();
+ ASSERT_TRUE(configurations.has_value());
// We expect the locale sv from the system assets, and de and fr from basic_de_fr assets.
// And one extra for the default configuration.
- EXPECT_EQ(4u, configurations.size());
+ EXPECT_EQ(4u, configurations->size());
ResTable_config expected_config;
memset(&expected_config, 0, sizeof(expected_config));
expected_config.language[0] = 's';
expected_config.language[1] = 'v';
- EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
expected_config.language[0] = 'd';
expected_config.language[1] = 'e';
- EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
expected_config.language[0] = 'f';
expected_config.language[1] = 'r';
- EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
// Take out the system assets.
configurations = assetmanager.GetResourceConfigurations(true /* exclude_system */);
+ ASSERT_TRUE(configurations.has_value());
// We expect de and fr from basic_de_fr assets.
- EXPECT_EQ(2u, configurations.size());
+ EXPECT_EQ(2u, configurations->size());
expected_config.language[0] = 's';
expected_config.language[1] = 'v';
- EXPECT_FALSE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_FALSE(IsConfigurationPresent(*configurations, expected_config));
expected_config.language[0] = 'd';
expected_config.language[1] = 'e';
- EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
expected_config.language[0] = 'f';
expected_config.language[1] = 'r';
- EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
}
TEST_F(AssetManager2Test, GetResourceLocales) {
@@ -578,12 +597,17 @@
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
- EXPECT_EQ(basic::R::layout::main,
- assetmanager.GetResourceId("com.android.basic:layout/main", "", ""));
- EXPECT_EQ(basic::R::layout::main,
- assetmanager.GetResourceId("layout/main", "", "com.android.basic"));
- EXPECT_EQ(basic::R::layout::main,
- assetmanager.GetResourceId("main", "layout", "com.android.basic"));
+ auto resid = assetmanager.GetResourceId("com.android.basic:layout/main", "", "");
+ ASSERT_TRUE(resid.has_value());
+ EXPECT_EQ(basic::R::layout::main, *resid);
+
+ resid = assetmanager.GetResourceId("layout/main", "", "com.android.basic");
+ ASSERT_TRUE(resid.has_value());
+ EXPECT_EQ(basic::R::layout::main, *resid);
+
+ resid = assetmanager.GetResourceId("main", "layout", "com.android.basic");
+ ASSERT_TRUE(resid.has_value());
+ EXPECT_EQ(basic::R::layout::main, *resid);
}
TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {
@@ -658,14 +682,8 @@
assetmanager.SetApkAssets({basic_assets_.get()});
assetmanager.SetResourceResolutionLoggingEnabled(false);
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
auto result = assetmanager.GetLastResourceResolution();
EXPECT_EQ("", result);
@@ -693,17 +711,12 @@
assetmanager.SetConfiguration(desired_config);
assetmanager.SetApkAssets({basic_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
auto result = assetmanager.GetLastResourceResolution();
- EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic", result);
+ EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
+ "\tFor config -de\n\tFound initial: com.android.basic", result);
}
TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
@@ -717,17 +730,14 @@
assetmanager.SetConfiguration(desired_config);
assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
- Res_value value = Res_value();
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
auto result = assetmanager.GetLastResourceResolution();
- EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic\n\tFound better: com.android.basic -de", result);
+ EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
+ "\tFor config -de\n"
+ "\tFound initial: com.android.basic\n"
+ "\tFound better: com.android.basic -de", result);
}
TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
@@ -739,14 +749,8 @@
assetmanager.SetConfiguration(desired_config);
assetmanager.SetApkAssets({basic_assets_.get()});
- Res_value value = Res_value();
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
auto resultEnabled = assetmanager.GetLastResourceResolution();
ASSERT_NE("", resultEnabled);
diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp
index fa300c5..ddd8ab8 100644
--- a/libs/androidfw/tests/AttributeResolution_bench.cpp
+++ b/libs/androidfw/tests/AttributeResolution_bench.cpp
@@ -108,27 +108,20 @@
device_config.screenHeightDp = 1024;
device_config.sdkVersion = 27;
- Res_value value;
- ResTable_config config;
- uint32_t flags = 0u;
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/,
- 0u /*density_override*/, &value, &config, &flags);
- if (cookie == kInvalidCookie) {
+ auto value = assetmanager.GetResource(basic::R::layout::layoutt);
+ if (!value.has_value()) {
state.SkipWithError("failed to find R.layout.layout");
return;
}
- size_t len = 0u;
- const char* layout_path =
- assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len);
- if (layout_path == nullptr || len == 0u) {
+ auto layout_path = assetmanager.GetStringPoolForCookie(value->cookie)->string8At(value->data);
+ if (!layout_path.has_value()) {
state.SkipWithError("failed to lookup layout path");
return;
}
- std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(
- StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER);
+ std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(layout_path->to_string(), value->cookie,
+ Asset::ACCESS_BUFFER);
if (asset == nullptr) {
state.SkipWithError("failed to load layout");
return;
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index 24361b5..bb9129a 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -77,9 +77,9 @@
{fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
std::array<uint32_t, attrs.size() + 1> indices;
- ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/,
- fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(),
- indices.data());
+ ASSERT_TRUE(ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/,
+ fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(),
+ values.data(), indices.data()).has_value());
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
@@ -102,7 +102,7 @@
TEST_F(AttributeResolutionTest, Theme) {
std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
+ ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value());
std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_empty}};
@@ -110,7 +110,7 @@
ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/,
nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(),
- attrs.size(), values.data(), nullptr /*out_indices*/));
+ attrs.size(), values.data(), nullptr /*out_indices*/).has_value());
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
@@ -162,7 +162,7 @@
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(),
- values.data(), nullptr /*out_indices*/));
+ values.data(), nullptr /*out_indices*/).has_value());
uint32_t* values_cursor = values.data();
EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
@@ -207,15 +207,15 @@
TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
+ ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value());
std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
std::array<uint32_t, attrs.size() + 1> indices;
- ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(),
- attrs.size(), values.data(), indices.data());
+ ASSERT_TRUE(ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/,
+ attrs.data(), attrs.size(), values.data(), indices.data()).has_value());
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index faddfe5..0fa0573 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -71,15 +71,9 @@
assetmanager.SetConfiguration(*config);
}
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
- uint32_t last_id = 0u;
-
while (state.KeepRunning()) {
- ApkAssetsCookie cookie = assetmanager.GetResource(
- resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags);
- assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id);
+ auto value = assetmanager.GetResource(resid);
+ assetmanager.ResolveReference(*value);
}
}
diff --git a/libs/androidfw/tests/CommonHelpers.cpp b/libs/androidfw/tests/CommonHelpers.cpp
index faa5350..3396729 100644
--- a/libs/androidfw/tests/CommonHelpers.cpp
+++ b/libs/androidfw/tests/CommonHelpers.cpp
@@ -58,8 +58,9 @@
}
std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) {
- String8 str = pool->string8ObjectAt(idx);
- return std::string(str.string(), str.length());
+ auto str = pool->string8ObjectAt(idx);
+ CHECK(str.has_value()) << "failed to find string entry";
+ return std::string(str->string(), str->length());
}
} // namespace android
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 7aa0dbb..3f0c7cb 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -62,10 +62,10 @@
std::unique_ptr<const ApkAssets> overlayable_assets_;
};
-std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value,
- ApkAssetsCookie cookie) {
+std::string GetStringFromApkAssets(const AssetManager2& asset_manager,
+ const AssetManager2::SelectedValue& value) {
auto assets = asset_manager.GetApkAssets();
- const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool();
+ const ResStringPool* string_pool = assets[value.cookie]->GetLoadedArsc()->GetStringPool();
return GetStringFromPool(string_pool, value.data);
}
@@ -75,117 +75,88 @@
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
- ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One");
+
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable5);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+ ASSERT_EQ("Overlay One", GetStringFromApkAssets(asset_manager, *value));
}
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 0U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
- ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes");
+
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable10);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 0U);
+ ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+ ASSERT_EQ("yes", GetStringFromApkAssets(asset_manager, *value));
}
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
- ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24));
+
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable8);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(value->data, (overlay::R::string::internal & 0x00ffffffU) | (0x02U << 24));
}
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC);
- ASSERT_EQ(val.data, 42);
+
+ auto value = asset_manager.GetResource(overlayable::R::integer::config_integer);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_INT_DEC);
+ ASSERT_EQ(value->data, 42);
}
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
- ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string");
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+ ASSERT_EQ("Hardcoded string", GetStringFromApkAssets(asset_manager, *value));
}
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
- ASSERT_EQ(val.data, overlayable::R::string::overlayable7);
+
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable9);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(value->data, overlayable::R::string::overlayable7);
}
TEST_F(IdmapTest, OverlayOverridesXmlParser) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
- ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml");
- auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie,
+ auto value = asset_manager.GetResource(overlayable::R::layout::hello_view);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+ ASSERT_EQ("res/layout/hello_view.xml", GetStringFromApkAssets(asset_manager, *value));
+
+ auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", value->cookie,
Asset::ACCESS_RANDOM);
- auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie);
+ auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(value->cookie);
auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table));
status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false);
ASSERT_EQ(err, NO_ERROR);
@@ -216,32 +187,24 @@
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- AssetManager2::ResourceName name;
- ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name));
- ASSERT_EQ(std::string(name.package), "com.android.overlayable");
- ASSERT_EQ(String16(name.type16), u"string");
- ASSERT_EQ(std::string(name.entry), "overlayable9");
+ auto name = asset_manager.GetResourceName(overlayable::R::string::overlayable9);
+ ASSERT_TRUE(name.has_value());
+ ASSERT_EQ("com.android.overlayable", std::string(name->package));
+ ASSERT_EQ(std::u16string(u"string"), std::u16string(name->type16));
+ ASSERT_EQ("overlayable9", std::string(name->entry));
}
TEST_F(IdmapTest, OverlayLoaderInterop) {
- std::string contents;
auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER);
-
AssetManager2 asset_manager;
asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- std::cout << asset_manager.GetLastResourceResolution();
- ASSERT_EQ(cookie, 1U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
- ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader");
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(1U, value->cookie);
+ ASSERT_EQ(Res_value::TYPE_STRING, value->type);
+ ASSERT_EQ("loader", GetStringFromApkAssets(asset_manager, *value));
}
TEST_F(IdmapTest, OverlayAssetsIsUpToDate) {
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 2d69dfe..6357411 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -50,7 +50,8 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc",
&contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ auto loaded_arsc = LoadedArsc::Load(reinterpret_cast<const void*>(contents.data()),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
@@ -66,9 +67,8 @@
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- const ResTable_type* type = type_spec->types[0];
- ASSERT_THAT(type, NotNull());
- ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+ auto type = type_spec->types[0];
+ ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
}
TEST(LoadedArscTest, LoadSparseEntryApp) {
@@ -76,7 +76,8 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
&contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
@@ -90,9 +91,8 @@
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- const ResTable_type* type = type_spec->types[0];
- ASSERT_THAT(type, NotNull());
- ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+ auto type = type_spec->types[0];
+ ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
}
TEST(LoadedArscTest, LoadSharedLibrary) {
@@ -100,7 +100,8 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
&contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
@@ -120,7 +121,8 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
@@ -145,8 +147,10 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC);
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length(),
+ nullptr /* loaded_idmap */,
+ PROPERTY_DYNAMIC);
ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
@@ -159,7 +163,8 @@
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
&contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
@@ -172,15 +177,12 @@
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
- size_t len;
- const char16_t* type_name16 =
- package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len);
- ASSERT_THAT(type_name16, NotNull());
- EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string"));
+ auto type_name16 = package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1);
+ ASSERT_TRUE(type_name16.has_value());
+ EXPECT_THAT(util::Utf16ToUtf8(*type_name16), StrEq("string"));
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull());
+ ASSERT_TRUE(LoadedPackage::GetEntry(type_spec->types[0], entry_index).has_value());
}
// AAPT(2) generates resource tables with chunks in a certain order. The rule is that
@@ -205,7 +207,8 @@
ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
@@ -215,12 +218,10 @@
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0);
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
type_spec = package->GetTypeSpecByTypeIndex(1);
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
}
TEST(LoadedArscTest, LoadOverlayable) {
@@ -228,7 +229,8 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package = loaded_arsc->GetPackageById(
@@ -272,7 +274,8 @@
ASSERT_TRUE(
ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_NE(nullptr, loaded_arsc);
const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
@@ -320,7 +323,8 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_NE(nullptr, loaded_arsc);
const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
@@ -345,7 +349,7 @@
asset->getLength());
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(data, nullptr, PROPERTY_LOADER);
+ LoadedArsc::Load(data.data(), data.length(), nullptr, PROPERTY_LOADER);
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
@@ -361,9 +365,8 @@
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- const ResTable_type* type = type_spec->types[0];
- ASSERT_THAT(type, NotNull());
- ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+ auto type = type_spec->types[0];
+ ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
}
// structs with size fields (like Res_value, ResTable_entry) should be
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 326474e1..9aeb00c 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -442,22 +442,22 @@
ASSERT_LT(val.data, pool->size());
// Make sure a string with a truncated length is read to its correct length
- size_t str_len;
- const char* target_str8 = pool->string8At(val.data, &str_len);
- ASSERT_TRUE(target_str8 != NULL);
- ASSERT_EQ(size_t(40076), String8(target_str8, str_len).size());
- ASSERT_EQ(target_str8[40075], ']');
+ auto target_str8 = pool->string8At(val.data);
+ ASSERT_TRUE(target_str8.has_value());
+ ASSERT_EQ(size_t(40076), String8(target_str8->data(), target_str8->size()).size());
+ ASSERT_EQ(target_str8->data()[40075], ']');
- const char16_t* target_str16 = pool->stringAt(val.data, &str_len);
- ASSERT_TRUE(target_str16 != NULL);
- ASSERT_EQ(size_t(40076), String16(target_str16, str_len).size());
- ASSERT_EQ(target_str8[40075], (char16_t) ']');
+ auto target_str16 = pool->stringAt(val.data);
+ ASSERT_TRUE(target_str16.has_value());
+ ASSERT_EQ(size_t(40076), String16(target_str16->data(), target_str16->size()).size());
+ ASSERT_EQ(target_str8->data()[40075], (char16_t) ']');
// Load an edited apk with the null terminator removed from the end of the
// string
std::string invalid_contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/length_decode/length_decode_invalid.apk",
- "resources.arsc", &invalid_contents));
+ ASSERT_TRUE(ReadFileFromZipToString(
+ GetTestDataPath() + "/length_decode/length_decode_invalid.apk", "resources.arsc",
+ &invalid_contents));
ResTable invalid_table;
ASSERT_EQ(NO_ERROR, invalid_table.add(invalid_contents.data(), invalid_contents.size()));
@@ -472,8 +472,8 @@
// Make sure a string with a truncated length that is not null terminated errors
// and does not return the string
- ASSERT_TRUE(invalid_pool->string8At(invalid_val.data, &str_len) == NULL);
- ASSERT_TRUE(invalid_pool->stringAt(invalid_val.data, &str_len) == NULL);
+ ASSERT_FALSE(invalid_pool->string8At(invalid_val.data).has_value());
+ ASSERT_FALSE(invalid_pool->stringAt(invalid_val.data).has_value());
}
} // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
index a81bb6f..10c0a4f 100644
--- a/libs/androidfw/tests/TestHelpers.cpp
+++ b/libs/androidfw/tests/TestHelpers.cpp
@@ -73,11 +73,15 @@
return AssertionFailure() << "table has no string pool for block " << block;
}
- const String8 actual_str = pool->string8ObjectAt(val.data);
- if (String8(expected_str) != actual_str) {
- return AssertionFailure() << actual_str.string();
+ auto actual_str = pool->string8ObjectAt(val.data);
+ if (!actual_str.has_value()) {
+ return AssertionFailure() << "could not find string entry";
}
- return AssertionSuccess() << actual_str.string();
+
+ if (String8(expected_str) != *actual_str) {
+ return AssertionFailure() << actual_str->string();
+ }
+ return AssertionSuccess() << actual_str->string();
}
} // namespace android
diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp
index 594c39e..f3d60bb 100644
--- a/libs/androidfw/tests/Theme_bench.cpp
+++ b/libs/androidfw/tests/Theme_bench.cpp
@@ -70,11 +70,8 @@
auto theme = assets.NewTheme();
theme->ApplyStyle(kStyleId, false /* force */);
- Res_value value;
- uint32_t flags;
-
while (state.KeepRunning()) {
- theme->GetAttribute(kAttrId, &value, &flags);
+ theme->GetAttribute(kAttrId);
}
}
BENCHMARK(BM_ThemeGetAttribute);
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 16b9c75..f658735 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -67,10 +67,7 @@
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
EXPECT_EQ(0u, theme->GetChangingConfigurations());
EXPECT_EQ(&assetmanager, theme->GetAssetManager());
-
- Res_value value;
- uint32_t flags;
- EXPECT_EQ(kInvalidCookie, theme->GetAttribute(app::R::attr::attr_one, &value, &flags));
+ EXPECT_FALSE(theme->GetAttribute(app::R::attr::attr_one).has_value());
}
TEST_F(ThemeTest, SingleThemeNoParent) {
@@ -78,23 +75,19 @@
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne));
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne).has_value());
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
+ auto value = theme->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(1u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
- cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(1u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
-
- cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(2u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_two);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(2u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
}
TEST_F(ThemeTest, SingleThemeWithParent) {
@@ -102,32 +95,28 @@
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
+ auto value = theme->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(1u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
- cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(1u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
-
- cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
- EXPECT_EQ(0, cookie);
+ value = theme->GetAttribute(app::R::attr::attr_two);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_STRING, value->type);
+ EXPECT_EQ(0, value->cookie);
EXPECT_EQ(std::string("string"),
- GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value.data));
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value->data));
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// This attribute should point to an attr_indirect, so the result should be 3.
- cookie = theme->GetAttribute(app::R::attr::attr_three, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(3u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_three);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(3u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
}
TEST_F(ThemeTest, TryToUseBadResourceId) {
@@ -135,11 +124,8 @@
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
-
- Res_value value;
- uint32_t flags;
- ASSERT_EQ(kInvalidCookie, theme->GetAttribute(0x7f000001, &value, &flags));
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
+ ASSERT_FALSE(theme->GetAttribute(0x7f000001));
}
TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) {
@@ -147,33 +133,29 @@
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree));
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree).has_value());
// attr_one is still here from the base.
- cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(1u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ auto value = theme->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(1u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// check for the new attr_six
- cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(6u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_six);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(6u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// check for the old attr_five (force=true was not used).
- cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(app::R::string::string_one, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_five);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(app::R::string::string_one, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
}
TEST_F(ThemeTest, MultipleThemesOverlaidForced) {
@@ -181,33 +163,29 @@
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */));
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */).has_value());
// attr_one is still here from the base.
- cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(1u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ auto value = theme->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(1u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// check for the new attr_six
- cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(6u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_six);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(6u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// check for the new attr_five (force=true was used).
- cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(5u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_five);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(5u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
}
TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) {
@@ -216,28 +194,24 @@
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/));
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
+ ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/).has_value());
// The attribute should be resolved to the final value.
- cookie = theme->GetAttribute(libclient::R::attr::foo, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(700u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ auto value = theme->GetAttribute(libclient::R::attr::foo);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(700u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// The reference should be resolved to a TYPE_REFERENCE.
- cookie = theme->GetAttribute(libclient::R::attr::bar, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+ value = theme->GetAttribute(libclient::R::attr::bar);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
// lib_one is assigned package ID 0x03.
- EXPECT_EQ(3u, get_package_id(value.data));
- EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value.data));
- EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value.data));
+ EXPECT_EQ(3u, get_package_id(value->data));
+ EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value->data));
+ EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value->data));
}
TEST_F(ThemeTest, CopyThemeSameAssetManager) {
@@ -245,24 +219,20 @@
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme_one = assetmanager.NewTheme();
- ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
+ ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne).has_value());
// attr_one is still here from the base.
- cookie = theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(1u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ auto value = theme_one->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(1u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// attr_six is not here.
- EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags));
+ ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_six).has_value());
std::unique_ptr<Theme> theme_two = assetmanager.NewTheme();
- ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree));
+ ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree).has_value());
// Copy the theme to theme_one.
theme_one->SetTo(*theme_two);
@@ -271,14 +241,14 @@
theme_two->Clear();
// attr_one is now not here.
- EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+ ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_one).has_value());
// attr_six is now here because it was copied.
- cookie = theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(6u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme_one->GetAttribute(app::R::attr::attr_six);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(6u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
}
TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) {
@@ -291,39 +261,43 @@
style_assets_.get()});
auto theme_dst = assetmanager_dst.NewTheme();
- ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne));
+ ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne).has_value());
auto theme_src = assetmanager_src.NewTheme();
- ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One));
- ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo));
+ ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One).has_value());
+ ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo).has_value());
ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03),
- false /*force*/));
+ false /*force*/).has_value());
ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02),
- false /*force*/));
+ false /*force*/).has_value());
theme_dst->SetTo(*theme_src);
- Res_value value;
- uint32_t flags;
-
// System resources (present in destination asset manager).
- EXPECT_EQ(0, theme_dst->GetAttribute(R::attr::foreground, &value, &flags));
+ auto value = theme_dst->GetAttribute(R::attr::foreground);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(0, value->cookie);
// The cookie of the style asset is 3 in the source and 2 in the destination.
// Check that the cookie has been rewritten to the destination values.
- EXPECT_EQ(2, theme_dst->GetAttribute(app::R::attr::attr_one, &value, &flags));
+ value = theme_dst->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(2, value->cookie);
// The cookie of the lib_one asset is 2 in the source and 1 in the destination.
// The package id of the lib_one package is 0x03 in the source and 0x02 in the destination
// Check that the cookie and packages have been rewritten to the destination values.
- EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value,
- &flags));
- EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value,
- &flags));
+ value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02));
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(1, value->cookie);
+
+ value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02));
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(1, value->cookie);
// attr2 references an attribute in lib_one. Check that the resolution of the attribute value is
// correct after the value of attr2 had its package id rewritten to the destination package id.
- EXPECT_EQ(700, value.data);
+ EXPECT_EQ(700, value->data);
}
TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) {
@@ -335,28 +309,32 @@
auto theme_dst = assetmanager_dst.NewTheme();
auto theme_src = assetmanager_src.NewTheme();
- ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven));
+ ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven).has_value());
theme_dst->SetTo(*theme_src);
- Res_value value;
- uint32_t flags;
-
// Allow inline resource values to be copied even if the source apk asset is not present in the
// destination.
- EXPECT_EQ(0, theme_dst->GetAttribute(0x0101021b /* android:versionCode */, &value, &flags));
+ auto value = theme_dst->GetAttribute(0x0101021b /* android:versionCode */);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(0, value->cookie);
// Do not copy strings since the data is an index into the values string pool of the source apk
// asset.
- EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010001 /* android:label */, &value, &flags));
+ EXPECT_FALSE(theme_dst->GetAttribute(0x01010001 /* android:label */).has_value());
// Do not copy values that reference another resource if the resource is not present in the
// destination.
- EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010002 /* android:icon */, &value, &flags));
- EXPECT_EQ(-1, theme_dst->GetAttribute(0x010100d1 /* android:tag */, &value, &flags));
+ EXPECT_FALSE(theme_dst->GetAttribute(0x01010002 /* android:icon */).has_value());
+ EXPECT_FALSE(theme_dst->GetAttribute(0x010100d1 /* android:tag */).has_value());
// Allow @empty to and @null to be copied.
- EXPECT_EQ(0, theme_dst->GetAttribute(0x010100d0 /* android:id */, &value, &flags));
- EXPECT_EQ(0, theme_dst->GetAttribute(0x01010000 /* android:theme */, &value, &flags));
+ value = theme_dst->GetAttribute(0x010100d0 /* android:id */);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(0, value->cookie);
+
+ value = theme_dst->GetAttribute(0x01010000 /* android:theme */);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(0, value->cookie);
}
} // namespace android
diff --git a/lowpan/java/android/net/lowpan/LowpanManager.java b/lowpan/java/android/net/lowpan/LowpanManager.java
index 76876ce..33b35e6 100644
--- a/lowpan/java/android/net/lowpan/LowpanManager.java
+++ b/lowpan/java/android/net/lowpan/LowpanManager.java
@@ -24,6 +24,10 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
@@ -97,10 +101,14 @@
*
* @param context the application context
* @param service the Binder interface
- * @param looper the default Looper to run callbacks on
* @hide - hide this because it takes in a parameter of type ILowpanManager, which is a system
* private class.
*/
+ public LowpanManager(Context context, ILowpanManager service) {
+ this(context, service, BackgroundThread.get().getLooper());
+ }
+
+ @VisibleForTesting
public LowpanManager(Context context, ILowpanManager service, Looper looper) {
mContext = context;
mService = service;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 107d656..b330037 100755
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -75,6 +75,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -1598,21 +1599,14 @@
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy,
@NonNull AudioDeviceAttributes device) {
- Objects.requireNonNull(strategy);
- Objects.requireNonNull(device);
- try {
- final int status =
- getService().setPreferredDeviceForStrategy(strategy.getId(), device);
- return status == AudioSystem.SUCCESS;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return setPreferredDevicesForStrategy(strategy, Arrays.asList(device));
}
/**
* @hide
- * Removes the preferred audio device previously set with
- * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}.
+ * Removes the preferred audio device(s) previously set with
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+ * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}.
* @param strategy the audio strategy whose routing will be affected
* @return true if the operation was successful, false otherwise (invalid strategy, or no
* device set for example)
@@ -1623,7 +1617,7 @@
Objects.requireNonNull(strategy);
try {
final int status =
- getService().removePreferredDeviceForStrategy(strategy.getId());
+ getService().removePreferredDevicesForStrategy(strategy.getId());
return status == AudioSystem.SUCCESS;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1633,18 +1627,74 @@
/**
* @hide
* Return the preferred device for an audio strategy, previously set with
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+ * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
+ * @param strategy the strategy to query
+ * @return the preferred device for that strategy, if multiple devices are set as preferred
+ * devices, the first one in the list will be returned. Null will be returned if none was
+ * ever set or if the strategy is invalid
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @Nullable
+ public AudioDeviceAttributes getPreferredDeviceForStrategy(
+ @NonNull AudioProductStrategy strategy) {
+ List<AudioDeviceAttributes> devices = getPreferredDevicesForStrategy(strategy);
+ return devices.isEmpty() ? null : devices.get(0);
+ }
+
+ /**
+ * @hide
+ * Set the preferred devices for a given strategy, i.e. the audio routing to be used by
+ * this audio strategy. Note that the devices may not be available at the time the preferred
+ * devices is set, but it will be used once made available.
+ * <p>Use {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} to cancel setting
+ * this preference for this strategy.</p>
+ * Note that the list of devices is not a list ranked by preference, but a list of one or more
+ * devices used simultaneously to output the same audio signal.
+ * @param strategy the audio strategy whose routing will be affected
+ * @param devices a non-empty list of the audio devices to route to when available
+ * @return true if the operation was successful, false otherwise
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean setPreferredDevicesForStrategy(@NonNull AudioProductStrategy strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ Objects.requireNonNull(strategy);
+ Objects.requireNonNull(devices);
+ if (devices.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Tried to set preferred devices for strategy with a empty list");
+ }
+ for (AudioDeviceAttributes device : devices) {
+ Objects.requireNonNull(device);
+ }
+ try {
+ final int status =
+ getService().setPreferredDevicesForStrategy(strategy.getId(), devices);
+ return status == AudioSystem.SUCCESS;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Return the preferred devices for an audio strategy, previously set with
* {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
+ * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
* @param strategy the strategy to query
* @return the preferred device for that strategy, or null if none was ever set or if the
* strategy is invalid
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public @Nullable AudioDeviceAttributes getPreferredDeviceForStrategy(
+ @NonNull
+ public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(
@NonNull AudioProductStrategy strategy) {
Objects.requireNonNull(strategy);
try {
- return getService().getPreferredDeviceForStrategy(strategy.getId());
+ return getService().getPreferredDevicesForStrategy(strategy.getId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1656,6 +1706,7 @@
* strategy.
* <p>Note that this listener will only be invoked whenever
* {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+ * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
* {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
* preferred device. It will not be invoked directly after registration with
* {@link #addOnPreferredDeviceForStrategyChangedListener(Executor, OnPreferredDeviceForStrategyChangedListener)}
@@ -1663,8 +1714,10 @@
* @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
* @see #removePreferredDeviceForStrategy(AudioProductStrategy)
* @see #getPreferredDeviceForStrategy(AudioProductStrategy)
+ * @deprecated use #OnPreferredDevicesForStrategyChangedListener
*/
@SystemApi
+ @Deprecated
public interface OnPreferredDeviceForStrategyChangedListener {
/**
* Called on the listener to indicate that the preferred audio device for the given
@@ -1679,6 +1732,70 @@
/**
* @hide
+ * Interface to be notified of changes in the preferred audio devices set for a given audio
+ * strategy.
+ * <p>Note that this listener will only be invoked whenever
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+ * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
+ * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
+ * preferred device(s). It will not be invoked directly after registration with
+ * {@link #addOnPreferredDevicesForStrategyChangedListener(
+ * Executor, OnPreferredDevicesForStrategyChangedListener)}
+ * to indicate which strategies had preferred devices at the time of registration.</p>
+ * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
+ * @see #setPreferredDevicesForStrategy(AudioProductStrategy, List)
+ * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
+ * @see #getPreferredDeviceForStrategy(AudioProductStrategy)
+ * @see #getPreferredDevicesForStrategy(AudioProductStrategy)
+ */
+ @SystemApi
+ public interface OnPreferredDevicesForStrategyChangedListener {
+ /**
+ * Called on the listener to indicate that the preferred audio devices for the given
+ * strategy has changed.
+ * @param strategy the {@link AudioProductStrategy} whose preferred device changed
+ * @param devices a list of newly set preferred audio devices
+ */
+ void onPreferredDevicesForStrategyChanged(@NonNull AudioProductStrategy strategy,
+ @NonNull List<AudioDeviceAttributes> devices);
+ }
+
+ /**
+ * @hide
+ * Adds a listener for being notified of changes to the strategy-preferred audio device.
+ * @param executor
+ * @param listener
+ * @throws SecurityException if the caller doesn't hold the required permission
+ * @deprecated use {@link #addOnPreferredDevicesForStrategyChangedListener(
+ * Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @Deprecated
+ public void addOnPreferredDeviceForStrategyChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnPreferredDeviceForStrategyChangedListener listener)
+ throws SecurityException {
+ // No-op, the method is deprecated.
+ }
+
+ /**
+ * @hide
+ * Removes a previously added listener of changes to the strategy-preferred audio device.
+ * @param listener
+ * @deprecated use {@link #removeOnPreferredDevicesForStrategyChangedListener(
+ * AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @Deprecated
+ public void removeOnPreferredDeviceForStrategyChangedListener(
+ @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
+ // No-op, the method is deprecated.
+ }
+
+ /**
+ * @hide
* Adds a listener for being notified of changes to the strategy-preferred audio device.
* @param executor
* @param listener
@@ -1686,16 +1803,16 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void addOnPreferredDeviceForStrategyChangedListener(
+ public void addOnPreferredDevicesForStrategyChangedListener(
@NonNull @CallbackExecutor Executor executor,
- @NonNull OnPreferredDeviceForStrategyChangedListener listener)
+ @NonNull OnPreferredDevicesForStrategyChangedListener listener)
throws SecurityException {
Objects.requireNonNull(executor);
Objects.requireNonNull(listener);
synchronized (mPrefDevListenerLock) {
if (hasPrefDevListener(listener)) {
throw new IllegalArgumentException(
- "attempt to call addOnPreferredDeviceForStrategyChangedListener() "
+ "attempt to call addOnPreferredDevicesForStrategyChangedListener() "
+ "on a previously registered listener");
}
// lazy initialization of the list of strategy-preferred device listener
@@ -1707,10 +1824,10 @@
if (oldCbCount == 0 && mPrefDevListeners.size() > 0) {
// register binder for callbacks
if (mPrefDevDispatcherStub == null) {
- mPrefDevDispatcherStub = new StrategyPreferredDeviceDispatcherStub();
+ mPrefDevDispatcherStub = new StrategyPreferredDevicesDispatcherStub();
}
try {
- getService().registerStrategyPreferredDeviceDispatcher(mPrefDevDispatcherStub);
+ getService().registerStrategyPreferredDevicesDispatcher(mPrefDevDispatcherStub);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1725,8 +1842,8 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void removeOnPreferredDeviceForStrategyChangedListener(
- @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
+ public void removeOnPreferredDevicesForStrategyChangedListener(
+ @NonNull OnPreferredDevicesForStrategyChangedListener listener) {
Objects.requireNonNull(listener);
synchronized (mPrefDevListenerLock) {
if (!removePrefDevListener(listener)) {
@@ -1737,7 +1854,7 @@
if (mPrefDevListeners.size() == 0) {
// unregister binder for callbacks
try {
- getService().unregisterStrategyPreferredDeviceDispatcher(
+ getService().unregisterStrategyPreferredDevicesDispatcher(
mPrefDevDispatcherStub);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1759,23 +1876,23 @@
private @Nullable ArrayList<PrefDevListenerInfo> mPrefDevListeners;
private static class PrefDevListenerInfo {
- final @NonNull OnPreferredDeviceForStrategyChangedListener mListener;
+ final @NonNull OnPreferredDevicesForStrategyChangedListener mListener;
final @NonNull Executor mExecutor;
- PrefDevListenerInfo(OnPreferredDeviceForStrategyChangedListener listener, Executor exe) {
+ PrefDevListenerInfo(OnPreferredDevicesForStrategyChangedListener listener, Executor exe) {
mListener = listener;
mExecutor = exe;
}
}
@GuardedBy("mPrefDevListenerLock")
- private StrategyPreferredDeviceDispatcherStub mPrefDevDispatcherStub;
+ private StrategyPreferredDevicesDispatcherStub mPrefDevDispatcherStub;
- private final class StrategyPreferredDeviceDispatcherStub
- extends IStrategyPreferredDeviceDispatcher.Stub {
+ private final class StrategyPreferredDevicesDispatcherStub
+ extends IStrategyPreferredDevicesDispatcher.Stub {
@Override
- public void dispatchPrefDeviceChanged(int strategyId,
- @Nullable AudioDeviceAttributes device) {
+ public void dispatchPrefDevicesChanged(int strategyId,
+ @NonNull List<AudioDeviceAttributes> devices) {
// make a shallow copy of listeners so callback is not executed under lock
final ArrayList<PrefDevListenerInfo> prefDevListeners;
synchronized (mPrefDevListenerLock) {
@@ -1790,7 +1907,7 @@
try {
for (PrefDevListenerInfo info : prefDevListeners) {
info.mExecutor.execute(() ->
- info.mListener.onPreferredDeviceForStrategyChanged(strategy, device));
+ info.mListener.onPreferredDevicesForStrategyChanged(strategy, devices));
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1800,7 +1917,7 @@
@GuardedBy("mPrefDevListenerLock")
private @Nullable PrefDevListenerInfo getPrefDevListenerInfo(
- OnPreferredDeviceForStrategyChangedListener listener) {
+ OnPreferredDevicesForStrategyChangedListener listener) {
if (mPrefDevListeners == null) {
return null;
}
@@ -1813,7 +1930,7 @@
}
@GuardedBy("mPrefDevListenerLock")
- private boolean hasPrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) {
+ private boolean hasPrefDevListener(OnPreferredDevicesForStrategyChangedListener listener) {
return getPrefDevListenerInfo(listener) != null;
}
@@ -1821,7 +1938,7 @@
/**
* @return true if the listener was removed from the list
*/
- private boolean removePrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) {
+ private boolean removePrefDevListener(OnPreferredDevicesForStrategyChangedListener listener) {
final PrefDevListenerInfo infoToRemove = getPrefDevListenerInfo(listener);
if (infoToRemove != null) {
mPrefDevListeners.remove(infoToRemove);
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index f1eb53d..1590485 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -33,6 +33,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -1371,6 +1372,11 @@
/** @hide */ public static final int FOR_VIBRATE_RINGING = 7;
private static final int NUM_FORCE_USE = 8;
+ // Device role in audio policy
+ public static final int DEVICE_ROLE_NONE = 0;
+ public static final int DEVICE_ROLE_PREFERRED = 1;
+ public static final int DEVICE_ROLE_DISABLED = 2;
+
/** @hide */
public static String forceUseUsageToString(int usage) {
switch (usage) {
@@ -1691,47 +1697,58 @@
/**
* @hide
- * Sets the preferred device to use for a given audio strategy in the audio policy engine
+ * Set device as role for product strategy.
* @param strategy the id of the strategy to configure
- * @param device the device type and address to route to when available
+ * @param role the role of the devices
+ * @param devices the list of devices to be set as role for the given strategy
* @return {@link #SUCCESS} if successfully set
*/
- public static int setPreferredDeviceForStrategy(
- int strategy, @NonNull AudioDeviceAttributes device) {
- return setPreferredDeviceForStrategy(strategy,
- AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
- device.getAddress());
+ public static int setDevicesRoleForStrategy(
+ int strategy, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ if (devices.isEmpty()) {
+ return BAD_VALUE;
+ }
+ int[] types = new int[devices.size()];
+ String[] addresses = new String[devices.size()];
+ for (int i = 0; i < devices.size(); ++i) {
+ types[i] = AudioDeviceInfo.convertDeviceTypeToInternalDevice(devices.get(i).getType());
+ addresses[i] = devices.get(i).getAddress();
+ }
+ return setDevicesRoleForStrategy(strategy, role, types, addresses);
}
- /**
- * @hide
- * Set device routing per product strategy.
- * @param strategy the id of the strategy to configure
- * @param deviceType the native device type, NOT AudioDeviceInfo types
- * @param deviceAddress the address of the device
- * @return {@link #SUCCESS} if successfully set
- */
- private static native int setPreferredDeviceForStrategy(
- int strategy, int deviceType, String deviceAddress);
/**
* @hide
- * Remove preferred routing for the strategy
+ * Set device as role for product strategy.
* @param strategy the id of the strategy to configure
+ * @param role the role of the devices
+ * @param types all device types
+ * @param addresses all device addresses
+ * @return {@link #SUCCESS} if successfully set
+ */
+ private static native int setDevicesRoleForStrategy(
+ int strategy, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+ /**
+ * @hide
+ * Remove devices as role for the strategy
+ * @param strategy the id of the strategy to configure
+ * @param role the role of the devices
* @return {@link #SUCCESS} if successfully removed
*/
- public static native int removePreferredDeviceForStrategy(int strategy);
+ public static native int removeDevicesRoleForStrategy(int strategy, int role);
/**
* @hide
- * Query previously set preferred device for a strategy
+ * Query previously set devices as role for a strategy
* @param strategy the id of the strategy to query for
- * @param device an array of size 1 that will contain the preferred device, or null if
- * none was set
+ * @param role the role of the devices
+ * @param devices a list that will contain the devices of role
* @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved
* and written to the array
*/
- public static native int getPreferredDeviceForStrategy(int strategy,
- AudioDeviceAttributes[] device);
+ public static native int getDevicesForRoleAndStrategy(
+ int strategy, int role, @NonNull List<AudioDeviceAttributes> devices);
// Items shared with audio service
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 78806eb..bc2839e 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -29,7 +29,7 @@
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.IVolumeController;
import android.media.IVolumeController;
import android.media.PlayerBase;
@@ -279,11 +279,11 @@
boolean isCallScreeningModeSupported();
- int setPreferredDeviceForStrategy(in int strategy, in AudioDeviceAttributes device);
+ int setPreferredDevicesForStrategy(in int strategy, in List<AudioDeviceAttributes> device);
- int removePreferredDeviceForStrategy(in int strategy);
+ int removePreferredDevicesForStrategy(in int strategy);
- AudioDeviceAttributes getPreferredDeviceForStrategy(in int strategy);
+ List<AudioDeviceAttributes> getPreferredDevicesForStrategy(in int strategy);
List<AudioDeviceAttributes> getDevicesForAttributes(in AudioAttributes attributes);
@@ -291,10 +291,10 @@
int getAllowedCapturePolicy();
- void registerStrategyPreferredDeviceDispatcher(IStrategyPreferredDeviceDispatcher dispatcher);
+ void registerStrategyPreferredDevicesDispatcher(IStrategyPreferredDevicesDispatcher dispatcher);
- oneway void unregisterStrategyPreferredDeviceDispatcher(
- IStrategyPreferredDeviceDispatcher dispatcher);
+ oneway void unregisterStrategyPreferredDevicesDispatcher(
+ IStrategyPreferredDevicesDispatcher dispatcher);
oneway void setRttEnabled(in boolean rttEnabled);
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
similarity index 82%
rename from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
rename to media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
index b1f99e6..db674c3 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
@@ -19,12 +19,12 @@
import android.media.AudioDeviceAttributes;
/**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
+ * AIDL for AudioService to signal audio strategy-preferred devices updates.
*
* {@hide}
*/
-oneway interface IStrategyPreferredDeviceDispatcher {
+oneway interface IStrategyPreferredDevicesDispatcher {
- void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
+ void dispatchPrefDevicesChanged(int strategyId, in List<AudioDeviceAttributes> devices);
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index c2168f1..9657b25e 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -25,6 +25,7 @@
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import android.os.Process;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Pair;
@@ -188,13 +189,14 @@
// COMMON CONSTANTS
private static final Range<Integer> POSITIVE_INTEGERS =
- Range.create(1, Integer.MAX_VALUE);
+ Range.create(1, Integer.MAX_VALUE);
private static final Range<Long> POSITIVE_LONGS =
- Range.create(1l, Long.MAX_VALUE);
+ Range.create(1L, Long.MAX_VALUE);
private static final Range<Rational> POSITIVE_RATIONALS =
- Range.create(new Rational(1, Integer.MAX_VALUE),
- new Rational(Integer.MAX_VALUE, 1));
- private static final Range<Integer> SIZE_RANGE = Range.create(1, 32768);
+ Range.create(new Rational(1, Integer.MAX_VALUE),
+ new Rational(Integer.MAX_VALUE, 1));
+ private static final Range<Integer> SIZE_RANGE =
+ Process.is64Bit() ? Range.create(1, 32768) : Range.create(1, 4096);
private static final Range<Integer> FRAME_RATE_RANGE = Range.create(0, 960);
private static final Range<Integer> BITRATE_RANGE = Range.create(0, 500000000);
private static final int DEFAULT_MAX_SUPPORTED_INSTANCES = 32;
@@ -1399,6 +1401,9 @@
/**
* Returns the range of supported video widths.
+ * <p class=note>
+ * 32-bit processes will not support resolutions larger than 4096x4096 due to
+ * the limited address space.
*/
public Range<Integer> getSupportedWidths() {
return mWidthRange;
@@ -1406,6 +1411,9 @@
/**
* Returns the range of supported video heights.
+ * <p class=note>
+ * 32-bit processes will not support resolutions larger than 4096x4096 due to
+ * the limited address space.
*/
public Range<Integer> getSupportedHeights() {
return mHeightRange;
@@ -1997,6 +2005,12 @@
* Performance points assume a single active codec. For use cases where multiple
* codecs are active, should use that highest pixel count, and add the frame rates of
* each individual codec.
+ * <p class=note>
+ * 32-bit processes will not support resolutions larger than 4096x4096 due to
+ * the limited address space, but performance points will be presented as is.
+ * In other words, even though a component publishes a performance point for
+ * a resolution higher than 4096x4096, it does not mean that the resolution is supported
+ * for 32-bit processes.
*/
@Nullable
public List<PerformancePoint> getSupportedPerformancePoints() {
@@ -2164,6 +2178,12 @@
if (size == null || size.getWidth() * size.getHeight() <= 0) {
continue;
}
+ if (size.getWidth() > SIZE_RANGE.getUpper()
+ || size.getHeight() > SIZE_RANGE.getUpper()) {
+ size = new Size(
+ Math.min(size.getWidth(), SIZE_RANGE.getUpper()),
+ Math.min(size.getHeight(), SIZE_RANGE.getUpper()));
+ }
Range<Long> range = Utils.parseLongRange(map.get(key), null);
if (range == null || range.getLower() < 0 || range.getUpper() < 0) {
continue;
@@ -2193,6 +2213,7 @@
(a.getMaxMacroBlockRate() < b.getMaxMacroBlockRate() ? -1 : 1) :
(a.getMaxFrameRate() != b.getMaxFrameRate()) ?
(a.getMaxFrameRate() < b.getMaxFrameRate() ? -1 : 1) : 0));
+
return Collections.unmodifiableList(ret);
}
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index cbc9ab7..cf06fad 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -6,3 +6,4 @@
olly@google.com
andrewlewis@google.com
sungsoo@google.com
+jmtrivi@google.com
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index d55e9d0..0d53ab1 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -333,7 +333,7 @@
if (deviceType != AUDIO_DEVICE_NONE) {
device.mType = (audio_devices_t)deviceType;
ScopedUtfChars address(env, deviceAddress);
- device.mAddress = address.c_str();
+ device.setAddress(address.c_str());
}
// create the native AudioEffect object
diff --git a/mime/OWNERS b/mime/OWNERS
index 6f9dbea..2357979 100644
--- a/mime/OWNERS
+++ b/mime/OWNERS
@@ -1 +1,2 @@
+include platform/libcore:/OWNERS
include /core/java/android/os/storage/OWNERS
diff --git a/packages/Connectivity/OWNERS b/packages/Connectivity/OWNERS
new file mode 100644
index 0000000..48e54da
--- /dev/null
+++ b/packages/Connectivity/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include platform/frameworks/base:/services/core/java/com/android/server/net/OWNERS
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index 082c148..3b7775a 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -57,7 +57,6 @@
<string name="print_forget_printer" msgid="5035287497291910766">"Supprimer l\'imprimante"</string>
<plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
<item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvée</item>
- <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvées</item>
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
@@ -78,7 +77,6 @@
<string name="all_services_title" msgid="5578662754874906455">"Tous les services"</string>
<plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
<item quantity="one">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimante</item>
- <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
<item quantity="other">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes</item>
</plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Impression de <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> en cours…"</string>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index 560c5dc..f6e901d 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -57,7 +57,6 @@
<string name="print_forget_printer" msgid="5035287497291910766">"Supprimer l\'imprimante"</string>
<plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
<item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvée</item>
- <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes trouvées</item>
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
@@ -78,7 +77,6 @@
<string name="all_services_title" msgid="5578662754874906455">"Tous les services"</string>
<plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
<item quantity="one">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimante</item>
- <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
<item quantity="other">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes</item>
</plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Impression de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" en cours…"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 671461b..e4ba93a 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -28,7 +28,7 @@
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Greška u konfiguraciji IP-a"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Niste povezani zbog slabog kvaliteta mreže"</string>
<string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Greška pri povezivanju na WiFi"</string>
- <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problem pri autentifikaciji."</string>
+ <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problem pri autentifikaciji"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nije se moguće povezati"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Nije se moguće povezati na aplikaciju \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"Provjerite lozinku i pokušajte ponovo"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 121da7b..19ba5d0 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -34,7 +34,7 @@
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"گذرواژه را بررسی و دوباره امتحان کنید"</string>
<string name="wifi_not_in_range" msgid="1541760821805777772">"در محدوده نیست"</string>
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"اتصال بهصورت خودکار انجام نمیشود"</string>
- <string name="wifi_no_internet" msgid="1774198889176926299">"بدون دسترسی به اینترنت"</string>
+ <string name="wifi_no_internet" msgid="1774198889176926299">"دسترسی به اینترنت ندارد"</string>
<string name="saved_network" msgid="7143698034077223645">"ذخیرهشده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"اتصال خودکار ازطریق %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده رتبهبندی شبکه"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 6bb1a3a..bdbfbc6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -488,7 +488,6 @@
<string name="wifi_status_mac_randomized" msgid="466382542497832189">"Les adresses MAC sont randomisées"</string>
<plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139">
<item quantity="one">%1$d appareil connecté</item>
- <item quantity="many">%1$d devices connected</item>
<item quantity="other">%1$d appareils connectés</item>
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Plus longtemps."</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 8690dd8..4509b09 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -28,7 +28,7 @@
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Échec de configuration de l\'adresse IP"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Non connecté en raison de la faible qualité du réseau"</string>
<string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Échec de la connexion Wi-Fi"</string>
- <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problème d\'authentification."</string>
+ <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problème d\'authentification"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Connexion impossible"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Impossible de se connecter au réseau \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"Vérifiez le mot de passe et réessayez"</string>
@@ -251,7 +251,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certification affichage sans fil"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Autoriser l\'enregistrement d\'infos Wi-Fi détaillées"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limiter la recherche Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC en Wi-Fi"</string>
+ <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Changement aléatoire d\'adresse MAC en Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Données mobiles toujours actives"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Accélération matérielle pour le partage de connexion"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string>
@@ -488,7 +488,6 @@
<string name="wifi_status_mac_randomized" msgid="466382542497832189">"La sélection des adresses MAC est aléatoire"</string>
<plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139">
<item quantity="one">%1$d appareil connecté</item>
- <item quantity="many">%1$d devices connected</item>
<item quantity="other">%1$d appareils connectés</item>
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Plus longtemps."</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 6e734bc..4ba2961 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -28,7 +28,7 @@
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉन्फ़िगरेशन की विफलता"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"खराब नेटवर्क होने के कारण कनेक्ट नहीं हुआ"</string>
<string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"वाईफ़ाई कनेक्शन विफलता"</string>
- <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"प्रमाणीकरण समस्या"</string>
+ <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"पुष्टि नहीं हो सकी"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"कनेक्ट नहीं हो पा रहा है"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' से कनेक्ट नहीं हो पा रहा है"</string>
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"पासवर्ड जाँचें और दोबारा कोशिश करें"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index ec2429b..3e06a3a 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -207,7 +207,7 @@
<string name="enable_adb_summary" msgid="3711526030096574316">"Mode debug ketika USB tersambung"</string>
<string name="clear_adb_keys" msgid="3010148733140369917">"Cabut otorisasi debug USB"</string>
<string name="enable_adb_wireless" msgid="6973226350963971018">"Proses debug nirkabel"</string>
- <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Mode debug saat Wi-Fi tersambung"</string>
+ <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Mode debug saat Wi-Fi terhubung"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"Error"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Proses debug nirkabel"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Untuk melihat dan menggunakan perangkat yang tersedia, aktifkan proses debug nirkabel"</string>
@@ -225,13 +225,13 @@
<string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Sambungkan dengan perangkat"</string>
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Kode penyambungan Wi-Fi"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Penyambungan perangkat gagal"</string>
- <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Pastikan perangkat tersambung ke jaringan yang sama."</string>
- <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Menyambungkan perangkat melalui Wi‑Fi dengan memindai Kode QR"</string>
+ <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Pastikan perangkat terhubung ke jaringan yang sama."</string>
+ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Sambungkan perangkat melalui Wi‑Fi dengan memindai Kode QR"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Menyambungkan perangkat…"</string>
<string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Gagal menyambungkan perangkat. Kode QR salah, atau perangkat tidak tersambung ke jaringan yang sama."</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Alamat IP & Port"</string>
<string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Memindai kode QR"</string>
- <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Menyambungkan perangkat melalui Wi‑Fi dengan memindai Kode QR"</string>
+ <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Sambungkan perangkat melalui Wi‑Fi dengan memindai Kode QR"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Harap sambungkan ke jaringan Wi-Fi"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Pintasan laporan bug"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 3729a83..d62d944 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -28,7 +28,7 @@
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP ပြုပြင်ခြင်း မအောင်မြင်ပါ"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"ကွန်ရက်ချိတ်ဆက်မှု အားနည်းသည့်အတွက် ချိတ်ဆက်ထားခြင်း မရှိပါ"</string>
<string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi ချိတ်ဆက်မှု မအောင်မြင်ပါ"</string>
- <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"စစ်မှန်ကြောင်းအတည်ပြုရန်၌ ပြသနာရှိခြင်း"</string>
+ <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"အထောက်အထားစိစစ်မှု ပြဿနာ"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"ချိတ်ဆက်၍ မရပါ"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' နှင့် ချိတ်ဆက်၍ မရပါ"</string>
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"စကားဝှက်ကို စစ်ဆေးပြီး ထပ်လုပ်ကြည့်ပါ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 0e0e761..d567ee2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -34,7 +34,7 @@
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"Sjekk passordet og prøv igjen"</string>
<string name="wifi_not_in_range" msgid="1541760821805777772">"Utenfor område"</string>
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kobler ikke til automatisk"</string>
- <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen Internett-tilgang"</string>
+ <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internettilgang"</string>
<string name="saved_network" msgid="7143698034077223645">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilkoblet via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk tilkoblet via leverandør av nettverksvurdering"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index d200f50..c9bbe71 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -28,7 +28,7 @@
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP କନଫିଗରେଶନ ବିଫଳ ହୋଇଛି"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"ନିମ୍ନ ମାନର ନେଟ୍ୱର୍କ କାରଣରୁ ସଂଯୁକ୍ତ ହୋଇନାହିଁ"</string>
<string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"ୱାଇଫାଇ ସଂଯୋଗ ବିଫଳ ହୋଇଛି"</string>
- <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ସତ୍ୟାପନରେ ସମସ୍ୟା"</string>
+ <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ପ୍ରମାଣୀକରଣରେ ସମସ୍ୟା"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"ସଂଯୋଗ କରିପାରିବ ନାହିଁ"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' ସହିତ ସଂଯୁକ୍ତ ହୋଇପାରୁନାହିଁ"</string>
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"ପାସ୍ୱର୍ଡ ଯାଞ୍ଚ କରନ୍ତୁ ଏବଂ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index cc1626c..737c000 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -28,7 +28,7 @@
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Zlyhanie konfigurácie adresy IP"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Nepripojené z dôvodu siete nízkej kvality"</string>
<string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Zlyhanie pripojenia Wi‑Fi"</string>
- <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problém s overením totožnosti"</string>
+ <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problém s overením"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nedá sa pripojiť"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"K sieti <xliff:g id="AP_NAME">%1$s</xliff:g> sa nedá pripojiť"</string>
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"Skontrolujte heslo a skúste to znova"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 5d4e975..1a2d5e2 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -28,7 +28,7 @@
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Pagkabigo ng Configuration ng IP"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Hindi nakakonekta dahil mababa ang kalidad ng network"</string>
<string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Pagkabigo ng Koneksyon sa WiFi"</string>
- <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problema sa pagpapatotoo"</string>
+ <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problema sa pag-authenticate"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Hindi makakonekta"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Hindi makakonekta sa \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"Suriin ang password at subukang muli"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 2ae55fe..2325efd 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -28,7 +28,7 @@
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP manzilini sozlab bo‘lmadi"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Sifatsiz tarmoq sababli ulanib bo‘lmadi"</string>
<string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi ulanishini o‘rnatib bo‘lmadi"</string>
- <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Tasdiqdan o‘tishda muammo"</string>
+ <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Tekshiruvda muammo"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Tarmoqqa ulanilmadi"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"“<xliff:g id="AP_NAME">%1$s</xliff:g>” nomli tarmoqqa ulanilmadi"</string>
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"Parolni tekshirib, qaytadan urining"</string>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 51f69a9..2eba38e 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -180,7 +180,7 @@
<!-- Default state of tap to wake -->
<bool name="def_double_tap_to_wake">true</bool>
- <!-- Default for Settings.Secure.NFC_PAYMENT_COMPONENT -->
+ <!-- Default for Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT -->
<string name="def_nfc_payment_component"></string>
<!-- Default setting for ability to add users from the lock screen -->
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 757bbda..0fdb282 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -154,6 +154,7 @@
<uses-permission android:name="android.permission.MANAGE_CONTENT_CAPTURE" />
<uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS" />
<uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" />
+ <uses-permission android:name="android.permission.MANAGE_SEARCH_UI" />
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.SET_TIME" />
@@ -333,6 +334,9 @@
<!-- Permission needed for CTS test - TimeManagerTest -->
<uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
+ <!-- Permission needed for CTS test - CtsHdmiCecHostTestCases -->
+ <uses-permission android:name="android.permission.HDMI_CEC" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 02815a57..63b9bb3 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -378,6 +378,9 @@
}
}
+ @Override
+ public void onEarlyReportFinished() {}
+
/**
* Reads bugreport id and links it to the bugreport info to track a bugreport that is in
* process. id is incremented in the dumpstate code.
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 2eafc2f..3e858c2 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -65,7 +65,6 @@
<string name="kg_wrong_pin" msgid="4160978845968732624">"NIP incorrect"</string>
<plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="991400408675793914">
<item quantity="one">Réessayer dans <xliff:g id="NUMBER">%d</xliff:g> seconde.</item>
- <item quantity="many">Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds.</item>
<item quantity="other">Réessayer dans <xliff:g id="NUMBER">%d</xliff:g> secondes.</item>
</plurals>
<string name="kg_pattern_instructions" msgid="5376036737065051736">"Dessinez votre schéma"</string>
@@ -89,13 +88,11 @@
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"NIP de carte SIM incorrect. Vous devez maintenant communiquer avec votre fournisseur de services pour déverrouiller votre appareil."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
<item quantity="one">Le NIP de la carte SIM incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
- <item quantity="many">Incorrect SIM PIN code, you have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="other">Le NIP de la carte SIM incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
</plurals>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"La carte SIM est inutilisable. Communiquez avec votre fournisseur de services."</string>
<plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
<item quantity="one">Le code PUK de la carte SIM est incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM devienne définitivement inutilisable.</item>
- <item quantity="many">Incorrect SIM PUK code, you have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable.</item>
<item quantity="other">Le code PUK de la carte SIM est incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM devienne définitivement inutilisable.</item>
</plurals>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Le déverrouillage par NIP de la carte SIM a échoué."</string>
@@ -117,29 +114,24 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"L\'appareil a été verrouillé manuellement"</string>
<plurals name="kg_prompt_reason_time_pattern" formatted="false" msgid="1337428979661197957">
<item quantity="one">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heure. Confirmez le schéma.</item>
- <item quantity="many">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm pattern.</item>
<item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le schéma.</item>
</plurals>
<plurals name="kg_prompt_reason_time_pin" formatted="false" msgid="6444519502336330270">
<item quantity="one">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heure. Confirmez le NIP.</item>
- <item quantity="many">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm PIN.</item>
<item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le NIP.</item>
</plurals>
<plurals name="kg_prompt_reason_time_password" formatted="false" msgid="5343961527665116914">
<item quantity="one">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heure. Confirmez le mot de passe.</item>
- <item quantity="many">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
<item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le mot de passe.</item>
</plurals>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Doigt non reconnu"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Doigt non reconnu"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
- <item quantity="many">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="other">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
</plurals>
<plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
<item quantity="one">La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM devienne définitivement inutilisable. Pour obtenir plus de détails, communiquez avec votre fournisseur de services.</item>
- <item quantity="many">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item>
<item quantity="other">La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM devienne définitivement inutilisable. Pour obtenir plus de détails, communiquez avec votre fournisseur de services.</item>
</plurals>
<string name="clock_title_default" msgid="6342735240617459864">"Par défaut"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index 824ea41..8551fab 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -65,7 +65,6 @@
<string name="kg_wrong_pin" msgid="4160978845968732624">"Code incorrect"</string>
<plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="991400408675793914">
<item quantity="one">Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> seconde.</item>
- <item quantity="many">Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds.</item>
<item quantity="other">Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> secondes.</item>
</plurals>
<string name="kg_pattern_instructions" msgid="5376036737065051736">"Dessinez votre schéma"</string>
@@ -89,13 +88,11 @@
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Code PIN de la carte SIM incorrect. Vous devez désormais contacter votre opérateur pour déverrouiller votre appareil."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
<item quantity="one">Code PIN de la carte SIM incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
- <item quantity="many">Incorrect SIM PIN code, you have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="other">Code PIN de la carte SIM incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
</plurals>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"La carte SIM est inutilisable. Contactez votre opérateur."</string>
<plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
<item quantity="one">Clé PUK de la carte SIM incorrecte. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM ne devienne définitivement inutilisable.</item>
- <item quantity="many">Incorrect SIM PUK code, you have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable.</item>
<item quantity="other">Clé PUK de la carte SIM incorrecte. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM ne devienne définitivement inutilisable.</item>
</plurals>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Échec du déverrouillage à l\'aide du code PIN de la carte SIM."</string>
@@ -117,29 +114,24 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Appareil verrouillé manuellement"</string>
<plurals name="kg_prompt_reason_time_pattern" formatted="false" msgid="1337428979661197957">
<item quantity="one">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heure. Confirmez le schéma.</item>
- <item quantity="many">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm pattern.</item>
<item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le schéma.</item>
</plurals>
<plurals name="kg_prompt_reason_time_pin" formatted="false" msgid="6444519502336330270">
<item quantity="one">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heure. Confirmez le code.</item>
- <item quantity="many">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm PIN.</item>
<item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le code.</item>
</plurals>
<plurals name="kg_prompt_reason_time_password" formatted="false" msgid="5343961527665116914">
<item quantity="one">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heure. Confirmez le mot de passe.</item>
- <item quantity="many">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
<item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le mot de passe.</item>
</plurals>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Non reconnu"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Non reconnu"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
<item quantity="one">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentative restante.</item>
- <item quantity="many">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="other">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentatives restantes.</item>
</plurals>
<plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
<item quantity="one">La carte SIM est maintenant désactivée. Saisissez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM ne devienne définitivement inutilisable. Pour de plus amples informations, veuillez contacter votre opérateur.</item>
- <item quantity="many">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item>
<item quantity="other">La carte SIM est maintenant désactivée. Saisissez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM ne devienne définitivement inutilisable. Pour de plus amples informations, veuillez contacter votre opérateur.</item>
</plurals>
<string name="clock_title_default" msgid="6342735240617459864">"Par défaut"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 323fea5..d19f30e 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -101,9 +101,9 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Aloqa yo‘q."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Matn kiritish usulini almashtirish"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Parvoz rejimi"</string>
- <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Qurilma o‘chirib yoqilgandan keyin grafik kalit talab qilinadi"</string>
- <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Qurilma o‘chirib yoqilgandan keyin PIN kod talab qilinadi"</string>
- <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Qurilma o‘chirib yoqilgandan keyin parol talab qilinadi"</string>
+ <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Qurilma qayta ishga tushganidan keyin grafik kalitni kiritish zarur"</string>
+ <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Qurilma qayta ishga tushganidan keyin PIN kodni kiritish zarur"</string>
+ <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Qurilma qayta ishga tushganidan keyin parolni kiritish zarur"</string>
<string name="kg_prompt_reason_timeout_pattern" msgid="9170360502528959889">"Qo‘shimcha xavfsizlik chorasi sifatida grafik kalit talab qilinadi"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="5945186097160029201">"Qo‘shimcha xavfsizlik chorasi sifatida PIN kod talab qilinadi"</string>
<string name="kg_prompt_reason_timeout_password" msgid="2258263949430384278">"Qo‘shimcha xavfsizlik chorasi sifatida parol talab qilinadi"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 9e9eb85..6baa848 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -325,7 +325,6 @@
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
<item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> autre notification à l\'intérieur.</item>
- <item quantity="many"><xliff:g id="NUMBER_1">%s</xliff:g> more notifications inside.</item>
<item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> autres notifications à l\'intérieur.</item>
</plurals>
<string name="notification_summary_message_format" msgid="5158219088501909966">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g> : <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string>
@@ -401,7 +400,6 @@
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Écon. données activé"</string>
<plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
<item quantity="one">%d appareil</item>
- <item quantity="many">%d devices</item>
<item quantity="other">%d appareils</item>
</plurals>
<string name="quick_settings_notifications_label" msgid="3379631363952582758">"Notifications"</string>
@@ -494,7 +492,6 @@
<string name="user_limit_reached_title" msgid="2429229448830346057">"Limite d\'utilisateurs atteinte"</string>
<plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
<item quantity="one">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> utilisateur.</item>
- <item quantity="many">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item>
<item quantity="other">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> utilisateurs.</item>
</plurals>
<string name="user_remove_user_title" msgid="9124124694835811874">"Supprimer l\'utilisateur?"</string>
@@ -761,12 +758,10 @@
<string name="snoozed_for_time" msgid="7586689374860469469">"Reporté pour <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
<plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
<item quantity="one">%d heure</item>
- <item quantity="many">%d hours</item>
<item quantity="other">%d heures</item>
</plurals>
<plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
<item quantity="one">%d minute</item>
- <item quantity="many">%d minutes</item>
<item quantity="other">%d minutes</item>
</plurals>
<string name="battery_panel_title" msgid="5931157246673665963">"Utilisation de la pile"</string>
@@ -1045,7 +1040,6 @@
<string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'application pour laquelle ajouter des commandes"</string>
<plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
<item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> commande ajoutée.</item>
- <item quantity="many"><xliff:g id="NUMBER_1">%s</xliff:g> controls added.</item>
<item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> commandes ajoutées.</item>
</plurals>
<string name="controls_removed" msgid="3731789252222856959">"Supprimé"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 5d1b3a1..ad258b2 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -325,7 +325,6 @@
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g> autres"</string>
<plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
<item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> autre notification à l\'intérieur.</item>
- <item quantity="many"><xliff:g id="NUMBER_1">%s</xliff:g> more notifications inside.</item>
<item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> autres notifications à l\'intérieur.</item>
</plurals>
<string name="notification_summary_message_format" msgid="5158219088501909966">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g> : <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string>
@@ -401,7 +400,6 @@
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Écon. données activé"</string>
<plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
<item quantity="one">%d appareil</item>
- <item quantity="many">%d devices</item>
<item quantity="other">%d appareils</item>
</plurals>
<string name="quick_settings_notifications_label" msgid="3379631363952582758">"Notifications"</string>
@@ -494,7 +492,6 @@
<string name="user_limit_reached_title" msgid="2429229448830346057">"Limite nombre utilisateurs atteinte"</string>
<plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
<item quantity="one">Vous pouvez ajouter <xliff:g id="COUNT">%d</xliff:g> profil utilisateur.</item>
- <item quantity="many">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item>
<item quantity="other">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> profils utilisateur.</item>
</plurals>
<string name="user_remove_user_title" msgid="9124124694835811874">"Supprimer l\'utilisateur ?"</string>
@@ -761,12 +758,10 @@
<string name="snoozed_for_time" msgid="7586689374860469469">"Répétée après <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
<plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
<item quantity="one">%d heure</item>
- <item quantity="many">%d hours</item>
<item quantity="other">%d heures</item>
</plurals>
<plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
<item quantity="one">%d minute</item>
- <item quantity="many">%d minutes</item>
<item quantity="other">%d minutes</item>
</plurals>
<string name="battery_panel_title" msgid="5931157246673665963">"Utilisation batterie"</string>
@@ -1045,7 +1040,6 @@
<string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'appli pour laquelle ajouter des commandes"</string>
<plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
<item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> commande ajoutée.</item>
- <item quantity="many"><xliff:g id="NUMBER_1">%s</xliff:g> controls added.</item>
<item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> commandes ajoutées.</item>
</plurals>
<string name="controls_removed" msgid="3731789252222856959">"Supprimé"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index cc8b5fe..f6b71e4 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -776,7 +776,7 @@
<string name="keyboard_key_dpad_left" msgid="8329738048908755640">"Зүүн"</string>
<string name="keyboard_key_dpad_right" msgid="6282105433822321767">"Баруун"</string>
<string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Гол хэсэг"</string>
- <string name="keyboard_key_tab" msgid="4592772350906496730">"Чихтэй хуудас"</string>
+ <string name="keyboard_key_tab" msgid="4592772350906496730">"Таб"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Зай"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Оруулах"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Арилгах"</string>
@@ -826,7 +826,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Идэвхтэй"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Идэвхгүй"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Боломжгүй"</string>
- <string name="nav_bar" msgid="4642708685386136807">"Навигацийн самбар"</string>
+ <string name="nav_bar" msgid="4642708685386136807">"Навигацын самбар"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Бүдүүвч"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Нэмэлт зүүн товчлуураар шивэх"</string>
<string name="right_nav_bar_button_type" msgid="4472566498647364715">"Нэмэлт баруун товчлуураар шивэх"</string>
@@ -848,7 +848,7 @@
<string name="reset" msgid="8715144064608810383">"Шинэчлэх"</string>
<string name="adjust_button_width" msgid="8313444823666482197">"Товчлуурын өргөнг тохируулах"</string>
<string name="clipboard" msgid="8517342737534284617">"Түр санах ой"</string>
- <string name="accessibility_key" msgid="3471162841552818281">"Навигацийн товчлуурыг өөрчлөх"</string>
+ <string name="accessibility_key" msgid="3471162841552818281">"Навигацын товчлуурыг өөрчлөх"</string>
<string name="left_keycode" msgid="8211040899126637342">"Зүүн түлхүүрийн код"</string>
<string name="right_keycode" msgid="2480715509844798438">"Баруун түлхүүрийн код"</string>
<string name="left_icon" msgid="5036278531966897006">"Зүүн дүрс тэмдэг"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 9c69703..555b1c0 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -563,9 +563,9 @@
<string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"Abrir as definições de VPN"</string>
<string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string>
<string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"Abrir credenciais fidedignas"</string>
- <string name="monitoring_description_network_logging" msgid="577305979174002252">"O seu gestor ativou os registos de rede, que monitorizam o tráfego no seu dispositivo.\n\nPara obter mais informações, contacte o gestor."</string>
+ <string name="monitoring_description_network_logging" msgid="577305979174002252">"O seu gestor ativou os registos de rede, que monitorizam o tráfego no seu dispositivo.\n\nPara mais informações, contacte o gestor."</string>
<string name="monitoring_description_vpn" msgid="1685428000684586870">"Concedeu autorização a uma app para configurar uma ligação VPN.\n\nEsta app pode monitorizar a atividade do dispositivo e da rede, incluindo emails, aplicações e Sites."</string>
- <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"O seu perfil de trabalho é gerido por <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nO seu gestor tem a capacidade de monitorizar a sua atividade da rede, incluindo emails, aplicações e Sites.\n\nPara obter mais informações, contacte o gestor.\n\nAlém disso, está ligado a uma VPN, que pode monitorizar a sua atividade da rede."</string>
+ <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"O seu perfil de trabalho é gerido por <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nO seu gestor tem a capacidade de monitorizar a sua atividade da rede, incluindo emails, aplicações e Sites.\n\nPara mais informações, contacte o gestor.\n\nAlém disso, está ligado a uma VPN, que pode monitorizar a sua atividade da rede."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="monitoring_description_app" msgid="376868879287922929">"Está associado à app <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorizar a sua atividade de rede, incluindo emails, aplicações e Sites."</string>
<string name="monitoring_description_app_personal" msgid="1970094872688265987">"Está ligado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorizar a atividade da rede pessoal, incluindo emails, aplicações e Sites."</string>
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4b9d0f1..77cb125 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -196,7 +196,6 @@
"java/com/android/server/connectivity/KeepaliveTracker.java",
"java/com/android/server/connectivity/LingerMonitor.java",
"java/com/android/server/connectivity/MockableSystemProperties.java",
- "java/com/android/server/connectivity/MultipathPolicyTracker.java",
"java/com/android/server/connectivity/Nat464Xlat.java",
"java/com/android/server/connectivity/NetdEventListenerService.java",
"java/com/android/server/connectivity/NetworkAgentInfo.java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d867442..2e1fbb7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -113,7 +113,6 @@
import android.net.NetworkMonitorManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkProvider;
-import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
@@ -129,6 +128,7 @@
import android.net.SocketKeepalive;
import android.net.TetheringManager;
import android.net.UidRange;
+import android.net.UidRangeParcel;
import android.net.Uri;
import android.net.VpnManager;
import android.net.VpnService;
@@ -136,8 +136,6 @@
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
import android.net.shared.PrivateDnsConfig;
-import android.net.util.LinkPropertiesUtils.CompareOrUpdateResult;
-import android.net.util.LinkPropertiesUtils.CompareResult;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
import android.os.Binder;
@@ -146,6 +144,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.INetworkActivityListener;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
@@ -191,6 +190,8 @@
import com.android.internal.util.MessageUtils;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.AutodestructReference;
import com.android.server.connectivity.DataConnectionStats;
@@ -876,6 +877,10 @@
*/
@VisibleForTesting
public static class Dependencies {
+ public int getCallingUid() {
+ return Binder.getCallingUid();
+ }
+
/**
* Get system properties to use in ConnectivityService.
*/
@@ -1376,8 +1381,11 @@
return;
}
final String action = blocked ? "BLOCKED" : "UNBLOCKED";
+ final NetworkRequest satisfiedRequest = nri.getSatisfiedRequest();
+ final int requestId = satisfiedRequest != null
+ ? satisfiedRequest.requestId : nri.mRequests.get(0).requestId;
mNetworkInfoBlockingLogs.log(String.format(
- "%s %d(%d) on netId %d", action, nri.mUid, nri.request.requestId, net.getNetId()));
+ "%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
}
/**
@@ -1408,7 +1416,7 @@
@Override
public NetworkInfo getActiveNetworkInfo() {
enforceAccessPermission();
- final int uid = Binder.getCallingUid();
+ final int uid = mDeps.getCallingUid();
final NetworkState state = getUnfilteredActiveNetworkState(uid);
filterNetworkStateForUid(state, uid, false);
maybeLogBlockedNetworkInfo(state.networkInfo, uid);
@@ -1418,7 +1426,7 @@
@Override
public Network getActiveNetwork() {
enforceAccessPermission();
- return getActiveNetworkForUidInternal(Binder.getCallingUid(), false);
+ return getActiveNetworkForUidInternal(mDeps.getCallingUid(), false);
}
@Override
@@ -1458,7 +1466,7 @@
// Public because it's used by mLockdownTracker.
public NetworkInfo getActiveNetworkInfoUnfiltered() {
enforceAccessPermission();
- final int uid = Binder.getCallingUid();
+ final int uid = mDeps.getCallingUid();
NetworkState state = getUnfilteredActiveNetworkState(uid);
return state.networkInfo;
}
@@ -1474,7 +1482,7 @@
@Override
public NetworkInfo getNetworkInfo(int networkType) {
enforceAccessPermission();
- final int uid = Binder.getCallingUid();
+ final int uid = mDeps.getCallingUid();
if (getVpnUnderlyingNetworks(uid) != null) {
// A VPN is active, so we may need to return one of its underlying networks. This
// information is not available in LegacyTypeTracker, so we have to get it from
@@ -1519,7 +1527,7 @@
@Override
public Network getNetworkForType(int networkType) {
enforceAccessPermission();
- final int uid = Binder.getCallingUid();
+ final int uid = mDeps.getCallingUid();
NetworkState state = getFilteredNetworkState(networkType, uid);
if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, false)) {
return state.network;
@@ -1566,7 +1574,7 @@
result.put(
nai.network,
maybeSanitizeLocationInfoForCaller(
- nc, Binder.getCallingUid(), callingPackageName));
+ nc, mDeps.getCallingUid(), callingPackageName));
}
synchronized (mVpns) {
@@ -1581,7 +1589,7 @@
result.put(
network,
maybeSanitizeLocationInfoForCaller(
- nc, Binder.getCallingUid(), callingPackageName));
+ nc, mDeps.getCallingUid(), callingPackageName));
}
}
}
@@ -1611,7 +1619,7 @@
@Override
public LinkProperties getActiveLinkProperties() {
enforceAccessPermission();
- final int uid = Binder.getCallingUid();
+ final int uid = mDeps.getCallingUid();
NetworkState state = getUnfilteredActiveNetworkState(uid);
if (state.linkProperties == null) return null;
return linkPropertiesRestrictedForCallerPermissions(state.linkProperties,
@@ -1625,7 +1633,7 @@
final LinkProperties lp = getLinkProperties(nai);
if (lp == null) return null;
return linkPropertiesRestrictedForCallerPermissions(
- lp, Binder.getCallingPid(), Binder.getCallingUid());
+ lp, Binder.getCallingPid(), mDeps.getCallingUid());
}
// TODO - this should be ALL networks
@@ -1635,7 +1643,7 @@
final LinkProperties lp = getLinkProperties(getNetworkAgentInfoForNetwork(network));
if (lp == null) return null;
return linkPropertiesRestrictedForCallerPermissions(
- lp, Binder.getCallingPid(), Binder.getCallingUid());
+ lp, Binder.getCallingPid(), mDeps.getCallingUid());
}
@Nullable
@@ -1657,17 +1665,17 @@
synchronized (nai) {
if (nai.networkCapabilities == null) return null;
return networkCapabilitiesRestrictedForCallerPermissions(
- nai.networkCapabilities, Binder.getCallingPid(), Binder.getCallingUid());
+ nai.networkCapabilities, Binder.getCallingPid(), mDeps.getCallingUid());
}
}
@Override
public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
- mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
+ mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName);
enforceAccessPermission();
return maybeSanitizeLocationInfoForCaller(
getNetworkCapabilitiesInternal(network),
- Binder.getCallingUid(), callingPackageName);
+ mDeps.getCallingUid(), callingPackageName);
}
@VisibleForTesting
@@ -1705,16 +1713,17 @@
return newNc;
}
- Binder.withCleanCallingIdentity(
- () -> {
- if (!mLocationPermissionChecker.checkLocationPermission(
- callerPkgName, null /* featureId */, callerUid, null /* message */)) {
- // Caller does not have the requisite location permissions. Reset the
- // owner's UID in the NetworkCapabilities.
- newNc.setOwnerUid(INVALID_UID);
- }
- }
- );
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ callerPkgName, null /* featureId */, callerUid, null /* message */)) {
+ // Caller does not have the requisite location permissions. Reset the
+ // owner's UID in the NetworkCapabilities.
+ newNc.setOwnerUid(INVALID_UID);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
return newNc;
}
@@ -1755,7 +1764,7 @@
}
private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) {
- if (!mPermissionMonitor.hasUseBackgroundNetworksPermission(Binder.getCallingUid())) {
+ if (!mPermissionMonitor.hasUseBackgroundNetworksPermission(mDeps.getCallingUid())) {
nc.addCapability(NET_CAPABILITY_FOREGROUND);
}
}
@@ -1780,14 +1789,6 @@
}
@Override
- @Deprecated
- public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
- Log.w(TAG, "Shame on UID " + Binder.getCallingUid()
- + " for calling the hidden API getNetworkQuotaInfo(). Shame!");
- return new NetworkQuotaInfo();
- }
-
- @Override
public boolean isActiveNetworkMetered() {
enforceAccessPermission();
@@ -1816,7 +1817,7 @@
// requestRouteToHost. In Q, GnssLocationProvider is changed to not call requestRouteToHost
// for devices launched with Q and above. However, existing devices upgrading to Q and
// above must continued to be supported for few more releases.
- if (isSystem(Binder.getCallingUid()) && SystemProperties.getInt(
+ if (isSystem(mDeps.getCallingUid()) && SystemProperties.getInt(
"ro.product.first_api_level", 0) > Build.VERSION_CODES.P) {
log("This method exists only for app backwards compatibility"
+ " and must not be called by system services.");
@@ -1882,7 +1883,7 @@
return false;
}
- final int uid = Binder.getCallingUid();
+ final int uid = mDeps.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
LinkProperties lp;
@@ -2302,7 +2303,7 @@
*/
@Override
public void systemReady() {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ if (mDeps.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Calling Uid is not system uid.");
}
systemReadyInternal();
@@ -2340,6 +2341,31 @@
}
/**
+ * Start listening for default data network activity state changes.
+ */
+ @Override
+ public void registerNetworkActivityListener(@NonNull INetworkActivityListener l) {
+ // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here
+ }
+
+ /**
+ * Stop listening for default data network activity state changes.
+ */
+ @Override
+ public void unregisterNetworkActivityListener(@NonNull INetworkActivityListener l) {
+ // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here
+ }
+
+ /**
+ * Check whether the default network radio is currently active.
+ */
+ @Override
+ public boolean isDefaultNetworkActive() {
+ // TODO: Replace isNetworkActive() in NMS.
+ return false;
+ }
+
+ /**
* Setup data activity tracking for the given network.
*
* Every {@code setupDataActivityTracking} should be paired with a
@@ -2528,7 +2554,7 @@
if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump " + tag + " from from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + Binder.getCallingPid() + ", uid=" + mDeps.getCallingUid()
+ " due to missing android.permission.DUMP permission");
return false;
} else {
@@ -2709,7 +2735,7 @@
* Return an array of all current NetworkRequest sorted by request id.
*/
@VisibleForTesting
- protected NetworkRequestInfo[] requestsSortedById() {
+ NetworkRequestInfo[] requestsSortedById() {
NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
requests = mNetworkRequests.values().toArray(requests);
// Sort the array based off the NRI containing the min requestId in its requests.
@@ -2822,6 +2848,7 @@
break;
}
case NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED: {
+ // TODO: prevent loops, e.g., if a network declares itself as underlying.
if (!nai.supportsUnderlyingNetworks()) {
Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
break;
@@ -3421,6 +3448,7 @@
}
}
nai.clearLingerState();
+ propagateUnderlyingNetworkCapabilities(nai.network);
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
mDefaultNetworkNai = null;
updateDataActivityTracking(null /* newNetwork */, nai);
@@ -3428,9 +3456,6 @@
ensureNetworkTransitionWakelock(nai.toShortString());
}
mLegacyTypeTracker.remove(nai, wasDefault);
- if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
- propagateUnderlyingNetworkCapabilities();
- }
rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
if (nai.created) {
@@ -3560,30 +3585,58 @@
return false;
}
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (reason == UnneededFor.LINGER && nri.request.isBackgroundRequest()) {
+ if (reason == UnneededFor.LINGER
+ && !nri.isMultilayerRequest()
+ && nri.mRequests.get(0).isBackgroundRequest()) {
// Background requests don't affect lingering.
continue;
}
- // If this Network is already the highest scoring Network for a request, or if
- // there is hope for it to become one if it validated, then it is needed.
- if (nri.request.isRequest() && nai.satisfies(nri.request) &&
- (nai.isSatisfyingRequest(nri.request.requestId) ||
- // Note that this catches two important cases:
- // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
- // is currently satisfying the request. This is desirable when
- // cellular ends up validating but WiFi does not.
- // 2. Unvalidated WiFi will not be reaped when validated cellular
- // is currently satisfying the request. This is desirable when
- // WiFi ends up validating and out scoring cellular.
- nri.mSatisfier.getCurrentScore()
- < nai.getCurrentScoreAsValidated())) {
+ if (isNetworkPotentialSatisfier(nai, nri)) {
return false;
}
}
return true;
}
+ private boolean isNetworkPotentialSatisfier(
+ @NonNull final NetworkAgentInfo candidate, @NonNull final NetworkRequestInfo nri) {
+ // listen requests won't keep up a network satisfying it. If this is not a multilayer
+ // request, we can return immediately. For multilayer requests, we have to check to see if
+ // any of the multilayer requests may have a potential satisfier.
+ if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+ return false;
+ }
+ for (final NetworkRequest req : nri.mRequests) {
+ // As non-multilayer listen requests have already returned, the below would only happen
+ // for a multilayer request therefore continue to the next request if available.
+ if (req.isListen()) {
+ continue;
+ }
+ // If this Network is already the highest scoring Network for a request, or if
+ // there is hope for it to become one if it validated, then it is needed.
+ if (candidate.satisfies(req)) {
+ // As soon as a network is found that satisfies a request, return. Specifically for
+ // multilayer requests, returning as soon as a NetworkAgentInfo satisfies a request
+ // is important so as to not evaluate lower priority requests further in
+ // nri.mRequests.
+ final boolean isNetworkNeeded = candidate.isSatisfyingRequest(req.requestId)
+ // Note that this catches two important cases:
+ // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
+ // is currently satisfying the request. This is desirable when
+ // cellular ends up validating but WiFi does not.
+ // 2. Unvalidated WiFi will not be reaped when validated cellular
+ // is currently satisfying the request. This is desirable when
+ // WiFi ends up validating and out scoring cellular.
+ || nri.mSatisfier.getCurrentScore()
+ < candidate.getCurrentScoreAsValidated();
+ return isNetworkNeeded;
+ }
+ }
+
+ return false;
+ }
+
private NetworkRequestInfo getNriForAppRequest(
NetworkRequest request, int callingUid, String requestedOperation) {
final NetworkRequestInfo nri = mNetworkRequests.get(request);
@@ -3880,8 +3933,12 @@
new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
- Binder.withCleanCallingIdentity(() ->
- mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.startActivityAsUser(appIntent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private class CaptivePortalImpl extends ICaptivePortal.Stub {
@@ -3909,7 +3966,7 @@
if (request == CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED) {
checkNetworkStackPermission();
- nm.forceReevaluation(Binder.getCallingUid());
+ nm.forceReevaluation(mDeps.getCallingUid());
}
}
@@ -4376,7 +4433,7 @@
public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
enforceAccessPermission();
enforceInternetPermission();
- final int uid = Binder.getCallingUid();
+ final int uid = mDeps.getCallingUid();
final int connectivityInfo = encodeBool(hasConnectivity);
// Handle ConnectivityDiagnostics event before attempting to revalidate the network. This
@@ -4446,13 +4503,13 @@
if (globalProxy != null) return globalProxy;
if (network == null) {
// Get the network associated with the calling UID.
- final Network activeNetwork = getActiveNetworkForUidInternal(Binder.getCallingUid(),
+ final Network activeNetwork = getActiveNetworkForUidInternal(mDeps.getCallingUid(),
true);
if (activeNetwork == null) {
return null;
}
return getLinkPropertiesProxyInfo(activeNetwork);
- } else if (mDeps.queryUserAccess(Binder.getCallingUid(), network.getNetId())) {
+ } else if (mDeps.queryUserAccess(mDeps.getCallingUid(), network.getNetId())) {
// Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
// caller may not have.
return getLinkPropertiesProxyInfo(network);
@@ -4621,7 +4678,7 @@
*/
@Override
public ParcelFileDescriptor establishVpn(VpnConfig config) {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
throwIfLockdownEnabled();
return mVpns.get(user).establish(config);
@@ -4642,7 +4699,7 @@
*/
@Override
public boolean provisionVpnProfile(@NonNull VpnProfile profile, @NonNull String packageName) {
- final int user = UserHandle.getUserId(Binder.getCallingUid());
+ final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
return mVpns.get(user).provisionVpnProfile(packageName, profile, mKeyStore);
}
@@ -4660,7 +4717,7 @@
*/
@Override
public void deleteVpnProfile(@NonNull String packageName) {
- final int user = UserHandle.getUserId(Binder.getCallingUid());
+ final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
mVpns.get(user).deleteVpnProfile(packageName, mKeyStore);
}
@@ -4677,7 +4734,7 @@
*/
@Override
public void startVpnProfile(@NonNull String packageName) {
- final int user = UserHandle.getUserId(Binder.getCallingUid());
+ final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
throwIfLockdownEnabled();
mVpns.get(user).startVpnProfile(packageName, mKeyStore);
@@ -4694,7 +4751,7 @@
*/
@Override
public void stopVpnProfile(@NonNull String packageName) {
- final int user = UserHandle.getUserId(Binder.getCallingUid());
+ final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
mVpns.get(user).stopVpnProfile(packageName);
}
@@ -4706,7 +4763,7 @@
*/
@Override
public void startLegacyVpn(VpnProfile profile) {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
final LinkProperties egress = getActiveLinkProperties();
if (egress == null) {
throw new IllegalStateException("Missing active network connection");
@@ -4819,17 +4876,35 @@
}
}
+ private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
+ final Network defaultNetwork = getNetwork(getDefaultNetwork());
+ if (underlyingNetworks == null && defaultNetwork != null) {
+ // null underlying networks means to track the default.
+ underlyingNetworks = new Network[] { defaultNetwork };
+ }
+ return underlyingNetworks;
+ }
+
+ // Returns true iff |network| is an underlying network of |nai|.
+ private boolean hasUnderlyingNetwork(NetworkAgentInfo nai, Network network) {
+ // TODO: support more than one level of underlying networks, either via a fixed-depth search
+ // (e.g., 2 levels of underlying networks), or via loop detection, or....
+ if (!nai.supportsUnderlyingNetworks()) return false;
+ final Network[] underlying = underlyingNetworksOrDefault(nai.declaredUnderlyingNetworks);
+ return ArrayUtils.contains(underlying, network);
+ }
+
/**
- * Ask all networks with underlying networks to recompute and update their capabilities.
+ * Recompute the capabilities for any networks that had a specific network as underlying.
*
* When underlying networks change, such networks may have to update capabilities to reflect
* things like the metered bit, their transports, and so on. The capabilities are calculated
* immediately. This method runs on the ConnectivityService thread.
*/
- private void propagateUnderlyingNetworkCapabilities() {
+ private void propagateUnderlyingNetworkCapabilities(Network updatedNetwork) {
ensureRunningOnConnectivityServiceThread();
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- if (nai.supportsUnderlyingNetworks()) {
+ if (updatedNetwork == null || hasUnderlyingNetwork(nai, updatedNetwork)) {
updateCapabilitiesForNetwork(nai);
}
}
@@ -4837,7 +4912,7 @@
@Override
public boolean updateLockdownVpn() {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ if (mDeps.getCallingUid() != Process.SYSTEM_UID) {
logw("Lockdown VPN only available to AID_SYSTEM");
return false;
}
@@ -4859,7 +4934,7 @@
setLockdownTracker(null);
return true;
}
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
Vpn vpn = mVpns.get(user);
if (vpn == null) {
logw("VPN for user " + user + " not ready yet. Skipping lockdown");
@@ -5152,7 +5227,7 @@
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId, mKeyStore);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
mVpns.put(userId, userVpn);
if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
updateLockdownVpn();
@@ -5424,7 +5499,7 @@
messenger = null;
mBinder = null;
mPid = getCallingPid();
- mUid = getCallingUid();
+ mUid = mDeps.getCallingUid();
enforceRequestCountLimit();
}
@@ -5436,7 +5511,7 @@
ensureAllNetworkRequestsHaveType(mRequests);
mBinder = binder;
mPid = getCallingPid();
- mUid = getCallingUid();
+ mUid = mDeps.getCallingUid();
mPendingIntent = null;
enforceRequestCountLimit();
@@ -5451,6 +5526,10 @@
this(r, null);
}
+ boolean isMultilayerRequest() {
+ return mRequests.size() > 1;
+ }
+
private List<NetworkRequest> initializeRequests(NetworkRequest r) {
final ArrayList<NetworkRequest> tempRequests = new ArrayList<>();
tempRequests.add(new NetworkRequest(r));
@@ -5492,7 +5571,7 @@
public void binderDied() {
log("ConnectivityService NetworkRequestInfo binderDied(" +
mRequests + ", " + mBinder + ")");
- releaseNetworkRequest(mRequests);
+ releaseNetworkRequests(mRequests);
}
@Override
@@ -5525,13 +5604,15 @@
mAppOpsManager.checkPackage(callerUid, callerPackageName);
}
- private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
+ private ArrayList<Integer> getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
final SortedSet<Integer> thresholds = new TreeSet<>();
synchronized (nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
- thresholds.add(nri.request.networkCapabilities.getSignalStrength());
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ for (final NetworkRequest req : nri.mRequests) {
+ if (req.networkCapabilities.hasSignalStrength()
+ && nai.satisfiesImmutableCapabilitiesOf(req)) {
+ thresholds.add(req.networkCapabilities.getSignalStrength());
+ }
}
}
}
@@ -5579,7 +5660,7 @@
}
private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) {
- final UserHandle user = UserHandle.getUserHandleForUid(Binder.getCallingUid());
+ final UserHandle user = UserHandle.getUserHandleForUid(mDeps.getCallingUid());
final PackageManager pm =
mContext.createContextAsUser(user, 0 /* flags */).getPackageManager();
try {
@@ -5599,7 +5680,7 @@
throw new SecurityException("Insufficient permissions to specify legacy type");
}
}
- final int callingUid = Binder.getCallingUid();
+ final int callingUid = mDeps.getCallingUid();
final NetworkRequest.Type type = (networkCapabilities == null)
? NetworkRequest.Type.TRACK_DEFAULT
: NetworkRequest.Type.REQUEST;
@@ -5669,7 +5750,7 @@
if (nai != null) {
nai.asyncChannel.sendMessage(android.net.NetworkAgent.CMD_REQUEST_BANDWIDTH_UPDATE);
synchronized (mBandwidthRequests) {
- final int uid = Binder.getCallingUid();
+ final int uid = mDeps.getCallingUid();
Integer uidReqs = mBandwidthRequests.get(uid);
if (uidReqs == null) {
uidReqs = 0;
@@ -5686,7 +5767,7 @@
}
private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) {
- final int uid = Binder.getCallingUid();
+ final int uid = mDeps.getCallingUid();
if (isSystem(uid)) {
// Exemption for system uid.
return;
@@ -5706,7 +5787,7 @@
PendingIntent operation, @NonNull String callingPackageName,
@Nullable String callingAttributionTag) {
Objects.requireNonNull(operation, "PendingIntent cannot be null.");
- final int callingUid = Binder.getCallingUid();
+ final int callingUid = mDeps.getCallingUid();
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
callingAttributionTag);
@@ -5765,7 +5846,7 @@
@Override
public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
- final int callingUid = Binder.getCallingUid();
+ final int callingUid = mDeps.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
}
@@ -5795,7 +5876,7 @@
public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
PendingIntent operation, @NonNull String callingPackageName) {
Objects.requireNonNull(operation, "PendingIntent cannot be null.");
- final int callingUid = Binder.getCallingUid();
+ final int callingUid = mDeps.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
}
@@ -5818,7 +5899,7 @@
return mNextNetworkProviderId.getAndIncrement();
}
- private void releaseNetworkRequest(List<NetworkRequest> networkRequests) {
+ private void releaseNetworkRequests(List<NetworkRequest> networkRequests) {
for (int i = 0; i < networkRequests.size(); i++) {
releaseNetworkRequest(networkRequests.get(i));
}
@@ -5896,7 +5977,7 @@
} else {
enforceNetworkFactoryPermission();
}
- mHandler.post(() -> handleReleaseNetworkRequest(request, Binder.getCallingUid(), true));
+ mHandler.post(() -> handleReleaseNetworkRequest(request, mDeps.getCallingUid(), true));
}
// NOTE: Accessed on multiple threads, must be synchronized on itself.
@@ -5990,7 +6071,7 @@
enforceNetworkFactoryPermission();
}
- final int uid = Binder.getCallingUid();
+ final int uid = mDeps.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
return registerNetworkAgentInternal(messenger, networkInfo, linkProperties,
@@ -6368,27 +6449,28 @@
* This method should never alter the agent's NetworkCapabilities, only store data in |nai|.
*/
private void processCapabilitiesFromAgent(NetworkAgentInfo nai, NetworkCapabilities nc) {
- nai.declaredMetered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+ // Note: resetting the owner UID before storing the agent capabilities in NAI means that if
+ // the agent attempts to change the owner UID, then nai.declaredCapabilities will not
+ // actually be the same as the capabilities sent by the agent. Still, it is safer to reset
+ // the owner UID here and behave as if the agent had never tried to change it.
if (nai.networkCapabilities.getOwnerUid() != nc.getOwnerUid()) {
Log.e(TAG, nai.toShortString() + ": ignoring attempt to change owner from "
+ nai.networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
nc.setOwnerUid(nai.networkCapabilities.getOwnerUid());
}
+ nai.declaredCapabilities = new NetworkCapabilities(nc);
}
- /** Modifies |caps| based on the capabilities of the specified underlying networks. */
+ /** Modifies |newNc| based on the capabilities of |underlyingNetworks| and |agentCaps|. */
@VisibleForTesting
void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
- @NonNull NetworkCapabilities caps, boolean declaredMetered) {
- final Network defaultNetwork = getNetwork(getDefaultNetwork());
- if (underlyingNetworks == null && defaultNetwork != null) {
- // null underlying networks means to track the default.
- underlyingNetworks = new Network[] { defaultNetwork };
- }
- int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
+ @NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
+ underlyingNetworks = underlyingNetworksOrDefault(underlyingNetworks);
+ int[] transportTypes = agentCaps.getTransportTypes();
int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
- boolean metered = declaredMetered; // metered if any underlying is metered, or agentMetered
+ // metered if any underlying is metered, or originally declared metered by the agent.
+ boolean metered = !agentCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
boolean roaming = false; // roaming if any underlying is roaming
boolean congested = false; // congested if any underlying is congested
boolean suspended = true; // suspended if all underlying are suspended
@@ -6434,13 +6516,13 @@
suspended = false;
}
- caps.setTransportTypes(transportTypes);
- caps.setLinkDownstreamBandwidthKbps(downKbps);
- caps.setLinkUpstreamBandwidthKbps(upKbps);
- caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
- caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
- caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
- caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
+ newNc.setTransportTypes(transportTypes);
+ newNc.setLinkDownstreamBandwidthKbps(downKbps);
+ newNc.setLinkUpstreamBandwidthKbps(upKbps);
+ newNc.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
+ newNc.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
+ newNc.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
+ newNc.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
}
/**
@@ -6497,7 +6579,8 @@
}
if (nai.supportsUnderlyingNetworks()) {
- applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, newNc, nai.declaredMetered);
+ applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, nai.declaredCapabilities,
+ newNc);
}
return newNc;
@@ -6576,11 +6659,8 @@
}
}
- if (!newNc.hasTransport(TRANSPORT_VPN)) {
- // Tell VPNs about updated capabilities, since they may need to
- // bubble those changes through.
- propagateUnderlyingNetworkCapabilities();
- }
+ // This network might have been underlying another network. Propagate its capabilities.
+ propagateUnderlyingNetworkCapabilities(nai.network);
if (!newNc.equalsTransportTypes(prevNc)) {
mDnsManager.updateTransportsForNetwork(
@@ -6622,6 +6702,16 @@
&& (lp.hasIpv6DefaultRoute() || lp.hasIpv6UnreachableDefaultRoute());
}
+ private static UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set<UidRange> ranges) {
+ final UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.size()];
+ int index = 0;
+ for (UidRange range : ranges) {
+ stableRanges[index] = new UidRangeParcel(range.start, range.stop);
+ index++;
+ }
+ return stableRanges;
+ }
+
private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
NetworkCapabilities newNc) {
Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUids();
@@ -6641,14 +6731,11 @@
// removing old range works because, unlike the filtering rules below, it's possible to
// add duplicate UID routing rules.
if (!newRanges.isEmpty()) {
- final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
- newRanges.toArray(addedRangesArray);
- mNMS.addVpnUidRanges(nai.network.getNetId(), addedRangesArray);
+ mNetd.networkAddUidRanges(nai.network.netId, toUidRangeStableParcels(newRanges));
}
if (!prevRanges.isEmpty()) {
- final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
- prevRanges.toArray(removedRangesArray);
- mNMS.removeVpnUidRanges(nai.network.getNetId(), removedRangesArray);
+ mNetd.networkRemoveUidRanges(
+ nai.network.netId, toUidRangeStableParcels(prevRanges));
}
final boolean wasFiltering = requiresVpnIsolation(nai, prevNc, nai.linkProperties);
final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties);
@@ -6903,8 +6990,10 @@
updateTcpBufferSizes(null != newNetwork
? newNetwork.linkProperties.getTcpBufferSizes() : null);
notifyIfacesChangedForNetworkStats();
- // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
- propagateUnderlyingNetworkCapabilities();
+ // Fix up the NetworkCapabilities of any networks that have this network as underlying.
+ if (newNetwork != null) {
+ propagateUnderlyingNetworkCapabilities(newNetwork.network);
+ }
}
private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
@@ -7360,13 +7449,11 @@
networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
if (!createNativeNetwork(networkAgent)) return;
- if (networkAgent.isVPN()) {
- // Initialize the VPN capabilities to their starting values according to the
- // underlying networks. This will avoid a spurious callback to
- // onCapabilitiesUpdated being sent in updateAllVpnCapabilities below as
- // the VPN would switch from its default, blank capabilities to those
- // that reflect the capabilities of its underlying networks.
- propagateUnderlyingNetworkCapabilities();
+ if (networkAgent.supportsUnderlyingNetworks()) {
+ // Initialize the network's capabilities to their starting values according to the
+ // underlying networks. This ensures that the capabilities are correct before
+ // anything happens to the network.
+ updateCapabilitiesForNetwork(networkAgent);
}
networkAgent.created = true;
}
@@ -7408,10 +7495,6 @@
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
- if (networkAgent.supportsUnderlyingNetworks()) {
- propagateUnderlyingNetworkCapabilities();
- }
-
// Consider network even though it is not yet validated.
rematchAllNetworksAndRequests();
@@ -7642,7 +7725,7 @@
@Override
public boolean addVpnAddress(String address, int prefixLength) {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
throwIfLockdownEnabled();
return mVpns.get(user).addAddress(address, prefixLength);
@@ -7651,7 +7734,7 @@
@Override
public boolean removeVpnAddress(String address, int prefixLength) {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
throwIfLockdownEnabled();
return mVpns.get(user).removeAddress(address, prefixLength);
@@ -7660,7 +7743,7 @@
@Override
public boolean setUnderlyingNetworksForVpn(Network[] networks) {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
final boolean success;
synchronized (mVpns) {
throwIfLockdownEnabled();
@@ -7748,10 +7831,13 @@
final int userId = UserHandle.getCallingUserId();
- Binder.withCleanCallingIdentity(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext);
ipMemoryStore.factoryReset();
- });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
// Turn airplane mode off
setAirplaneMode(false);
@@ -7887,7 +7973,7 @@
@GuardedBy("mVpns")
private Vpn getVpnIfOwner() {
- return getVpnIfOwner(Binder.getCallingUid());
+ return getVpnIfOwner(mDeps.getCallingUid());
}
@GuardedBy("mVpns")
@@ -8365,7 +8451,7 @@
throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
+ " Please use NetworkCapabilities instead.");
}
- final int callingUid = Binder.getCallingUid();
+ final int callingUid = mDeps.getCallingUid();
mAppOpsManager.checkPackage(callingUid, callingPackageName);
// This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
@@ -8400,7 +8486,7 @@
mConnectivityDiagnosticsHandler.obtainMessage(
ConnectivityDiagnosticsHandler
.EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK,
- Binder.getCallingUid(),
+ mDeps.getCallingUid(),
0,
callback));
}
@@ -8416,7 +8502,7 @@
}
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- if (nai == null || nai.creatorUid != Binder.getCallingUid()) {
+ if (nai == null || nai.creatorUid != mDeps.getCallingUid()) {
throw new SecurityException("Data Stall simulation is only possible for network "
+ "creators");
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 5e86f85..636da6f 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -60,7 +60,6 @@
import android.net.NetworkStats;
import android.net.RouteInfo;
import android.net.TetherStatsParcel;
-import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.shared.NetdUtils;
import android.net.shared.RouteUtils;
@@ -91,7 +90,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.HexDump;
import com.android.internal.util.Preconditions;
@@ -420,8 +418,6 @@
getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid);
} catch (RemoteException e) {
}
- FrameworkStatsLog.write_non_chained(
- FrameworkStatsLog.MOBILE_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
}
}
@@ -432,8 +428,6 @@
getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid);
} catch (RemoteException e) {
}
- FrameworkStatsLog.write_non_chained(
- FrameworkStatsLog.WIFI_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
}
}
@@ -1393,38 +1387,6 @@
}
}
- private static UidRangeParcel makeUidRangeParcel(int start, int stop) {
- UidRangeParcel range = new UidRangeParcel();
- range.start = start;
- range.stop = stop;
- return range;
- }
-
- private static UidRangeParcel[] toStableParcels(UidRange[] ranges) {
- UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.length];
- for (int i = 0; i < ranges.length; i++) {
- stableRanges[i] = makeUidRangeParcel(ranges[i].start, ranges[i].stop);
- }
- return stableRanges;
- }
-
- @Override
- public void setAllowOnlyVpnForUids(boolean add, UidRange[] uidRanges)
- throws ServiceSpecificException {
- NetworkStack.checkNetworkStackPermission(mContext);
- try {
- mNetdService.networkRejectNonSecureVpn(add, toStableParcels(uidRanges));
- } catch (ServiceSpecificException e) {
- Log.w(TAG, "setAllowOnlyVpnForUids(" + add + ", " + Arrays.toString(uidRanges) + ")"
- + ": netd command failed", e);
- throw e;
- } catch (RemoteException e) {
- Log.w(TAG, "setAllowOnlyVpnForUids(" + add + ", " + Arrays.toString(uidRanges) + ")"
- + ": netd command failed", e);
- throw e.rethrowAsRuntimeException();
- }
- }
-
private void applyUidCleartextNetworkPolicy(int uid, int policy) {
final int policyValue;
switch (policy) {
@@ -1553,27 +1515,6 @@
}
@Override
- public void addVpnUidRanges(int netId, UidRange[] ranges) {
- NetworkStack.checkNetworkStackPermission(mContext);
-
- try {
- mNetdService.networkAddUidRanges(netId, toStableParcels(ranges));
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
- public void removeVpnUidRanges(int netId, UidRange[] ranges) {
- NetworkStack.checkNetworkStackPermission(mContext);
- try {
- mNetdService.networkRemoveUidRanges(netId, toStableParcels(ranges));
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
public void setFirewallEnabled(boolean enabled) {
enforceSystemUid();
try {
@@ -1616,7 +1557,7 @@
ranges = new UidRangeParcel[] {
// TODO: is there a better way of finding all existing users? If so, we could
// specify their ranges here.
- makeUidRangeParcel(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE),
+ new UidRangeParcel(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE),
};
// ... except for the UIDs that have allow rules.
synchronized (mRulesLock) {
@@ -1647,7 +1588,7 @@
for (int i = 0; i < ranges.length; i++) {
if (rules.valueAt(i) == FIREWALL_RULE_DENY) {
int uid = rules.keyAt(i);
- ranges[numUids] = makeUidRangeParcel(uid, uid);
+ ranges[numUids] = new UidRangeParcel(uid, uid);
numUids++;
}
}
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index 4a1820a..d907505 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -25,7 +25,6 @@
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
-import android.net.util.nsd.DnsSdTxtRecord;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
@@ -42,6 +41,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.net.module.util.DnsSdTxtRecord;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index ff0596c..dd497a6 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4612,14 +4612,7 @@
// Create package obb and data dir if it doesn't exist.
int appUid = UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
- File file = new File(packageObbDir);
- if (!file.exists()) {
- vold.setupAppDir(packageObbDir, appUid);
- }
- file = new File(packageDataDir);
- if (!file.exists()) {
- vold.setupAppDir(packageDataDir, appUid);
- }
+ vold.ensureAppDirsCreated(new String[] {packageObbDir, packageDataDir}, appUid);
}
} catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
Slog.e(TAG, "Unable to create obb and data directories for " + processName,e);
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 655d8ab..e8687e5 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -107,23 +107,23 @@
String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
String iface = ifacePrefix + sTestTunIndex.getAndIncrement();
- return Binder.withCleanCallingIdentity(
- () -> {
- try {
- ParcelFileDescriptor tunIntf =
- ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface));
- for (LinkAddress addr : linkAddrs) {
- mNetd.interfaceAddAddress(
- iface,
- addr.getAddress().getHostAddress(),
- addr.getPrefixLength());
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ ParcelFileDescriptor tunIntf =
+ ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface));
+ for (LinkAddress addr : linkAddrs) {
+ mNetd.interfaceAddAddress(
+ iface,
+ addr.getAddress().getHostAddress(),
+ addr.getPrefixLength());
+ }
- return new TestNetworkInterface(tunIntf, iface);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- });
+ return new TestNetworkInterface(tunIntf, iface);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -317,7 +317,12 @@
try {
// This requires NETWORK_STACK privileges.
- Binder.withCleanCallingIdentity(() -> mNMS.setInterfaceUp(iface));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNMS.setInterfaceUp(iface);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
// Synchronize all accesses to mTestNetworkTracker to prevent the case where:
// 1. TestNetworkAgent successfully binds to death of binder
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 165b6a1..74e3851 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -25,24 +25,44 @@
import android.net.NetworkRequest;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.VcnConfig;
+import android.os.Binder;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
/**
* VcnManagementService manages Virtual Carrier Network profiles and lifecycles.
*
* <pre>The internal structure of the VCN Management subsystem is as follows:
*
- * +------------------------+ 1:1 +--------------------------------+
- * | VcnManagementService | ------------ Creates -------------> | TelephonySubscriptionManager |
- * | | | |
- * | Manages configs and | | Tracks subscriptions, carrier |
- * | VcnInstance lifecycles | <--- Notifies of subscription & --- | privilege changes, caches maps |
- * +------------------------+ carrier privilege changes +--------------------------------+
+ * +-------------------------+ 1:1 +--------------------------------+
+ * | VcnManagementService | ------------ Creates ------------> | TelephonySubscriptionManager |
+ * | | | |
+ * | Manages configs and | | Tracks subscriptions, carrier |
+ * | Vcn instance lifecycles | <--- Notifies of subscription & -- | privilege changes, caches maps |
+ * +-------------------------+ carrier privilege changes +--------------------------------+
* | 1:N ^
* | |
* | +-------------------------------+
@@ -54,19 +74,19 @@
* | mode state changes
* v |
* +-----------------------------------------------------------------------+
- * | VcnInstance |
+ * | Vcn |
* | |
- * | Manages tunnel lifecycles based on fulfillable NetworkRequest(s) |
- * | and overall safe-mode |
+ * | Manages GatewayConnection lifecycles based on fulfillable |
+ * | NetworkRequest(s) and overall safe-mode |
* +-----------------------------------------------------------------------+
* | 1:N ^
* Creates to fulfill |
- * NetworkRequest(s), tears Notifies of VcnTunnel
+ * NetworkRequest(s), tears Notifies of VcnGatewayConnection
* down when no longer needed teardown (e.g. Network reaped)
* | and safe-mode timer changes
* v |
* +-----------------------------------------------------------------------+
- * | VcnTunnel |
+ * | VcnGatewayConnection |
* | |
* | Manages a single (IKEv2) tunnel session and NetworkAgent, |
* | handles mobility events, (IPsec) Tunnel setup and safe-mode timers |
@@ -92,20 +112,72 @@
public static final boolean VDBG = false; // STOPSHIP: if true
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";
+
/* Binder context for this service */
@NonNull private final Context mContext;
@NonNull private final Dependencies mDeps;
@NonNull private final Looper mLooper;
+ @NonNull private final Handler mHandler;
@NonNull private final VcnNetworkProvider mNetworkProvider;
+ @GuardedBy("mLock")
+ @NonNull
+ private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>();
+
+ @NonNull private final Object mLock = new Object();
+
+ @NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper;
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
mContext = requireNonNull(context, "Missing context");
mDeps = requireNonNull(deps, "Missing dependencies");
mLooper = mDeps.getLooper();
+ mHandler = new Handler(mLooper);
mNetworkProvider = new VcnNetworkProvider(mContext, mLooper);
+
+ mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
+
+ // Run on handler to ensure I/O does not block system server startup
+ mHandler.post(() -> {
+ PersistableBundle configBundle = null;
+ try {
+ configBundle = mConfigDiskRwHelper.readFromDisk();
+ } catch (IOException e1) {
+ Slog.e(TAG, "Failed to read configs from disk; retrying", e1);
+
+ // Retry immediately. The IOException may have been transient.
+ try {
+ configBundle = mConfigDiskRwHelper.readFromDisk();
+ } catch (IOException e2) {
+ Slog.wtf(TAG, "Failed to read configs from disk", e2);
+ return;
+ }
+ }
+
+ if (configBundle != null) {
+ final Map<ParcelUuid, VcnConfig> configs =
+ PersistableBundleUtils.toMap(
+ configBundle,
+ PersistableBundleUtils::toParcelUuid,
+ VcnConfig::new);
+
+ synchronized (mLock) {
+ for (Entry<ParcelUuid, VcnConfig> entry : configs.entrySet()) {
+ // Ensure no new configs are overwritten; a carrier app may have added a new
+ // config.
+ if (!mConfigs.containsKey(entry.getKey())) {
+ mConfigs.put(entry.getKey(), entry.getValue());
+ }
+ }
+ // TODO: Trigger re-evaluation of active VCNs; start/stop VCNs as needed.
+ }
+ }
+ });
}
// Package-visibility for SystemServer to create instances.
@@ -130,16 +202,81 @@
}
return mHandlerThread.getLooper();
}
+
+ /**
+ * Retrieves the caller's UID
+ *
+ * <p>This call MUST be made before calling {@link Binder#clearCallingIdentity}, otherwise
+ * this will not work properly.
+ *
+ * @return
+ */
+ public int getBinderCallingUid() {
+ return Binder.getCallingUid();
+ }
+
+ /**
+ * Creates and returns a new {@link PersistableBundle.LockingReadWriteHelper}
+ *
+ * @param path the file path to read/write from/to.
+ * @return the {@link PersistableBundleUtils.LockingReadWriteHelper} instance
+ */
+ public PersistableBundleUtils.LockingReadWriteHelper
+ newPersistableBundleLockingReadWriteHelper(@NonNull String path) {
+ return new PersistableBundleUtils.LockingReadWriteHelper(path);
+ }
}
/** Notifies the VcnManagementService that external dependencies can be set up. */
public void systemReady() {
- // TODO: Retrieve existing profiles from KeyStore
-
mContext.getSystemService(ConnectivityManager.class)
.registerNetworkProvider(mNetworkProvider);
}
+ private void enforcePrimaryUser() {
+ final int uid = mDeps.getBinderCallingUid();
+ if (uid == Process.SYSTEM_UID) {
+ throw new IllegalStateException(
+ "Calling identity was System Server. Was Binder calling identity cleared?");
+ }
+
+ if (!UserHandle.getUserHandleForUid(uid).isSystem()) {
+ throw new SecurityException(
+ "VcnManagementService can only be used by callers running as the primary user");
+ }
+ }
+
+ private void enforceCallingUserAndCarrierPrivilege(ParcelUuid subscriptionGroup) {
+ // Only apps running in the primary (system) user are allowed to configure the VCN. This is
+ // in line with Telephony's behavior with regards to binding to a Carrier App provided
+ // CarrierConfigService.
+ enforcePrimaryUser();
+
+ // TODO (b/172619301): Check based on events propagated from CarrierPrivilegesTracker
+ final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class);
+ final List<SubscriptionInfo> subscriptionInfos = new ArrayList<>();
+ Binder.withCleanCallingIdentity(
+ () -> {
+ subscriptionInfos.addAll(subMgr.getSubscriptionsInGroup(subscriptionGroup));
+ });
+
+ final TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class);
+ for (SubscriptionInfo info : subscriptionInfos) {
+ // Check subscription is active first; much cheaper/faster check, and an app (currently)
+ // cannot be carrier privileged for inactive subscriptions.
+ if (subMgr.isValidSlotIndex(info.getSimSlotIndex())
+ && telMgr.hasCarrierPrivileges(info.getSubscriptionId())) {
+ // TODO (b/173717728): Allow configuration for inactive, but manageable
+ // subscriptions.
+ // TODO (b/173718661): Check for whole subscription groups at a time.
+ return;
+ }
+ }
+
+ throw new SecurityException(
+ "Carrier privilege required for subscription group to set VCN Config");
+ }
+
/**
* Sets a VCN config for a given subscription group.
*
@@ -150,7 +287,17 @@
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
requireNonNull(config, "config was null");
- // TODO: Store VCN configuration, trigger startup as necessary
+ enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
+
+ synchronized (mLock) {
+ mConfigs.put(subscriptionGroup, config);
+
+ // Must be done synchronously to ensure that writes do not happen out-of-order.
+ writeConfigsToDiskLocked();
+ }
+
+ // TODO: Clear Binder calling identity
+ // TODO: Trigger startup as necessary
}
/**
@@ -162,7 +309,40 @@
public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
- // TODO: Clear VCN configuration, trigger teardown as necessary
+ enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
+
+ synchronized (mLock) {
+ mConfigs.remove(subscriptionGroup);
+
+ // Must be done synchronously to ensure that writes do not happen out-of-order.
+ writeConfigsToDiskLocked();
+ }
+
+ // TODO: Clear Binder calling identity
+ // TODO: Trigger teardown as necessary
+ }
+
+ @GuardedBy("mLock")
+ private void writeConfigsToDiskLocked() {
+ try {
+ PersistableBundle bundle =
+ PersistableBundleUtils.fromMap(
+ mConfigs,
+ PersistableBundleUtils::fromParcelUuid,
+ VcnConfig::toPersistableBundle);
+ mConfigDiskRwHelper.writeToDisk(bundle);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to save configs to disk", e);
+ throw new ServiceSpecificException(0, "Failed to save configs");
+ }
+ }
+
+ /** Get current configuration list for testing purposes */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ Map<ParcelUuid, VcnConfig> getConfigs() {
+ synchronized (mLock) {
+ return Collections.unmodifiableMap(mConfigs);
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 090ac54..1ade8e7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -688,6 +688,8 @@
if (update) {
mWorker.scheduleSync("modem-data", BatteryExternalStatsWorker.UPDATE_RADIO);
}
+ FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.MOBILE_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
}
public void notePhoneOn() {
@@ -869,6 +871,8 @@
}
mStats.noteWifiRadioPowerState(powerState, tsNanos, uid);
}
+ FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.WIFI_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
}
public void noteWifiRunning(WorkSource ws) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 074d3fe..5447605 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -28,7 +28,7 @@
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics;
import android.os.Binder;
import android.os.Handler;
@@ -47,6 +47,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -526,23 +527,23 @@
}
}
- /*package*/ int setPreferredDeviceForStrategySync(int strategy,
- @NonNull AudioDeviceAttributes device) {
- return mDeviceInventory.setPreferredDeviceForStrategySync(strategy, device);
+ /*package*/ int setPreferredDevicesForStrategySync(int strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return mDeviceInventory.setPreferredDevicesForStrategySync(strategy, devices);
}
- /*package*/ int removePreferredDeviceForStrategySync(int strategy) {
- return mDeviceInventory.removePreferredDeviceForStrategySync(strategy);
+ /*package*/ int removePreferredDevicesForStrategySync(int strategy) {
+ return mDeviceInventory.removePreferredDevicesForStrategySync(strategy);
}
- /*package*/ void registerStrategyPreferredDeviceDispatcher(
- @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
- mDeviceInventory.registerStrategyPreferredDeviceDispatcher(dispatcher);
+ /*package*/ void registerStrategyPreferredDevicesDispatcher(
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+ mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
}
- /*package*/ void unregisterStrategyPreferredDeviceDispatcher(
- @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
- mDeviceInventory.unregisterStrategyPreferredDeviceDispatcher(dispatcher);
+ /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+ mDeviceInventory.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
}
//---------------------------------------------------------------------
@@ -683,14 +684,14 @@
sendLMsgNoDelay(MSG_L_SPEAKERPHONE_CLIENT_DIED, SENDMSG_QUEUE, obj);
}
- /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy,
- AudioDeviceAttributes device)
+ /*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy,
+ List<AudioDeviceAttributes> devices)
{
- sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy, device);
+ sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy, devices);
}
- /*package*/ void postSaveRemovePreferredDeviceForStrategy(int strategy) {
- sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
+ /*package*/ void postSaveRemovePreferredDevicesForStrategy(int strategy) {
+ sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
}
//---------------------------------------------------------------------
@@ -1084,14 +1085,15 @@
info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
}
} break;
- case MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY: {
+ case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: {
final int strategy = msg.arg1;
- final AudioDeviceAttributes device = (AudioDeviceAttributes) msg.obj;
- mDeviceInventory.onSaveSetPreferredDevice(strategy, device);
+ final List<AudioDeviceAttributes> devices =
+ (List<AudioDeviceAttributes>) msg.obj;
+ mDeviceInventory.onSaveSetPreferredDevices(strategy, devices);
} break;
- case MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY: {
+ case MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY: {
final int strategy = msg.arg1;
- mDeviceInventory.onSaveRemovePreferredDevice(strategy);
+ mDeviceInventory.onSaveRemovePreferredDevices(strategy);
} break;
case MSG_CHECK_MUTE_MUSIC:
checkMessagesMuteMusic(0);
@@ -1165,8 +1167,8 @@
// a ScoClient died in BtHelper
private static final int MSG_L_SCOCLIENT_DIED = 32;
- private static final int MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY = 33;
- private static final int MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY = 34;
+ private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 33;
+ private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 34;
private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35;
private static final int MSG_CHECK_MUTE_MUSIC = 36;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 02a846e..fbf07cc 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -16,7 +16,6 @@
package com.android.server.audio;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
@@ -32,7 +31,7 @@
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics;
import android.os.Binder;
import android.os.RemoteCallbackList;
@@ -51,6 +50,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Set;
/**
@@ -137,7 +137,8 @@
private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>();
// List of preferred devices for strategies
- private final ArrayMap<Integer, AudioDeviceAttributes> mPreferredDevices = new ArrayMap<>();
+ private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices =
+ new ArrayMap<>();
// the wrapper for AudioSystem static methods, allows us to spy AudioSystem
private final @NonNull AudioSystemAdapter mAudioSystem;
@@ -150,8 +151,8 @@
new RemoteCallbackList<IAudioRoutesObserver>();
// Monitoring of strategy-preferred device
- final RemoteCallbackList<IStrategyPreferredDeviceDispatcher> mPrefDevDispatchers =
- new RemoteCallbackList<IStrategyPreferredDeviceDispatcher>();
+ final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers =
+ new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>();
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
mDeviceBroker = broker;
@@ -265,8 +266,9 @@
}
}
synchronized (mPreferredDevices) {
- mPreferredDevices.forEach((strategy, device) -> {
- mAudioSystem.setPreferredDeviceForStrategy(strategy, device); });
+ mPreferredDevices.forEach((strategy, devices) -> {
+ mAudioSystem.setDevicesRoleForStrategy(
+ strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); });
}
}
@@ -600,49 +602,52 @@
mmi.record();
}
- /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAttributes device) {
- mPreferredDevices.put(strategy, device);
- dispatchPreferredDevice(strategy, device);
+ /*package*/ void onSaveSetPreferredDevices(int strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ mPreferredDevices.put(strategy, devices);
+ dispatchPreferredDevice(strategy, devices);
}
- /*package*/ void onSaveRemovePreferredDevice(int strategy) {
+ /*package*/ void onSaveRemovePreferredDevices(int strategy) {
mPreferredDevices.remove(strategy);
- dispatchPreferredDevice(strategy, null);
+ dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>());
}
//------------------------------------------------------------
//
- /*package*/ int setPreferredDeviceForStrategySync(int strategy,
- @NonNull AudioDeviceAttributes device) {
+ /*package*/ int setPreferredDevicesForStrategySync(int strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
final long identity = Binder.clearCallingIdentity();
- final int status = mAudioSystem.setPreferredDeviceForStrategy(strategy, device);
+ final int status = mAudioSystem.setDevicesRoleForStrategy(
+ strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.SUCCESS) {
- mDeviceBroker.postSaveSetPreferredDeviceForStrategy(strategy, device);
+ mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices);
}
return status;
}
- /*package*/ int removePreferredDeviceForStrategySync(int strategy) {
+ /*package*/ int removePreferredDevicesForStrategySync(int strategy) {
final long identity = Binder.clearCallingIdentity();
- final int status = mAudioSystem.removePreferredDeviceForStrategy(strategy);
+ final int status = mAudioSystem.removeDevicesRoleForStrategy(
+ strategy, AudioSystem.DEVICE_ROLE_PREFERRED);
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.SUCCESS) {
- mDeviceBroker.postSaveRemovePreferredDeviceForStrategy(strategy);
+ mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy);
}
return status;
}
- /*package*/ void registerStrategyPreferredDeviceDispatcher(
- @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+ /*package*/ void registerStrategyPreferredDevicesDispatcher(
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
mPrefDevDispatchers.register(dispatcher);
}
- /*package*/ void unregisterStrategyPreferredDeviceDispatcher(
- @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+ /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
mPrefDevDispatchers.unregister(dispatcher);
}
@@ -1288,11 +1293,13 @@
}
}
- private void dispatchPreferredDevice(int strategy, @Nullable AudioDeviceAttributes device) {
+ private void dispatchPreferredDevice(int strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
final int nbDispatchers = mPrefDevDispatchers.beginBroadcast();
for (int i = 0; i < nbDispatchers; i++) {
try {
- mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDeviceChanged(strategy, device);
+ mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
+ strategy, devices);
} catch (RemoteException e) {
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a06f9dd..dda57d0 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -87,7 +87,7 @@
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.IVolumeController;
import android.media.MediaExtractor;
import android.media.MediaFormat;
@@ -171,6 +171,7 @@
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
/**
* The implementation of the audio service for volume, audio focus, device management...
@@ -1850,22 +1851,28 @@
///////////////////////////////////////////////////////////////////////////
// IPC methods
///////////////////////////////////////////////////////////////////////////
- /** @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceInfo) */
- public int setPreferredDeviceForStrategy(int strategy, AudioDeviceAttributes device) {
- if (device == null) {
+ /**
+ * @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
+ * @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy,
+ * List<AudioDeviceAttributes>)
+ */
+ public int setPreferredDevicesForStrategy(int strategy, List<AudioDeviceAttributes> devices) {
+ if (devices == null) {
return AudioSystem.ERROR;
}
enforceModifyAudioRoutingPermission();
final String logString = String.format(
"setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
- Binder.getCallingUid(), Binder.getCallingPid(), strategy, device.toString());
+ Binder.getCallingUid(), Binder.getCallingPid(), strategy,
+ devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
- if (device.getRole() == AudioDeviceAttributes.ROLE_INPUT) {
+ if (devices.stream().anyMatch(device ->
+ device.getRole() == AudioDeviceAttributes.ROLE_INPUT)) {
Log.e(TAG, "Unsupported input routing in " + logString);
return AudioSystem.ERROR;
}
- final int status = mDeviceBroker.setPreferredDeviceForStrategySync(strategy, device);
+ final int status = mDeviceBroker.setPreferredDevicesForStrategySync(strategy, devices);
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in %s)", status, logString));
}
@@ -1874,53 +1881,61 @@
}
/** @see AudioManager#removePreferredDeviceForStrategy(AudioProductStrategy) */
- public int removePreferredDeviceForStrategy(int strategy) {
+ public int removePreferredDevicesForStrategy(int strategy) {
enforceModifyAudioRoutingPermission();
final String logString =
String.format("removePreferredDeviceForStrategy strat:%d", strategy);
sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
- final int status = mDeviceBroker.removePreferredDeviceForStrategySync(strategy);
+ final int status = mDeviceBroker.removePreferredDevicesForStrategySync(strategy);
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in %s)", status, logString));
}
return status;
}
- /** @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy) */
- public AudioDeviceAttributes getPreferredDeviceForStrategy(int strategy) {
+ /**
+ * @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy)
+ * @see AudioManager#getPreferredDevicesForStrategy(AudioProductStrategy)
+ */
+ public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(int strategy) {
enforceModifyAudioRoutingPermission();
- AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
+ List<AudioDeviceAttributes> devices = new ArrayList<>();
final long identity = Binder.clearCallingIdentity();
- final int status = AudioSystem.getPreferredDeviceForStrategy(strategy, devices);
+ final int status = AudioSystem.getDevicesForRoleAndStrategy(
+ strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
Binder.restoreCallingIdentity(identity);
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in getPreferredDeviceForStrategy(%d)",
status, strategy));
- return null;
+ return new ArrayList<AudioDeviceAttributes>();
} else {
- return devices[0];
+ return devices;
}
}
- /** @see AudioManager#addOnPreferredDeviceForStrategyChangedListener(Executor, AudioManager.OnPreferredDeviceForStrategyChangedListener) */
- public void registerStrategyPreferredDeviceDispatcher(
- @Nullable IStrategyPreferredDeviceDispatcher dispatcher) {
+ /** @see AudioManager#addOnPreferredDevicesForStrategyChangedListener(
+ * Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)
+ */
+ public void registerStrategyPreferredDevicesDispatcher(
+ @Nullable IStrategyPreferredDevicesDispatcher dispatcher) {
if (dispatcher == null) {
return;
}
enforceModifyAudioRoutingPermission();
- mDeviceBroker.registerStrategyPreferredDeviceDispatcher(dispatcher);
+ mDeviceBroker.registerStrategyPreferredDevicesDispatcher(dispatcher);
}
- /** @see AudioManager#removeOnPreferredDeviceForStrategyChangedListener(AudioManager.OnPreferredDeviceForStrategyChangedListener) */
- public void unregisterStrategyPreferredDeviceDispatcher(
- @Nullable IStrategyPreferredDeviceDispatcher dispatcher) {
+ /** @see AudioManager#removeOnPreferredDevicesForStrategyChangedListener(
+ * AudioManager.OnPreferredDevicesForStrategyChangedListener)
+ */
+ public void unregisterStrategyPreferredDevicesDispatcher(
+ @Nullable IStrategyPreferredDevicesDispatcher dispatcher) {
if (dispatcher == null) {
return;
}
enforceModifyAudioRoutingPermission();
- mDeviceBroker.unregisterStrategyPreferredDeviceDispatcher(dispatcher);
+ mDeviceBroker.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
}
/** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index e60243f..a0e1ca7 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -20,6 +20,8 @@
import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
+import java.util.List;
+
/**
* Provides an adapter to access functionality of the android.media.AudioSystem class for device
* related functionality.
@@ -77,22 +79,25 @@
}
/**
- * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDeviceAttributes)}
+ * Same as {@link AudioSystem#setDevicesRoleForStrategy(int, int, List)}
* @param strategy
- * @param device
+ * @param role
+ * @param devices
* @return
*/
- public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAttributes device) {
- return AudioSystem.setPreferredDeviceForStrategy(strategy, device);
+ public int setDevicesRoleForStrategy(int strategy, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return AudioSystem.setDevicesRoleForStrategy(strategy, role, devices);
}
/**
- * Same as {@link AudioSystem#removePreferredDeviceForStrategy(int)}
+ * Same as {@link AudioSystem#removeDevicesRoleForStrategy(int, int)}
* @param strategy
+ * @param role
* @return
*/
- public int removePreferredDeviceForStrategy(int strategy) {
- return AudioSystem.removePreferredDeviceForStrategy(strategy);
+ public int removeDevicesRoleForStrategy(int strategy, int role) {
+ return AudioSystem.removeDevicesRoleForStrategy(strategy, role);
}
/**
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index ff31931..a8aa9aa 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -143,6 +143,9 @@
* @return {@code true} if the change should be enabled for the package.
*/
boolean isEnabled(ApplicationInfo app) {
+ if (app == null) {
+ return defaultValue();
+ }
if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) {
return mPackageOverrides.get(app.packageName);
}
@@ -156,6 +159,15 @@
}
/**
+ * Returns the default value for the change id, assuming there are no overrides.
+ *
+ * @return {@code false} if it's a default disabled change, {@code true} otherwise.
+ */
+ boolean defaultValue() {
+ return !getDisabled();
+ }
+
+ /**
* Checks whether a change has an override for a package.
* @param packageName name of the package
* @return true if there is such override
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index d80c58b..8511118 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -392,6 +392,14 @@
return alreadyKnown;
}
+ boolean defaultChangeIdValue(long changeId) {
+ CompatChange c = mChanges.get(changeId);
+ if (c == null) {
+ return true;
+ }
+ return c.defaultValue();
+ }
+
@VisibleForTesting
void clearChanges() {
synchronized (mChanges) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 2c19a2d..4bd01d4 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -107,17 +107,25 @@
}
/**
- * Internal version of the above method. Does not perform costly permission check.
+ * Internal version of the above method, without logging. Does not perform costly permission
+ * check.
+ * TODO(b/167551701): Remove this method and add 'loggability' as a changeid property.
+ */
+ public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) {
+ return mCompatConfig.isChangeEnabled(changeId, appInfo);
+ }
+
+ /**
+ * Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}. Does not perform costly
+ * permission check.
*/
public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) {
- if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
+ boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo);
+ if (appInfo != null) {
reportChange(changeId, appInfo.uid,
- ChangeReporter.STATE_ENABLED);
- return true;
+ enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED);
}
- reportChange(changeId, appInfo.uid,
- ChangeReporter.STATE_DISABLED);
- return false;
+ return enabled;
}
@Override
@@ -125,9 +133,6 @@
@UserIdInt int userId) {
checkCompatChangeReadAndLogPermission();
ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
- if (appInfo == null) {
- return true;
- }
return isChangeEnabled(changeId, appInfo);
}
@@ -136,7 +141,7 @@
checkCompatChangeReadAndLogPermission();
String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
if (packages == null || packages.length == 0) {
- return true;
+ return mCompatConfig.defaultChangeIdValue(changeId);
}
boolean enabled = true;
for (String packageName : packages) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index ccd1f3b..52b9f5c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -138,9 +138,10 @@
// not guaranteed to be current or correct, or even to exist.
public @Nullable Network[] declaredUnderlyingNetworks;
- // Whether this network is always metered even if its underlying networks are unmetered.
- // Only relevant if #supportsUnderlyingNetworks is true.
- public boolean declaredMetered;
+ // The capabilities originally announced by the NetworkAgent, regardless of any capabilities
+ // that were added or removed due to this network's underlying networks.
+ // Only set if #supportsUnderlyingNetworks is true.
+ public @Nullable NetworkCapabilities declaredCapabilities;
// Indicates if netd has been told to create this Network. From this point on the appropriate
// routing rules are setup and routes are added so packets can begin flowing over the Network.
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 7795ed3..3d71b0a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -334,7 +334,13 @@
*/
public void setProvNotificationVisible(boolean visible, int id, String action) {
if (visible) {
- Intent intent = new Intent(action);
+ // For legacy purposes, action is sent as the action + the phone ID from DcTracker.
+ // Split the string here and send the phone ID as an extra instead.
+ String[] splitAction = action.split(":");
+ Intent intent = new Intent(splitAction[0]);
+ try {
+ intent.putExtra("provision.phone.id", Integer.parseInt(splitAction[1]));
+ } catch (NumberFormatException ignored) { }
PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
showNotification(id, NotificationType.SIGN_IN, null, null, pendingIntent, false);
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
index 1129899..b5f20d7 100644
--- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
@@ -36,6 +36,7 @@
import android.net.TcpKeepalivePacketData;
import android.net.TcpKeepalivePacketDataParcelable;
import android.net.TcpRepairWindow;
+import android.net.util.KeepalivePacketDataUtil;
import android.os.Handler;
import android.os.MessageQueue;
import android.os.Messenger;
@@ -112,7 +113,7 @@
throws InvalidPacketException, InvalidSocketException {
try {
final TcpKeepalivePacketDataParcelable tcpDetails = switchToRepairMode(fd);
- return TcpKeepalivePacketData.tcpKeepalivePacket(tcpDetails);
+ return KeepalivePacketDataUtil.fromStableParcelable(tcpDetails);
} catch (InvalidPacketException | InvalidSocketException e) {
switchOutOfRepairMode(fd);
throw e;
@@ -122,7 +123,7 @@
* Switch the tcp socket to repair mode and query detail tcp information.
*
* @param fd the fd of socket on which to use keepalive offload.
- * @return a {@link TcpKeepalivePacketData#TcpKeepalivePacketDataParcelable} object for current
+ * @return a {@link TcpKeepalivePacketDataParcelable} object for current
* tcp/ip information.
*/
private static TcpKeepalivePacketDataParcelable switchToRepairMode(FileDescriptor fd)
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 66bb4d7..228ad588 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -48,6 +48,7 @@
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.net.DnsResolver;
+import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.Ikev2VpnProfile;
import android.net.IpPrefix;
@@ -68,6 +69,7 @@
import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.UidRange;
+import android.net.UidRangeParcel;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -188,7 +190,8 @@
private PendingIntent mStatusIntent;
private volatile boolean mEnableTeardown = true;
- private final INetworkManagementService mNetd;
+ private final INetworkManagementService mNms;
+ private final INetd mNetd;
@VisibleForTesting
protected VpnConfig mConfig;
private final NetworkProvider mNetworkProvider;
@@ -234,7 +237,7 @@
* @see mLockdown
*/
@GuardedBy("this")
- private final Set<UidRange> mBlockedUidsAsToldToNetd = new ArraySet<>();
+ private final Set<UidRangeParcel> mBlockedUidsAsToldToNetd = new ArraySet<>();
// The user id of initiating VPN.
private final int mUserId;
@@ -243,7 +246,12 @@
void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException;
}
- static class Dependencies {
+ @VisibleForTesting
+ public static class Dependencies {
+ public boolean isCallerSystem() {
+ return Binder.getCallingUid() == Process.SYSTEM_UID;
+ }
+
public void startService(final String serviceName) {
SystemService.start(serviceName);
}
@@ -264,6 +272,10 @@
return new File("/data/misc/vpn/state");
}
+ public DeviceIdleInternal getDeviceIdleInternal() {
+ return LocalServices.getService(DeviceIdleInternal.class);
+ }
+
public void sendArgumentsToDaemon(
final String daemon, final LocalSocket socket, final String[] arguments,
final RetryScheduler retryScheduler) throws IOException, InterruptedException {
@@ -363,22 +375,31 @@
}
}
- public Vpn(Looper looper, Context context, INetworkManagementService netService,
+ public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
@UserIdInt int userId, @NonNull KeyStore keyStore) {
- this(looper, context, new Dependencies(), netService, userId, keyStore,
+ this(looper, context, new Dependencies(), netService, netd, userId, keyStore,
+ new SystemServices(context), new Ikev2SessionCreator());
+ }
+
+ @VisibleForTesting
+ public Vpn(Looper looper, Context context, Dependencies deps,
+ INetworkManagementService netService, INetd netd, @UserIdInt int userId,
+ @NonNull KeyStore keyStore) {
+ this(looper, context, deps, netService, netd, userId, keyStore,
new SystemServices(context), new Ikev2SessionCreator());
}
@VisibleForTesting
protected Vpn(Looper looper, Context context, Dependencies deps,
- INetworkManagementService netService,
+ INetworkManagementService netService, INetd netd,
int userId, @NonNull KeyStore keyStore, SystemServices systemServices,
Ikev2SessionCreator ikev2SessionCreator) {
mContext = context;
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
mDeps = deps;
- mNetd = netService;
+ mNms = netService;
+ mNetd = netd;
mUserId = userId;
mLooper = looper;
mSystemServices = systemServices;
@@ -768,8 +789,7 @@
// Tell the OS that background services in this app need to be allowed for
// a short time, so we can bootstrap the VPN service.
- DeviceIdleInternal idleController =
- LocalServices.getService(DeviceIdleInternal.class);
+ DeviceIdleInternal idleController = mDeps.getDeviceIdleInternal();
idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS, mUserId, false, "vpn");
@@ -912,7 +932,7 @@
}
try {
- mNetd.denyProtect(mOwnerUID);
+ mNms.denyProtect(mOwnerUID);
} catch (Exception e) {
Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
}
@@ -922,7 +942,7 @@
mOwnerUID = getAppUid(newPackage, mUserId);
mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
try {
- mNetd.allowProtect(mOwnerUID);
+ mNms.allowProtect(mOwnerUID);
} catch (Exception e) {
Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
}
@@ -1579,24 +1599,25 @@
exemptedPackages = new ArrayList<>(mLockdownAllowlist);
exemptedPackages.add(mPackage);
}
- final Set<UidRange> rangesToTellNetdToRemove = new ArraySet<>(mBlockedUidsAsToldToNetd);
+ final Set<UidRangeParcel> rangesToTellNetdToRemove =
+ new ArraySet<>(mBlockedUidsAsToldToNetd);
- final Set<UidRange> rangesToTellNetdToAdd;
+ final Set<UidRangeParcel> rangesToTellNetdToAdd;
if (enforce) {
- final Set<UidRange> rangesThatShouldBeBlocked =
+ final Set<UidRange> restrictedProfilesRanges =
createUserAndRestrictedProfilesRanges(mUserId,
- /* allowedApplications */ null,
- /* disallowedApplications */ exemptedPackages);
+ /* allowedApplications */ null,
+ /* disallowedApplications */ exemptedPackages);
+ final Set<UidRangeParcel> rangesThatShouldBeBlocked = new ArraySet<>();
// The UID range of the first user (0-99999) would block the IPSec traffic, which comes
// directly from the kernel and is marked as uid=0. So we adjust the range to allow
// it through (b/69873852).
- for (UidRange range : rangesThatShouldBeBlocked) {
- if (range.start == 0) {
- rangesThatShouldBeBlocked.remove(range);
- if (range.stop != 0) {
- rangesThatShouldBeBlocked.add(new UidRange(1, range.stop));
- }
+ for (UidRange range : restrictedProfilesRanges) {
+ if (range.start == 0 && range.stop != 0) {
+ rangesThatShouldBeBlocked.add(new UidRangeParcel(1, range.stop));
+ } else if (range.start != 0) {
+ rangesThatShouldBeBlocked.add(new UidRangeParcel(range.start, range.stop));
}
}
@@ -1628,13 +1649,13 @@
* including added ranges that already existed or removed ones that didn't.
*/
@GuardedBy("this")
- private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRange> ranges) {
+ private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRangeParcel> ranges) {
if (ranges.size() == 0) {
return true;
}
- final UidRange[] rangesArray = ranges.toArray(new UidRange[ranges.size()]);
+ final UidRangeParcel[] stableRanges = ranges.toArray(new UidRangeParcel[ranges.size()]);
try {
- mNetd.setAllowOnlyVpnForUids(enforce, rangesArray);
+ mNetd.networkRejectNonSecureVpn(enforce, stableRanges);
} catch (RemoteException | RuntimeException e) {
Log.e(TAG, "Updating blocked=" + enforce
+ " for UIDs " + Arrays.toString(ranges.toArray()) + " failed", e);
@@ -1849,10 +1870,20 @@
if (mNetworkInfo.isConnected()) {
return !appliesToUid(uid);
} else {
- return UidRange.containsUid(mBlockedUidsAsToldToNetd, uid);
+ return containsUid(mBlockedUidsAsToldToNetd, uid);
}
}
+ private boolean containsUid(Collection<UidRangeParcel> ranges, int uid) {
+ if (ranges == null) return false;
+ for (UidRangeParcel range : ranges) {
+ if (range.start <= uid && uid <= range.stop) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void updateAlwaysOnNotification(DetailedState networkState) {
final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
@@ -1944,10 +1975,6 @@
return mContext.createContextAsUser(
UserHandle.of(userId), 0 /* flags */).getContentResolver();
}
-
- public boolean isCallerSystem() {
- return Binder.getCallingUid() == Process.SYSTEM_UID;
- }
}
private native int jniCreate(int mtu);
@@ -2495,7 +2522,7 @@
address /* unused */,
address /* unused */,
network);
- mNetd.setInterfaceUp(mTunnelIface.getInterfaceName());
+ mNms.setInterfaceUp(mTunnelIface.getInterfaceName());
mSession = mIkev2SessionCreator.createIkeSession(
mContext,
@@ -3097,7 +3124,7 @@
@VisibleForTesting
@Nullable
VpnProfile getVpnProfilePrivileged(@NonNull String packageName, @NonNull KeyStore keyStore) {
- if (!mSystemServices.isCallerSystem()) {
+ if (!mDeps.isCallerSystem()) {
Log.wtf(TAG, "getVpnProfilePrivileged called as non-System UID ");
return null;
}
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index 62630300..fa03e59 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -60,12 +60,12 @@
import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
-import android.net.util.IpRange;
import android.system.OsConstants;
import android.util.Log;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.HexDump;
+import com.android.net.module.util.IpRange;
import java.net.Inet4Address;
import java.net.Inet6Address;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 5352840..0fdb879 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -26,6 +26,8 @@
import android.hardware.tv.cec.V1_0.IHdmiCecCallback;
import android.hardware.tv.cec.V1_0.Result;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.icu.util.IllformedLocaleException;
+import android.icu.util.ULocale;
import android.os.Handler;
import android.os.IHwBinder;
import android.os.Looper;
@@ -33,6 +35,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
@@ -48,8 +51,6 @@
import java.util.concurrent.ArrayBlockingQueue;
import java.util.function.Predicate;
-import sun.util.locale.LanguageTag;
-
/**
* Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
* and pass it to CEC HAL so that it sends message to other device. For incoming
@@ -369,13 +370,31 @@
@ServiceThreadOnly
void setLanguage(String language) {
assertRunOnServiceThread();
- if (!LanguageTag.isLanguage(language)) {
+ if (!isLanguage(language)) {
return;
}
mNativeWrapperImpl.nativeSetLanguage(language);
}
/**
+ * Returns true if the language code is well-formed.
+ */
+ @VisibleForTesting static boolean isLanguage(String language) {
+ // Handle null and empty string because because ULocale.Builder#setLanguage accepts them.
+ if (language == null || language.isEmpty()) {
+ return false;
+ }
+
+ ULocale.Builder builder = new ULocale.Builder();
+ try {
+ builder.setLanguage(language);
+ return true;
+ } catch (IllformedLocaleException e) {
+ return false;
+ }
+ }
+
+ /**
* Configure ARC circuit in the hardware logic to start or stop the feature.
*
* @param port ID of HDMI port to which AVR is connected
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index ea24532..c24973d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -156,7 +156,6 @@
addValidationInfo(Constants.MESSAGE_GIVE_DECK_STATUS, statusRequestValidator, DEST_DIRECT);
addValidationInfo(Constants.MESSAGE_PLAY, new PlayModeValidator(), DEST_DIRECT);
- // TODO: Handle messages for the Tuner Control.
addValidationInfo(
Constants.MESSAGE_GIVE_TUNER_DEVICE_STATUS, statusRequestValidator, DEST_DIRECT);
addValidationInfo(
@@ -167,6 +166,10 @@
Constants.MESSAGE_SELECT_DIGITAL_SERVICE,
new SelectDigitalServiceValidator(),
DEST_DIRECT);
+ addValidationInfo(
+ Constants.MESSAGE_TUNER_DEVICE_STATUS,
+ new TunerDeviceStatusValidator(),
+ DEST_DIRECT);
// Messages for the Vendor Specific Commands.
VariableLengthValidator maxLengthValidator = new VariableLengthValidator(0, 14);
@@ -306,7 +309,14 @@
}
private boolean isValidPhysicalAddress(byte[] params, int offset) {
- // TODO: Add more logic like validating 1.0.1.0.
+ int physicalAddress = HdmiUtils.twoBytesToInt(params, offset);
+ while (physicalAddress != 0) {
+ int maskedAddress = physicalAddress & 0xF000;
+ physicalAddress = (physicalAddress << 4) & 0xFFFF;
+ if (maskedAddress == 0 && physicalAddress != 0) {
+ return false;
+ }
+ }
if (!mService.isTvDevice()) {
// If the device is not TV, we can't convert path to port-id, so stop here.
@@ -722,6 +732,35 @@
|| (isWithinRange(value, 0xC1, 0xC3)));
}
+ /*
+ * Check if the given value is a valid Tuner Device info. A valid value is one which falls
+ * within the range description defined in CEC 1.4 Specification : Operand Descriptions
+ * (Section 17)
+ *
+ * @param params Tuner device info
+ * @return true if the Tuner device info is valid
+ */
+ private boolean isValidTunerDeviceInfo(byte[] params) {
+ int tunerDisplayInfo = params[0] & 0x7F;
+ if (tunerDisplayInfo == 0x00) {
+ // Displaying digital tuner
+ if (params.length >= 5) {
+ return isValidDigitalServiceIdentification(params, 1);
+ }
+ } else if (tunerDisplayInfo == 0x01) {
+ // Not displaying Tuner
+ return true;
+ } else if (tunerDisplayInfo == 0x02) {
+ // Displaying Analogue tuner
+ if (params.length >= 5) {
+ return (isValidAnalogueBroadcastType(params[1])
+ && isValidAnalogueFrequency(HdmiUtils.twoBytesToInt(params, 2))
+ && isValidBroadcastSystem(params[4]));
+ }
+ }
+ return false;
+ }
+
private class PhysicalAddressValidator implements ParameterValidator {
@Override
public int isValid(byte[] params) {
@@ -1000,6 +1039,21 @@
}
}
+ /**
+ * Check if the given tuner device status parameter is valid. A valid parameter should lie
+ * within the range description defined in CEC 1.4 Specification : Operand Descriptions (Section
+ * 17)
+ */
+ private class TunerDeviceStatusValidator implements ParameterValidator {
+ @Override
+ public int isValid(byte[] params) {
+ if (params.length < 1) {
+ return ERROR_PARAMETER_SHORT;
+ }
+ return toErrorCode(isValidTunerDeviceInfo(params));
+ }
+ }
+
/** Check if the given user control press parameter is valid. */
private class UserControlPressedValidator implements ParameterValidator {
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e2145f0..8e50bb4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -66,6 +66,8 @@
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -2291,6 +2293,16 @@
}
@Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, String[] args,
+ @Nullable ShellCallback callback, ResultReceiver resultReceiver)
+ throws RemoteException {
+ enforceAccessPermission();
+ new HdmiControlShellCommand(this)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return;
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
new file mode 100644
index 0000000..ee3427f
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.hdmi.IHdmiControlService;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+final class HdmiControlShellCommand extends ShellCommand {
+
+ private static final String TAG = "HdmiShellCommand";
+
+ private final IHdmiControlService.Stub mBinderService;
+
+
+ HdmiControlShellCommand(IHdmiControlService.Stub binderService) {
+ mBinderService = binderService;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ try {
+ return handleShellCommand(cmd);
+ } catch (Exception e) {
+ getErrPrintWriter().println(
+ "Caught error for command '" + cmd + "': " + e.getMessage());
+ Slog.e(TAG, "Error handling hdmi_control shell command: " + cmd, e);
+ return 1;
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+
+ pw.println("HdmiControlManager (hdmi_control) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" onetouchplay, otp");
+ pw.println(" Send the \"One Touch Play\" feature from a source to the TV");
+ pw.println(" vendorcommand --device_type <originating device type>");
+ pw.println(" --destination <destination device>");
+ pw.println(" --args <vendor specific arguments>");
+ pw.println(" [--id <true if vendor command should be sent with vendor id>]");
+ pw.println(" Send a Vendor Command to the given target device");
+ }
+
+ private int handleShellCommand(String cmd) throws RemoteException {
+ PrintWriter pw = getOutPrintWriter();
+
+ switch (cmd) {
+ case "otp":
+ case "onetouchplay":
+ return oneTouchPlay(pw);
+ case "vendorcommand":
+ return vendorCommand(pw);
+ }
+
+ getErrPrintWriter().println("Unhandled command: " + cmd);
+ return 1;
+ }
+
+ private int oneTouchPlay(PrintWriter pw) throws RemoteException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ AtomicInteger cecResult = new AtomicInteger();
+ pw.print("Sending One Touch Play...");
+ mBinderService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ pw.println(" done (" + result + ")");
+ latch.countDown();
+ cecResult.set(result);
+ }
+ });
+
+ try {
+ if (!latch.await(HdmiConfig.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ getErrPrintWriter().println("One Touch Play timed out.");
+ return 1;
+ }
+ } catch (InterruptedException e) {
+ getErrPrintWriter().println("Caught InterruptedException");
+ Thread.currentThread().interrupt();
+ }
+ return cecResult.get() == HdmiControlManager.RESULT_SUCCESS ? 0 : 1;
+ }
+
+ private int vendorCommand(PrintWriter pw) throws RemoteException {
+ if (6 > getRemainingArgsCount()) {
+ throw new IllegalArgumentException("Expected 3 arguments.");
+ }
+
+ int deviceType = -1;
+ int destination = -1;
+ String parameters = "";
+ boolean hasVendorId = false;
+
+ String arg = getNextOption();
+ while (arg != null) {
+ switch (arg) {
+ case "-t":
+ case "--device_type":
+ deviceType = Integer.parseInt(getNextArgRequired());
+ break;
+ case "-d":
+ case "--destination":
+ destination = Integer.parseInt(getNextArgRequired());
+ break;
+ case "-a":
+ case "--args":
+ parameters = getNextArgRequired();
+ break;
+ case "-i":
+ case "--id":
+ hasVendorId = Boolean.parseBoolean(getNextArgRequired());
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown argument: " + arg);
+ }
+ arg = getNextArg();
+ }
+
+ String[] parts = parameters.split(":");
+ byte[] params = new byte[parts.length];
+ for (int i = 0; i < params.length; i++) {
+ params[i] = (byte) Integer.parseInt(parts[i], 16);
+ }
+
+ pw.println("Sending <Vendor Command>");
+ mBinderService.sendVendorCommand(deviceType, destination, params, hasVendorId);
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/OWNERS b/services/core/java/com/android/server/locksettings/OWNERS
index dad6e39..08b8a8c 100644
--- a/services/core/java/com/android/server/locksettings/OWNERS
+++ b/services/core/java/com/android/server/locksettings/OWNERS
@@ -1,2 +1,3 @@
jaggies@google.com
kchyn@google.com
+rubinxu@google.com
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 5787f7c4..8d5f553 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -15,20 +15,15 @@
*/
package com.android.server.locksettings;
-
import static android.os.UserHandle.USER_SYSTEM;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
-import android.hardware.rebootescrow.IRebootEscrow;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Slog;
@@ -44,7 +39,6 @@
import java.util.Date;
import java.util.List;
import java.util.Locale;
-import java.util.NoSuchElementException;
class RebootEscrowManager {
private static final String TAG = "RebootEscrowManager";
@@ -116,8 +110,24 @@
static class Injector {
protected Context mContext;
+ private final RebootEscrowProviderInterface mRebootEscrowProvider;
+
Injector(Context context) {
mContext = context;
+ RebootEscrowProviderInterface rebootEscrowProvider = null;
+ // TODO(xunchang) add implementation for server based ror.
+ if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
+ "server_based_ror_enabled", false)) {
+ Slog.e(TAG, "Server based ror isn't implemented yet.");
+ } else {
+ rebootEscrowProvider = new RebootEscrowProviderHalImpl();
+ }
+
+ if (rebootEscrowProvider != null && rebootEscrowProvider.hasRebootEscrowSupport()) {
+ mRebootEscrowProvider = rebootEscrowProvider;
+ } else {
+ mRebootEscrowProvider = null;
+ }
}
public Context getContext() {
@@ -128,15 +138,8 @@
return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
- @Nullable
- public IRebootEscrow getRebootEscrow() {
- try {
- return IRebootEscrow.Stub.asInterface(ServiceManager.getService(
- "android.hardware.rebootescrow.IRebootEscrow/default"));
- } catch (NoSuchElementException e) {
- Slog.i(TAG, "Device doesn't implement RebootEscrow HAL");
- }
- return null;
+ public RebootEscrowProviderInterface getRebootEscrowProvider() {
+ return mRebootEscrowProvider;
}
public int getBootCount() {
@@ -210,45 +213,18 @@
}
private RebootEscrowKey getAndClearRebootEscrowKey() {
- IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
- if (rebootEscrow == null) {
- Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrow HAL is unavailable");
+ RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
+ if (rebootEscrowProvider == null) {
+ Slog.w(TAG,
+ "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
return null;
}
- try {
- byte[] escrowKeyBytes = rebootEscrow.retrieveKey();
- if (escrowKeyBytes == null) {
- Slog.w(TAG, "Had reboot escrow data for users, but could not retrieve key");
- return null;
- } else if (escrowKeyBytes.length != 32) {
- Slog.e(TAG, "IRebootEscrow returned key of incorrect size "
- + escrowKeyBytes.length);
- return null;
- }
-
- // Make sure we didn't get the null key.
- int zero = 0;
- for (int i = 0; i < escrowKeyBytes.length; i++) {
- zero |= escrowKeyBytes[i];
- }
- if (zero == 0) {
- Slog.w(TAG, "IRebootEscrow returned an all-zeroes key");
- return null;
- }
-
- // Overwrite the existing key with the null key
- rebootEscrow.storeKey(new byte[32]);
-
+ RebootEscrowKey key = rebootEscrowProvider.getAndClearRebootEscrowKey(null);
+ if (key != null) {
mEventLog.addEntry(RebootEscrowEvent.RETRIEVED_STORED_KEK);
- return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
- } catch (RemoteException e) {
- Slog.w(TAG, "Could not retrieve escrow data");
- return null;
- } catch (ServiceSpecificException e) {
- Slog.w(TAG, "Got service-specific exception: " + e.errorCode);
- return null;
}
+ return key;
}
private boolean restoreRebootEscrowForUser(@UserIdInt int userId, RebootEscrowKey key) {
@@ -279,9 +255,9 @@
return;
}
- IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
- if (rebootEscrow == null) {
- Slog.w(TAG, "Reboot escrow requested, but RebootEscrow HAL is unavailable");
+ if (mInjector.getRebootEscrowProvider() == null) {
+ Slog.w(TAG,
+ "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
return;
}
@@ -293,6 +269,7 @@
final RebootEscrowData escrowData;
try {
+ // TODO(xunchang) further wrap the escrowData with a key from keystore.
escrowData = RebootEscrowData.fromSyntheticPassword(escrowKey, spVersion,
syntheticPassword);
} catch (IOException e) {
@@ -330,18 +307,16 @@
mRebootEscrowWanted = false;
setRebootEscrowReady(false);
- IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
- if (rebootEscrow == null) {
+
+ RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
+ if (rebootEscrowProvider == null) {
+ Slog.w(TAG,
+ "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
return;
}
mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM);
-
- try {
- rebootEscrow.storeKey(new byte[32]);
- } catch (RemoteException | ServiceSpecificException e) {
- Slog.w(TAG, "Could not call RebootEscrow HAL to shred key");
- }
+ rebootEscrowProvider.clearRebootEscrowKey();
List<UserInfo> users = mUserManager.getUsers();
for (UserInfo user : users) {
@@ -356,9 +331,10 @@
return false;
}
- IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
- if (rebootEscrow == null) {
- Slog.w(TAG, "Escrow marked as ready, but RebootEscrow HAL is unavailable");
+ RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
+ if (rebootEscrowProvider == null) {
+ Slog.w(TAG,
+ "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
return false;
}
@@ -372,15 +348,7 @@
return false;
}
- boolean armedRebootEscrow = false;
- try {
- rebootEscrow.storeKey(escrowKey.getKeyBytes());
- armedRebootEscrow = true;
- Slog.i(TAG, "Reboot escrow key stored with RebootEscrow HAL");
- } catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e);
- }
-
+ boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, null);
if (armedRebootEscrow) {
mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
@@ -397,7 +365,7 @@
}
boolean prepareRebootEscrow() {
- if (mInjector.getRebootEscrow() == null) {
+ if (mInjector.getRebootEscrowProvider() == null) {
return false;
}
@@ -408,7 +376,7 @@
}
boolean clearRebootEscrow() {
- if (mInjector.getRebootEscrow() == null) {
+ if (mInjector.getRebootEscrowProvider() == null) {
return false;
}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
new file mode 100644
index 0000000..6c1040b
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.annotation.Nullable;
+import android.hardware.rebootescrow.IRebootEscrow;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.NoSuchElementException;
+
+import javax.crypto.SecretKey;
+
+/**
+ * An implementation of the {@link RebootEscrowProviderInterface} by calling the RebootEscrow HAL.
+ */
+class RebootEscrowProviderHalImpl implements RebootEscrowProviderInterface {
+ private static final String TAG = "RebootEscrowProvider";
+
+ private final Injector mInjector;
+
+ static class Injector {
+ @Nullable
+ public IRebootEscrow getRebootEscrow() {
+ try {
+ return IRebootEscrow.Stub.asInterface(ServiceManager.getService(
+ "android.hardware.rebootescrow.IRebootEscrow/default"));
+ } catch (NoSuchElementException e) {
+ Slog.i(TAG, "Device doesn't implement RebootEscrow HAL");
+ }
+ return null;
+ }
+ }
+
+ RebootEscrowProviderHalImpl() {
+ mInjector = new Injector();
+ }
+
+ @VisibleForTesting
+ RebootEscrowProviderHalImpl(Injector injector) {
+ mInjector = injector;
+ }
+
+ @Override
+ public boolean hasRebootEscrowSupport() {
+ return mInjector.getRebootEscrow() != null;
+ }
+
+ @Override
+ public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
+ IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+ if (rebootEscrow == null) {
+ Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrow HAL is unavailable");
+ return null;
+ }
+
+ try {
+ byte[] escrowKeyBytes = rebootEscrow.retrieveKey();
+ if (escrowKeyBytes == null) {
+ Slog.w(TAG, "Had reboot escrow data for users, but could not retrieve key");
+ return null;
+ } else if (escrowKeyBytes.length != 32) {
+ Slog.e(TAG, "IRebootEscrow returned key of incorrect size "
+ + escrowKeyBytes.length);
+ return null;
+ }
+
+ // Make sure we didn't get the null key.
+ int zero = 0;
+ for (int i = 0; i < escrowKeyBytes.length; i++) {
+ zero |= escrowKeyBytes[i];
+ }
+ if (zero == 0) {
+ Slog.w(TAG, "IRebootEscrow returned an all-zeroes key");
+ return null;
+ }
+
+ // Overwrite the existing key with the null key
+ rebootEscrow.storeKey(new byte[32]);
+
+ return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not retrieve escrow data");
+ return null;
+ } catch (ServiceSpecificException e) {
+ Slog.w(TAG, "Got service-specific exception: " + e.errorCode);
+ return null;
+ }
+ }
+
+ @Override
+ public void clearRebootEscrowKey() {
+ IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+ if (rebootEscrow == null) {
+ return;
+ }
+
+ try {
+ rebootEscrow.storeKey(new byte[32]);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.w(TAG, "Could not call RebootEscrow HAL to shred key");
+ }
+
+ }
+
+ @Override
+ public boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey) {
+ IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+ if (rebootEscrow == null) {
+ Slog.w(TAG, "Escrow marked as ready, but RebootEscrow HAL is unavailable");
+ return false;
+ }
+
+ try {
+ // The HAL interface only accept 32 bytes data. And the encrypted bytes for the escrow
+ // key may exceed that limit. So we just store the raw key bytes here.
+ rebootEscrow.storeKey(escrowKey.getKeyBytes());
+ Slog.i(TAG, "Reboot escrow key stored with RebootEscrow HAL");
+ return true;
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e);
+ }
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
new file mode 100644
index 0000000..857ad5f
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Provides APIs for {@link RebootEscrowManager} to access and manage the reboot escrow key.
+ * Implementations need to find a way to persist the key across a reboot, and securely discards the
+ * persisted copy.
+ *
+ * @hide
+ */
+public interface RebootEscrowProviderInterface {
+ /**
+ * Returns true if the secure store/discard of reboot escrow key is supported.
+ */
+ boolean hasRebootEscrowSupport();
+
+ /**
+ * Returns the stored RebootEscrowKey, and clears the storage. If the stored key is encrypted,
+ * use the input key to decrypt the RebootEscrowKey. Returns null on failure.
+ */
+ RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey);
+
+ /**
+ * Clears the stored RebootEscrowKey.
+ */
+ void clearRebootEscrowKey();
+
+ /**
+ * Saves the given RebootEscrowKey, optionally encrypt the storage with the encryptionKey.
+ */
+ boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey);
+}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 0b3cdae..7afa81a 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -165,11 +165,13 @@
private void buildBluetoothRoutes() {
mBluetoothRoutes.clear();
- for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
- if (device.isConnected()) {
- BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
- if (newBtRoute.connectedProfiles.size() > 0) {
- mBluetoothRoutes.put(device.getAddress(), newBtRoute);
+ if (mBluetoothAdapter.getBondedDevices() != null) {
+ for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
+ if (device.isConnected()) {
+ BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
+ if (newBtRoute.connectedProfiles.size() > 0) {
+ mBluetoothRoutes.put(device.getAddress(), newBtRoute);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 47fcc08..dd507a3 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -297,9 +297,8 @@
}
@Override
- public void onUiIntensiveBugreportDumpsFinished(String callingPackage)
- throws RemoteException {
- mListener.onUiIntensiveBugreportDumpsFinished(callingPackage);
+ public void onUiIntensiveBugreportDumpsFinished() throws RemoteException {
+ mListener.onUiIntensiveBugreportDumpsFinished();
}
@Override
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 069a00f..f221802 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -290,7 +290,8 @@
private void updateEnabledState(@NonNull AndroidPackage pkg) {
// TODO(b/135203078): Do not use toAppInfo
- final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternal(
+ // TODO(b/167551701): Make changeId non-logging
+ final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternalNoLogging(
PackageManager.FILTER_APPLICATION_QUERY, pkg.toAppInfoWithoutState());
if (enabled) {
mDisabledPackages.remove(pkg.getPackageName());
diff --git a/services/core/java/com/android/server/policy/OWNERS b/services/core/java/com/android/server/policy/OWNERS
index d25ec4a..8887e40 100644
--- a/services/core/java/com/android/server/policy/OWNERS
+++ b/services/core/java/com/android/server/policy/OWNERS
@@ -1,2 +1,3 @@
include /services/core/java/com/android/server/wm/OWNERS
include /services/core/java/com/android/server/input/OWNERS
+include /services/core/java/com/android/server/pm/permission/OWNERS
diff --git a/services/core/java/com/android/server/recoverysystem/OWNERS b/services/core/java/com/android/server/recoverysystem/OWNERS
new file mode 100644
index 0000000..79ded0d
--- /dev/null
+++ b/services/core/java/com/android/server/recoverysystem/OWNERS
@@ -0,0 +1,2 @@
+rvrolyk@google.com
+zhaojiac@google.com
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index e0701e86..5c01e43 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -16,8 +16,10 @@
package com.android.server.recoverysystem;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.IntentSender;
+import android.content.pm.PackageManager;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Binder;
@@ -30,8 +32,11 @@
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemProperties;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockSettingsInternal;
import com.android.internal.widget.RebootEscrowListener;
@@ -76,9 +81,53 @@
private final Injector mInjector;
private final Context mContext;
- private boolean mPreparedForReboot;
- private String mUnattendedRebootToken;
- private IntentSender mPreparedForRebootIntentSender;
+ @GuardedBy("this")
+ private final ArrayMap<String, IntentSender> mCallerPendingRequest = new ArrayMap<>();
+ @GuardedBy("this")
+ private final ArraySet<String> mCallerPreparedForReboot = new ArraySet<>();
+
+ /**
+ * Need to prepare for resume on reboot.
+ */
+ private static final int ROR_NEED_PREPARATION = 0;
+ /**
+ * Resume on reboot has been prepared, notify the caller.
+ */
+ private static final int ROR_SKIP_PREPARATION_AND_NOTIFY = 1;
+ /**
+ * Resume on reboot has been requested. Caller won't be notified until the preparation is done.
+ */
+ private static final int ROR_SKIP_PREPARATION_NOT_NOTIFY = 2;
+
+ /**
+ * The caller never requests for resume on reboot, no need for clear.
+ */
+ private static final int ROR_NOT_REQUESTED = 0;
+ /**
+ * Clear the resume on reboot preparation state.
+ */
+ private static final int ROR_REQUESTED_NEED_CLEAR = 1;
+ /**
+ * The caller has requested for resume on reboot. No need for clear since other callers may
+ * exist.
+ */
+ private static final int ROR_REQUESTED_SKIP_CLEAR = 2;
+
+ /**
+ * The action to perform upon new resume on reboot prepare request for a given client.
+ */
+ @IntDef({ ROR_NEED_PREPARATION,
+ ROR_SKIP_PREPARATION_AND_NOTIFY,
+ ROR_SKIP_PREPARATION_NOT_NOTIFY })
+ private @interface ResumeOnRebootActionsOnRequest {}
+
+ /**
+ * The action to perform upon resume on reboot clear request for a given client.
+ */
+ @IntDef({ROR_NOT_REQUESTED,
+ ROR_REQUESTED_NEED_CLEAR,
+ ROR_REQUESTED_SKIP_CLEAR})
+ private @interface ResumeOnRebootActionsOnClear{}
static class Injector {
protected final Context mContext;
@@ -286,47 +335,95 @@
}
}
- @Override // Binder call
- public boolean requestLskf(String updateToken, IntentSender intentSender) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+ private void enforcePermissionForResumeOnReboot() {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.RECOVERY)
+ != PackageManager.PERMISSION_GRANTED
+ && mContext.checkCallingOrSelfPermission(android.Manifest.permission.REBOOT)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Caller must have " + android.Manifest.permission.RECOVERY
+ + " or " + android.Manifest.permission.REBOOT + " for resume on reboot.");
+ }
+ }
- if (updateToken == null) {
+ @Override // Binder call
+ public boolean requestLskf(String packageName, IntentSender intentSender) {
+ enforcePermissionForResumeOnReboot();
+
+ if (packageName == null) {
+ Slog.w(TAG, "Missing packageName when requesting lskf.");
return false;
}
- // No need to prepare again for the same token.
- if (mPreparedForReboot && updateToken.equals(mUnattendedRebootToken)) {
- return true;
+ @ResumeOnRebootActionsOnRequest int action = updateRoRPreparationStateOnNewRequest(
+ packageName, intentSender);
+ switch (action) {
+ case ROR_SKIP_PREPARATION_AND_NOTIFY:
+ // We consider the preparation done if someone else has prepared.
+ sendPreparedForRebootIntentIfNeeded(intentSender);
+ return true;
+ case ROR_SKIP_PREPARATION_NOT_NOTIFY:
+ return true;
+ case ROR_NEED_PREPARATION:
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ mInjector.getLockSettingsService().prepareRebootEscrow();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ return true;
+ default:
+ throw new IllegalStateException("Unsupported action type on new request " + action);
+ }
+ }
+
+ // Checks and updates the resume on reboot preparation state.
+ private synchronized @ResumeOnRebootActionsOnRequest int updateRoRPreparationStateOnNewRequest(
+ String packageName, IntentSender intentSender) {
+ if (!mCallerPreparedForReboot.isEmpty()) {
+ if (mCallerPreparedForReboot.contains(packageName)) {
+ Slog.i(TAG, "RoR already has prepared for " + packageName);
+ }
+
+ // Someone else has prepared. Consider the preparation done, and send back the intent.
+ mCallerPreparedForReboot.add(packageName);
+ return ROR_SKIP_PREPARATION_AND_NOTIFY;
}
- mPreparedForReboot = false;
- mUnattendedRebootToken = updateToken;
- mPreparedForRebootIntentSender = intentSender;
-
- final long origId = Binder.clearCallingIdentity();
- try {
- mInjector.getLockSettingsService().prepareRebootEscrow();
- } finally {
- Binder.restoreCallingIdentity(origId);
+ boolean needPreparation = mCallerPendingRequest.isEmpty();
+ if (mCallerPendingRequest.containsKey(packageName)) {
+ Slog.i(TAG, "Duplicate RoR preparation request for " + packageName);
}
-
- return true;
+ // Update the request with the new intentSender.
+ mCallerPendingRequest.put(packageName, intentSender);
+ return needPreparation ? ROR_NEED_PREPARATION : ROR_SKIP_PREPARATION_NOT_NOTIFY;
}
@Override
public void onPreparedForReboot(boolean ready) {
- if (mUnattendedRebootToken == null) {
- Slog.w(TAG, "onPreparedForReboot called when mUnattendedRebootToken is null");
+ if (!ready) {
+ return;
}
-
- mPreparedForReboot = ready;
- if (ready) {
- sendPreparedForRebootIntentIfNeeded();
- }
+ updateRoRPreparationStateOnPreparedForReboot();
}
- private void sendPreparedForRebootIntentIfNeeded() {
- final IntentSender intentSender = mPreparedForRebootIntentSender;
+ private synchronized void updateRoRPreparationStateOnPreparedForReboot() {
+ if (!mCallerPreparedForReboot.isEmpty()) {
+ Slog.w(TAG, "onPreparedForReboot called when some clients have prepared.");
+ }
+
+ if (mCallerPendingRequest.isEmpty()) {
+ Slog.w(TAG, "onPreparedForReboot called but no client has requested.");
+ }
+
+ // Send intents to notify callers
+ for (int i = 0; i < mCallerPendingRequest.size(); i++) {
+ sendPreparedForRebootIntentIfNeeded(mCallerPendingRequest.valueAt(i));
+ mCallerPreparedForReboot.add(mCallerPendingRequest.keyAt(i));
+ }
+ mCallerPendingRequest.clear();
+ }
+
+ private void sendPreparedForRebootIntentIfNeeded(IntentSender intentSender) {
if (intentSender != null) {
try {
intentSender.sendIntent(null, 0, null, null, null);
@@ -337,37 +434,59 @@
}
@Override // Binder call
- public boolean clearLskf() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
-
- mPreparedForReboot = false;
- mUnattendedRebootToken = null;
- mPreparedForRebootIntentSender = null;
-
- final long origId = Binder.clearCallingIdentity();
- try {
- mInjector.getLockSettingsService().clearRebootEscrow();
- } finally {
- Binder.restoreCallingIdentity(origId);
+ public boolean clearLskf(String packageName) {
+ enforcePermissionForResumeOnReboot();
+ if (packageName == null) {
+ Slog.w(TAG, "Missing packageName when clearing lskf.");
+ return false;
}
- return true;
+ @ResumeOnRebootActionsOnClear int action = updateRoRPreparationStateOnClear(packageName);
+ switch (action) {
+ case ROR_NOT_REQUESTED:
+ Slog.w(TAG, "RoR clear called before preparation for caller " + packageName);
+ return true;
+ case ROR_REQUESTED_SKIP_CLEAR:
+ return true;
+ case ROR_REQUESTED_NEED_CLEAR:
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ mInjector.getLockSettingsService().clearRebootEscrow();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ return true;
+ default:
+ throw new IllegalStateException("Unsupported action type on clear " + action);
+ }
}
- @Override // Binder call
- public boolean rebootWithLskf(String updateToken, String reason) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+ private synchronized @ResumeOnRebootActionsOnClear int updateRoRPreparationStateOnClear(
+ String packageName) {
+ if (!mCallerPreparedForReboot.contains(packageName) && !mCallerPendingRequest.containsKey(
+ packageName)) {
+ Slog.w(TAG, packageName + " hasn't prepared for resume on reboot");
+ return ROR_NOT_REQUESTED;
+ }
+ mCallerPendingRequest.remove(packageName);
+ mCallerPreparedForReboot.remove(packageName);
- if (!mPreparedForReboot) {
- Slog.i(TAG, "Reboot requested before prepare completed");
+ // Check if others have prepared ROR.
+ boolean needClear = mCallerPendingRequest.isEmpty() && mCallerPreparedForReboot.isEmpty();
+ return needClear ? ROR_REQUESTED_NEED_CLEAR : ROR_REQUESTED_SKIP_CLEAR;
+ }
+
+ private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
+ if (packageName == null) {
+ Slog.w(TAG, "Missing packageName when rebooting with lskf.");
+ return false;
+ }
+ if (!isLskfCaptured(packageName)) {
return false;
}
- if (updateToken != null && !updateToken.equals(mUnattendedRebootToken)) {
- Slog.i(TAG, "Reboot requested after preparation, but with mismatching token");
- return false;
- }
-
+ // TODO(xunchang) check the slot to boot into, and fail the reboot upon slot mismatch.
+ // TODO(xunchang) write the vbmeta digest along with the escrowKey before reboot.
if (!mInjector.getLockSettingsService().armRebootEscrow()) {
Slog.w(TAG, "Failure to escrow key for reboot");
return false;
@@ -378,6 +497,34 @@
return true;
}
+ @Override // Binder call for the legacy rebootWithLskf
+ public boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+ return rebootWithLskfImpl(packageName, reason, true);
+ }
+
+ @Override // Binder call
+ public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) {
+ enforcePermissionForResumeOnReboot();
+ return rebootWithLskfImpl(packageName, reason, slotSwitch);
+ }
+
+ @Override // Binder call
+ public boolean isLskfCaptured(String packageName) {
+ enforcePermissionForResumeOnReboot();
+ boolean captured;
+ synchronized (this) {
+ captured = mCallerPreparedForReboot.contains(packageName);
+ }
+
+ if (!captured) {
+ Slog.i(TAG, "Reboot requested before prepare completed for caller "
+ + packageName);
+ return false;
+ }
+ return true;
+ }
+
/**
* Check if any of the init services is still running. If so, we cannot
* start a new uncrypt/setup-bcb/clear-bcb service right away; otherwise
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
index c6905b5..f20d80d 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
@@ -56,26 +56,31 @@
}
private int requestLskf() throws RemoteException {
- String updateToken = getNextArgRequired();
- boolean success = mService.requestLskf(updateToken, null);
+ String packageName = getNextArgRequired();
+ boolean success = mService.requestLskf(packageName, null);
PrintWriter pw = getOutPrintWriter();
- pw.println("Request LSKF status: " + (success ? "success" : "failure"));
+ pw.printf("Request LSKF for packageName: %s, status: %s\n", packageName,
+ success ? "success" : "failure");
return 0;
}
private int clearLskf() throws RemoteException {
- boolean success = mService.clearLskf();
+ String packageName = getNextArgRequired();
+ boolean success = mService.clearLskf(packageName);
PrintWriter pw = getOutPrintWriter();
- pw.println("Clear LSKF: " + (success ? "success" : "failure"));
+ pw.printf("Clear LSKF for packageName: %s, status: %s\n", packageName,
+ success ? "success" : "failure");
return 0;
}
private int rebootAndApply() throws RemoteException {
- String updateToken = getNextArgRequired();
+ String packageName = getNextArgRequired();
String rebootReason = getNextArgRequired();
- boolean success = mService.rebootWithLskf(updateToken, rebootReason);
+ boolean success = mService.rebootWithLskf(packageName, rebootReason, true);
PrintWriter pw = getOutPrintWriter();
- pw.println("Reboot and apply status: " + (success ? "success" : "failure"));
+ // Keep the old message for cts test.
+ pw.printf("%s Reboot and apply status: %s\n", packageName,
+ success ? "success" : "failure");
return 0;
}
diff --git a/services/core/java/com/android/server/timedetector/OWNERS b/services/core/java/com/android/server/timedetector/OWNERS
index 09447a97..8f80897 100644
--- a/services/core/java/com/android/server/timedetector/OWNERS
+++ b/services/core/java/com/android/server/timedetector/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 6dac6b1..84776ea 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
@@ -122,6 +123,14 @@
mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
}
+ @Override
+ public void suggestGnssTime(@NonNull GnssTimeSuggestion timeSignal) {
+ enforceSuggestGnssTimePermission();
+ Objects.requireNonNull(timeSignal);
+
+ mHandler.post(() -> mTimeDetectorStrategy.suggestGnssTime(timeSignal));
+ }
+
/** Internal method for handling the auto time setting being changed. */
@VisibleForTesting
public void handleAutoTimeDetectionChanged() {
@@ -153,4 +162,10 @@
android.Manifest.permission.SET_TIME,
"set time");
}
+
+ private void enforceSuggestGnssTimePermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.SET_TIME,
+ "suggest gnss time");
+ }
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index abd4c8c..de6412c 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -40,7 +41,7 @@
*/
public interface TimeDetectorStrategy {
- @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK })
+ @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK, ORIGIN_GNSS })
@Retention(RetentionPolicy.SOURCE)
@interface Origin {}
@@ -56,6 +57,10 @@
@Origin
int ORIGIN_NETWORK = 3;
+ /** Used when a time value originated from a gnss signal. */
+ @Origin
+ int ORIGIN_GNSS = 4;
+
/** Processes the suggested time from telephony sources. */
void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion);
@@ -70,6 +75,9 @@
/** Processes the suggested time from network sources. */
void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion);
+ /** Processes the suggested time from gnss sources. */
+ void suggestGnssTime(@NonNull GnssTimeSuggestion timeSuggestion);
+
/**
* Handles the auto-time configuration changing For example, when the auto-time setting is
* toggled on or off.
@@ -102,6 +110,8 @@
return "network";
case ORIGIN_TELEPHONY:
return "telephony";
+ case ORIGIN_GNSS:
+ return "gnss";
default:
throw new IllegalArgumentException("origin=" + origin);
}
@@ -119,6 +129,8 @@
return ORIGIN_NETWORK;
case "telephony":
return ORIGIN_TELEPHONY;
+ case "gnss":
+ return ORIGIN_GNSS;
default:
throw new IllegalArgumentException("originString=" + originString);
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index b3c8c90..6fe3782 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
+import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -109,6 +110,10 @@
private final ReferenceWithHistory<NetworkTimeSuggestion> mLastNetworkSuggestion =
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+ @GuardedBy("this")
+ private final ReferenceWithHistory<GnssTimeSuggestion> mLastGnssSuggestion =
+ new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+
/**
* The interface used by the strategy to interact with the surrounding service.
*
@@ -166,6 +171,20 @@
}
@Override
+ public synchronized void suggestGnssTime(@NonNull GnssTimeSuggestion timeSuggestion) {
+ final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
+
+ if (!validateAutoSuggestionTime(newUtcTime, timeSuggestion)) {
+ return;
+ }
+
+ mLastGnssSuggestion.set(timeSuggestion);
+
+ String reason = "GNSS time suggestion received: suggestion=" + timeSuggestion;
+ doAutoTimeDetection(reason);
+ }
+
+ @Override
public synchronized boolean suggestManualTime(@NonNull ManualTimeSuggestion suggestion) {
final TimestampedValue<Long> newUtcTime = suggestion.getUtcTime();
@@ -280,6 +299,11 @@
mLastNetworkSuggestion.dump(ipw);
ipw.decreaseIndent(); // level 2
+ ipw.println("Gnss suggestion history:");
+ ipw.increaseIndent(); // level 2
+ mLastGnssSuggestion.dump(ipw);
+ ipw.decreaseIndent(); // level 2
+
ipw.decreaseIndent(); // level 1
ipw.flush();
}
@@ -386,6 +410,14 @@
+ ", networkSuggestion=" + networkSuggestion
+ ", detectionReason=" + detectionReason;
}
+ } else if (origin == ORIGIN_GNSS) {
+ GnssTimeSuggestion gnssTimeSuggestion = findLatestValidGnssSuggestion();
+ if (gnssTimeSuggestion != null) {
+ newUtcTime = gnssTimeSuggestion.getUtcTime();
+ cause = "Found good gnss suggestion."
+ + ", gnssTimeSuggestion=" + gnssTimeSuggestion
+ + ", detectionReason=" + detectionReason;
+ }
} else {
Slog.w(LOG_TAG, "Unknown or unsupported origin=" + origin
+ " in " + Arrays.toString(originPriorities)
@@ -527,6 +559,26 @@
return networkSuggestion;
}
+ /** Returns the latest, valid, gnss suggestion. Returns {@code null} if there isn't one. */
+ @GuardedBy("this")
+ @Nullable
+ private GnssTimeSuggestion findLatestValidGnssSuggestion() {
+ GnssTimeSuggestion gnssTimeSuggestion = mLastGnssSuggestion.get();
+ if (gnssTimeSuggestion == null) {
+ // No gnss suggestions received. This is normal if there's no gnss signal.
+ return null;
+ }
+
+ TimestampedValue<Long> utcTime = gnssTimeSuggestion.getUtcTime();
+ long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
+ // The latest suggestion is not valid, usually due to its age.
+ return null;
+ }
+
+ return gnssTimeSuggestion;
+ }
+
@GuardedBy("this")
private boolean setSystemClockIfRequired(
@Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) {
@@ -655,6 +707,16 @@
}
/**
+ * Returns the latest valid gnss suggestion. Not intended for general use: it is used during
+ * tests to check strategy behavior.
+ */
+ @VisibleForTesting
+ @Nullable
+ public synchronized GnssTimeSuggestion findLatestValidGnssSuggestionForTests() {
+ return findLatestValidGnssSuggestion();
+ }
+
+ /**
* A method used to inspect state during tests. Not intended for general use.
*/
@VisibleForTesting
@@ -672,6 +734,15 @@
return mLastNetworkSuggestion.get();
}
+ /**
+ * A method used to inspect state during tests. Not intended for general use.
+ */
+ @VisibleForTesting
+ @Nullable
+ public synchronized GnssTimeSuggestion getLatestGnssSuggestion() {
+ return mLastGnssSuggestion.get();
+ }
+
private static boolean validateSuggestionUtcTime(
long elapsedRealtimeMillis, TimestampedValue<Long> utcTime) {
long referenceTimeMillis = utcTime.getReferenceTimeMillis();
diff --git a/services/core/java/com/android/server/timezone/OWNERS b/services/core/java/com/android/server/timezone/OWNERS
index 09447a97..8f80897 100644
--- a/services/core/java/com/android/server/timezone/OWNERS
+++ b/services/core/java/com/android/server/timezone/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/core/java/com/android/server/timezonedetector/OWNERS b/services/core/java/com/android/server/timezonedetector/OWNERS
index 09447a97..8f80897 100644
--- a/services/core/java/com/android/server/timezonedetector/OWNERS
+++ b/services/core/java/com/android/server/timezonedetector/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/core/java/com/android/server/vcn/Android.bp b/services/core/java/com/android/server/vcn/Android.bp
new file mode 100644
index 0000000..5ed204f
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/Android.bp
@@ -0,0 +1,4 @@
+filegroup {
+ name: "framework-vcn-util-sources",
+ srcs: ["util/**/*.java"],
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
new file mode 100644
index 0000000..c060807
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
+import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * TelephonySubscriptionTracker provides a caching layer for tracking active subscription groups.
+ *
+ * <p>This class performs two roles:
+ *
+ * <ol>
+ * <li>De-noises subscription changes by ensuring that only changes in active and ready
+ * subscription groups are acted upon
+ * <li>Caches mapping between subIds and subscription groups
+ * </ol>
+ *
+ * <p>An subscription group is active and ready if any of its contained subIds has had BOTH the
+ * {@link CarrierConfigManager#isConfigForIdentifiedCarrier()} return true, AND the subscription is
+ * listed as active per SubscriptionManager#getAllSubscriptionInfoList().
+ *
+ * <p>Note that due to the asynchronous nature of callbacks and broadcasts, the output of this class
+ * is (only) eventually consistent.
+ *
+ * @hide
+ */
+public class TelephonySubscriptionTracker extends BroadcastReceiver {
+ @NonNull private static final String TAG = TelephonySubscriptionTracker.class.getSimpleName();
+ private static final boolean LOG_DBG = false; // STOPSHIP if true
+
+ @NonNull private final Context mContext;
+ @NonNull private final Handler mHandler;
+ @NonNull private final TelephonySubscriptionTrackerCallback mCallback;
+ @NonNull private final Dependencies mDeps;
+
+ @NonNull private final SubscriptionManager mSubscriptionManager;
+ @NonNull private final CarrierConfigManager mCarrierConfigManager;
+
+ // TODO (Android T+): Add ability to handle multiple subIds per slot.
+ @NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>();
+ @NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener;
+
+ @NonNull private TelephonySubscriptionSnapshot mCurrentSnapshot;
+
+ public TelephonySubscriptionTracker(
+ @NonNull Context context,
+ @NonNull Handler handler,
+ @NonNull TelephonySubscriptionTrackerCallback callback) {
+ this(context, handler, callback, new Dependencies());
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ TelephonySubscriptionTracker(
+ @NonNull Context context,
+ @NonNull Handler handler,
+ @NonNull TelephonySubscriptionTrackerCallback callback,
+ @NonNull Dependencies deps) {
+ mContext = Objects.requireNonNull(context, "Missing context");
+ mHandler = Objects.requireNonNull(handler, "Missing handler");
+ mCallback = Objects.requireNonNull(callback, "Missing callback");
+ mDeps = Objects.requireNonNull(deps, "Missing deps");
+
+ mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+
+ mSubscriptionChangedListener =
+ new OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ handleSubscriptionsChanged();
+ }
+ };
+ }
+
+ /** Registers the receivers, and starts tracking subscriptions. */
+ public void register() {
+ mContext.registerReceiver(
+ this, new IntentFilter(ACTION_CARRIER_CONFIG_CHANGED), null, mHandler);
+ mSubscriptionManager.addOnSubscriptionsChangedListener(
+ new HandlerExecutor(mHandler), mSubscriptionChangedListener);
+ }
+
+ /** Unregisters the receivers, and stops tracking subscriptions. */
+ public void unregister() {
+ mContext.unregisterReceiver(this);
+ mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener);
+ }
+
+ /**
+ * Handles subscription changes, correlating available subscriptions and loaded carrier configs
+ *
+ * <p>The subscription change listener is registered with a HandlerExecutor backed by mHandler,
+ * so callbacks & broadcasts are all serialized on mHandler, avoiding the need for locking.
+ */
+ public void handleSubscriptionsChanged() {
+ final Set<ParcelUuid> activeSubGroups = new ArraySet<>();
+ final Map<Integer, ParcelUuid> newSubIdToGroupMap = new HashMap<>();
+
+ final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList();
+ if (allSubs == null) {
+ return; // Telephony crashed; no way to verify subscriptions.
+ }
+
+ // If allSubs is empty, no subscriptions exist. Cache will be cleared by virtue of no active
+ // subscriptions
+ for (SubscriptionInfo subInfo : allSubs) {
+ if (subInfo.getGroupUuid() == null) {
+ continue;
+ }
+
+ // Build subId -> subGrp cache
+ newSubIdToGroupMap.put(subInfo.getSubscriptionId(), subInfo.getGroupUuid());
+
+ // Update subscription groups that are both ready, and active. For a group to be
+ // considered active, both of the following must be true:
+ //
+ // 1. A final CARRIER_CONFIG_CHANGED (where config is for an identified carrier)
+ // broadcast must have been received for the subId
+ // 2. A active subscription (is loaded into a SIM slot) must be part of the subscription
+ // group.
+ if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX
+ && mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) {
+ activeSubGroups.add(subInfo.getGroupUuid());
+ }
+ }
+
+ final TelephonySubscriptionSnapshot newSnapshot =
+ new TelephonySubscriptionSnapshot(newSubIdToGroupMap, activeSubGroups);
+
+ // If snapshot was meaningfully updated, fire the callback
+ if (!newSnapshot.equals(mCurrentSnapshot)) {
+ mCurrentSnapshot = newSnapshot;
+ mHandler.post(
+ () -> {
+ mCallback.onNewSnapshot(newSnapshot);
+ });
+ }
+ }
+
+ /**
+ * Broadcast receiver for ACTION_CARRIER_CONFIG_CHANGED
+ *
+ * <p>The broadcast receiver is registered with mHandler, so callbacks & broadcasts are all
+ * serialized on mHandler, avoiding the need for locking.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
+ // already was for an identified carrier, we can stop waiting for initial load to complete
+ if (!ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+ return;
+ }
+
+ final int subId = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID);
+ final int slotId = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX);
+
+ if (slotId == INVALID_SIM_SLOT_INDEX) {
+ return;
+ }
+
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ final PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
+ if (mDeps.isConfigForIdentifiedCarrier(carrierConfigs)) {
+ Slog.v(TAG, String.format("SubId %s ready for SlotId %s", subId, slotId));
+ mReadySubIdsBySlotId.put(slotId, subId);
+ handleSubscriptionsChanged();
+ }
+ } else {
+ Slog.v(TAG, "Slot unloaded: " + slotId);
+ mReadySubIdsBySlotId.remove(slotId);
+ handleSubscriptionsChanged();
+ }
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setReadySubIdsBySlotId(Map<Integer, Integer> readySubIdsBySlotId) {
+ mReadySubIdsBySlotId.putAll(readySubIdsBySlotId);
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ Map<Integer, Integer> getReadySubIdsBySlotId() {
+ return Collections.unmodifiableMap(mReadySubIdsBySlotId);
+ }
+
+ /** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */
+ public static class TelephonySubscriptionSnapshot {
+ private final Map<Integer, ParcelUuid> mSubIdToGroupMap;
+ private final Set<ParcelUuid> mActiveGroups;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ TelephonySubscriptionSnapshot(
+ @NonNull Map<Integer, ParcelUuid> subIdToGroupMap,
+ @NonNull Set<ParcelUuid> activeGroups) {
+ mSubIdToGroupMap = Collections.unmodifiableMap(
+ Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null"));
+ mActiveGroups = Collections.unmodifiableSet(
+ Objects.requireNonNull(activeGroups, "activeGroups was null"));
+ }
+
+ /** Returns the active subscription groups */
+ @NonNull
+ public Set<ParcelUuid> getActiveSubscriptionGroups() {
+ return mActiveGroups;
+ }
+
+ /** Returns the Subscription Group for a given subId. */
+ @Nullable
+ public ParcelUuid getGroupForSubId(int subId) {
+ return mSubIdToGroupMap.get(subId);
+ }
+
+ /**
+ * Returns all the subIds in a given group, including available, but inactive subscriptions.
+ */
+ @NonNull
+ public Set<Integer> getAllSubIdsInGroup(ParcelUuid subGrp) {
+ final Set<Integer> subIds = new ArraySet<>();
+
+ for (Entry<Integer, ParcelUuid> entry : mSubIdToGroupMap.entrySet()) {
+ if (subGrp.equals(entry.getValue())) {
+ subIds.add(entry.getKey());
+ }
+ }
+
+ return subIds;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSubIdToGroupMap, mActiveGroups);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TelephonySubscriptionSnapshot)) {
+ return false;
+ }
+
+ final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj;
+
+ return mSubIdToGroupMap.equals(other.mSubIdToGroupMap)
+ && mActiveGroups.equals(other.mActiveGroups);
+ }
+ }
+
+ /**
+ * Interface for listening to changes in subscriptions
+ *
+ * @see TelephonySubscriptionTracker
+ */
+ public interface TelephonySubscriptionTrackerCallback {
+ /**
+ * Called when subscription information changes, and a new subscription snapshot was taken
+ *
+ * @param snapshot the snapshot of subscription information.
+ */
+ void onNewSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot);
+ }
+
+ /** External static dependencies for test injection */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class Dependencies {
+ /** Checks if the given bundle is for an identified carrier */
+ public boolean isConfigForIdentifiedCarrier(PersistableBundle bundle) {
+ return CarrierConfigManager.isConfigForIdentifiedCarrier(bundle);
+ }
+ }
+}
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 29bf374..ae9bd41 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -37,7 +37,6 @@
"java/android/net/util/NetworkConstants.java",
"java/android/net/IpMemoryStore.java",
"java/android/net/NetworkMonitorManager.java",
- "java/android/net/TcpKeepalivePacketData.java",
],
sdk_version: "module_current",
min_sdk_version: "30",
diff --git a/services/net/java/android/net/TcpKeepalivePacketData.java b/services/net/java/android/net/TcpKeepalivePacketData.java
deleted file mode 100644
index 4875c7c..0000000
--- a/services/net/java/android/net/TcpKeepalivePacketData.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net;
-
-import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.system.OsConstants;
-
-import com.android.net.module.util.IpUtils;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Objects;
-
-/**
- * Represents the actual tcp keep alive packets which will be used for hardware offload.
- * @hide
- */
-public class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
- private static final String TAG = "TcpKeepalivePacketData";
-
- /** TCP sequence number. */
- public final int tcpSeq;
-
- /** TCP ACK number. */
- public final int tcpAck;
-
- /** TCP RCV window. */
- public final int tcpWnd;
-
- /** TCP RCV window scale. */
- public final int tcpWndScale;
-
- /** IP TOS. */
- public final int ipTos;
-
- /** IP TTL. */
- public final int ipTtl;
-
- private static final int IPV4_HEADER_LENGTH = 20;
- private static final int IPV6_HEADER_LENGTH = 40;
- private static final int TCP_HEADER_LENGTH = 20;
-
- // This should only be constructed via static factory methods, such as
- // tcpKeepalivePacket.
- private TcpKeepalivePacketData(final TcpKeepalivePacketDataParcelable tcpDetails,
- final byte[] data) throws InvalidPacketException, UnknownHostException {
- super(InetAddress.getByAddress(tcpDetails.srcAddress), tcpDetails.srcPort,
- InetAddress.getByAddress(tcpDetails.dstAddress), tcpDetails.dstPort, data);
- tcpSeq = tcpDetails.seq;
- tcpAck = tcpDetails.ack;
- // In the packet, the window is shifted right by the window scale.
- tcpWnd = tcpDetails.rcvWnd;
- tcpWndScale = tcpDetails.rcvWndScale;
- ipTos = tcpDetails.tos;
- ipTtl = tcpDetails.ttl;
- }
-
- private TcpKeepalivePacketData(final InetAddress srcAddress, int srcPort,
- final InetAddress dstAddress, int dstPort, final byte[] data, int tcpSeq,
- int tcpAck, int tcpWnd, int tcpWndScale, int ipTos, int ipTtl)
- throws InvalidPacketException {
- super(srcAddress, srcPort, dstAddress, dstPort, data);
- this.tcpSeq = tcpSeq;
- this.tcpAck = tcpAck;
- this.tcpWnd = tcpWnd;
- this.tcpWndScale = tcpWndScale;
- this.ipTos = ipTos;
- this.ipTtl = ipTtl;
- }
-
- /**
- * Factory method to create tcp keepalive packet structure.
- */
- public static TcpKeepalivePacketData tcpKeepalivePacket(
- TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException {
- final byte[] packet;
- try {
- if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null)
- && (tcpDetails.srcAddress.length == 4 /* V4 IP length */)
- && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) {
- packet = buildV4Packet(tcpDetails);
- } else {
- // TODO: support ipv6
- throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
- }
- return new TcpKeepalivePacketData(tcpDetails, packet);
- } catch (UnknownHostException e) {
- throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
- }
-
- }
-
- /**
- * Build ipv4 tcp keepalive packet, not including the link-layer header.
- */
- // TODO : if this code is ever moved to the network stack, factorize constants with the ones
- // over there.
- private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) {
- final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
- ByteBuffer buf = ByteBuffer.allocate(length);
- buf.order(ByteOrder.BIG_ENDIAN);
- buf.put((byte) 0x45); // IP version and IHL
- buf.put((byte) tcpDetails.tos); // TOS
- buf.putShort((short) length);
- buf.putInt(0x00004000); // ID, flags=DF, offset
- buf.put((byte) tcpDetails.ttl); // TTL
- buf.put((byte) OsConstants.IPPROTO_TCP);
- final int ipChecksumOffset = buf.position();
- buf.putShort((short) 0); // IP checksum
- buf.put(tcpDetails.srcAddress);
- buf.put(tcpDetails.dstAddress);
- buf.putShort((short) tcpDetails.srcPort);
- buf.putShort((short) tcpDetails.dstPort);
- buf.putInt(tcpDetails.seq); // Sequence Number
- buf.putInt(tcpDetails.ack); // ACK
- buf.putShort((short) 0x5010); // TCP length=5, flags=ACK
- buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size
- final int tcpChecksumOffset = buf.position();
- buf.putShort((short) 0); // TCP checksum
- // URG is not set therefore the urgent pointer is zero.
- buf.putShort((short) 0); // Urgent pointer
-
- buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
- buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
- buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
-
- return buf.array();
- }
-
- // TODO: add buildV6Packet.
-
- @Override
- public boolean equals(@Nullable final Object o) {
- if (!(o instanceof TcpKeepalivePacketData)) return false;
- final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
- final InetAddress srcAddress = getSrcAddress();
- final InetAddress dstAddress = getDstAddress();
- return srcAddress.equals(other.getSrcAddress())
- && dstAddress.equals(other.getDstAddress())
- && getSrcPort() == other.getSrcPort()
- && getDstPort() == other.getDstPort()
- && this.tcpAck == other.tcpAck
- && this.tcpSeq == other.tcpSeq
- && this.tcpWnd == other.tcpWnd
- && this.tcpWndScale == other.tcpWndScale
- && this.ipTos == other.ipTos
- && this.ipTtl == other.ipTtl;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort(),
- tcpAck, tcpSeq, tcpWnd, tcpWndScale, ipTos, ipTtl);
- }
-
- /**
- * Parcelable Implementation.
- * Note that this object implements parcelable (and needs to keep doing this as it inherits
- * from a class that does), but should usually be parceled as a stable parcelable using
- * the toStableParcelable() and fromStableParcelable() methods.
- */
- public int describeContents() {
- return 0;
- }
-
- /** Write to parcel. */
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(getSrcAddress().getHostAddress());
- out.writeString(getDstAddress().getHostAddress());
- out.writeInt(getSrcPort());
- out.writeInt(getDstPort());
- out.writeByteArray(getPacket());
- out.writeInt(tcpSeq);
- out.writeInt(tcpAck);
- out.writeInt(tcpWnd);
- out.writeInt(tcpWndScale);
- out.writeInt(ipTos);
- out.writeInt(ipTtl);
- }
-
- private static TcpKeepalivePacketData readFromParcel(Parcel in) throws InvalidPacketException {
- InetAddress srcAddress = InetAddresses.parseNumericAddress(in.readString());
- InetAddress dstAddress = InetAddresses.parseNumericAddress(in.readString());
- int srcPort = in.readInt();
- int dstPort = in.readInt();
- byte[] packet = in.createByteArray();
- int tcpSeq = in.readInt();
- int tcpAck = in.readInt();
- int tcpWnd = in.readInt();
- int tcpWndScale = in.readInt();
- int ipTos = in.readInt();
- int ipTtl = in.readInt();
- return new TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, packet, tcpSeq,
- tcpAck, tcpWnd, tcpWndScale, ipTos, ipTtl);
- }
-
- /** Parcelable Creator. */
- public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
- new Parcelable.Creator<TcpKeepalivePacketData>() {
- public TcpKeepalivePacketData createFromParcel(Parcel in) {
- try {
- return readFromParcel(in);
- } catch (InvalidPacketException e) {
- throw new IllegalArgumentException(
- "Invalid NAT-T keepalive data: " + e.getError());
- }
- }
-
- public TcpKeepalivePacketData[] newArray(int size) {
- return new TcpKeepalivePacketData[size];
- }
- };
-
- /**
- * Convert this TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
- */
- @NonNull
- public TcpKeepalivePacketDataParcelable toStableParcelable() {
- final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
- final InetAddress srcAddress = getSrcAddress();
- final InetAddress dstAddress = getDstAddress();
- parcel.srcAddress = srcAddress.getAddress();
- parcel.srcPort = getSrcPort();
- parcel.dstAddress = dstAddress.getAddress();
- parcel.dstPort = getDstPort();
- parcel.seq = tcpSeq;
- parcel.ack = tcpAck;
- parcel.rcvWnd = tcpWnd;
- parcel.rcvWndScale = tcpWndScale;
- parcel.tos = ipTos;
- parcel.ttl = ipTtl;
- return parcel;
- }
-
- @Override
- public String toString() {
- return "saddr: " + getSrcAddress()
- + " daddr: " + getDstAddress()
- + " sport: " + getSrcPort()
- + " dport: " + getDstPort()
- + " seq: " + tcpSeq
- + " ack: " + tcpAck
- + " wnd: " + tcpWnd
- + " wndScale: " + tcpWndScale
- + " tos: " + ipTos
- + " ttl: " + ipTtl;
- }
-}
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
index db464e7..274b6dc 100644
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ b/services/net/java/android/net/ip/IpClientManager.java
@@ -21,6 +21,7 @@
import android.net.NattKeepalivePacketData;
import android.net.ProxyInfo;
import android.net.TcpKeepalivePacketData;
+import android.net.TcpKeepalivePacketDataParcelable;
import android.net.shared.Layer2Information;
import android.net.shared.ProvisioningConfiguration;
import android.net.util.KeepalivePacketDataUtil;
@@ -215,9 +216,20 @@
* Add a TCP keepalive packet filter before setting up keepalive offload.
*/
public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketData pkt) {
+ return addKeepalivePacketFilter(slot, KeepalivePacketDataUtil.toStableParcelable(pkt));
+ }
+
+ /**
+ * Add a TCP keepalive packet filter before setting up keepalive offload.
+ * @deprecated This method is for use on pre-S platforms where TcpKeepalivePacketData is not
+ * system API. On newer platforms use
+ * addKeepalivePacketFilter(int, TcpKeepalivePacketData) instead.
+ */
+ @Deprecated
+ public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) {
final long token = Binder.clearCallingIdentity();
try {
- mIpClient.addKeepalivePacketFilter(slot, pkt.toStableParcelable());
+ mIpClient.addKeepalivePacketFilter(slot, pkt);
return true;
} catch (RemoteException e) {
log("Error adding Keepalive Packet Filter ", e);
diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
index 4466ea0..6e539bb 100644
--- a/services/net/java/android/net/util/KeepalivePacketDataUtil.java
+++ b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
@@ -16,20 +16,47 @@
package android.net.util;
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InvalidPacketException;
+import android.net.KeepalivePacketData;
import android.net.NattKeepalivePacketData;
import android.net.NattKeepalivePacketDataParcelable;
+import android.net.TcpKeepalivePacketData;
+import android.net.TcpKeepalivePacketDataParcelable;
+import android.os.Build;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.net.module.util.IpUtils;
import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
-/** @hide */
+/**
+ * Utility class to convert to/from keepalive data parcelables.
+ *
+ * TODO: move to networkstack-client library when it is moved to frameworks/libs/net.
+ * This class cannot go into other shared libraries as it depends on NetworkStack AIDLs.
+ * @hide
+ */
public final class KeepalivePacketDataUtil {
- /**
- * Convert this NattKeepalivePacketData to a NattKeepalivePacketDataParcelable.
+ private static final int IPV4_HEADER_LENGTH = 20;
+ private static final int IPV6_HEADER_LENGTH = 40;
+ private static final int TCP_HEADER_LENGTH = 20;
+
+ private static final String TAG = KeepalivePacketDataUtil.class.getSimpleName();
+
+ /**
+ * Convert a NattKeepalivePacketData to a NattKeepalivePacketDataParcelable.
*/
@NonNull
public static NattKeepalivePacketDataParcelable toStableParcelable(
- NattKeepalivePacketData pkt) {
+ @NonNull NattKeepalivePacketData pkt) {
final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable();
final InetAddress srcAddress = pkt.getSrcAddress();
final InetAddress dstAddress = pkt.getDstAddress();
@@ -39,4 +66,158 @@
parcel.dstPort = pkt.getDstPort();
return parcel;
}
+
+ /**
+ * Convert a TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
+ */
+ @NonNull
+ public static TcpKeepalivePacketDataParcelable toStableParcelable(
+ @NonNull TcpKeepalivePacketData pkt) {
+ final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
+ final InetAddress srcAddress = pkt.getSrcAddress();
+ final InetAddress dstAddress = pkt.getDstAddress();
+ parcel.srcAddress = srcAddress.getAddress();
+ parcel.srcPort = pkt.getSrcPort();
+ parcel.dstAddress = dstAddress.getAddress();
+ parcel.dstPort = pkt.getDstPort();
+ parcel.seq = pkt.tcpSeq;
+ parcel.ack = pkt.tcpAck;
+ parcel.rcvWnd = pkt.tcpWindow;
+ parcel.rcvWndScale = pkt.tcpWindowScale;
+ parcel.tos = pkt.ipTos;
+ parcel.ttl = pkt.ipTtl;
+ return parcel;
+ }
+
+ /**
+ * Factory method to create tcp keepalive packet structure.
+ * @hide
+ */
+ public static TcpKeepalivePacketData fromStableParcelable(
+ TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException {
+ final byte[] packet;
+ try {
+ if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null)
+ && (tcpDetails.srcAddress.length == 4 /* V4 IP length */)
+ && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) {
+ packet = buildV4Packet(tcpDetails);
+ } else {
+ // TODO: support ipv6
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
+ return new TcpKeepalivePacketData(
+ InetAddress.getByAddress(tcpDetails.srcAddress),
+ tcpDetails.srcPort,
+ InetAddress.getByAddress(tcpDetails.dstAddress),
+ tcpDetails.dstPort,
+ packet,
+ tcpDetails.seq, tcpDetails.ack, tcpDetails.rcvWnd, tcpDetails.rcvWndScale,
+ tcpDetails.tos, tcpDetails.ttl);
+ } catch (UnknownHostException e) {
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
+
+ }
+
+ /**
+ * Build ipv4 tcp keepalive packet, not including the link-layer header.
+ */
+ // TODO : if this code is ever moved to the network stack, factorize constants with the ones
+ // over there.
+ private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) {
+ final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
+ ByteBuffer buf = ByteBuffer.allocate(length);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ buf.put((byte) 0x45); // IP version and IHL
+ buf.put((byte) tcpDetails.tos); // TOS
+ buf.putShort((short) length);
+ buf.putInt(0x00004000); // ID, flags=DF, offset
+ buf.put((byte) tcpDetails.ttl); // TTL
+ buf.put((byte) OsConstants.IPPROTO_TCP);
+ final int ipChecksumOffset = buf.position();
+ buf.putShort((short) 0); // IP checksum
+ buf.put(tcpDetails.srcAddress);
+ buf.put(tcpDetails.dstAddress);
+ buf.putShort((short) tcpDetails.srcPort);
+ buf.putShort((short) tcpDetails.dstPort);
+ buf.putInt(tcpDetails.seq); // Sequence Number
+ buf.putInt(tcpDetails.ack); // ACK
+ buf.putShort((short) 0x5010); // TCP length=5, flags=ACK
+ buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size
+ final int tcpChecksumOffset = buf.position();
+ buf.putShort((short) 0); // TCP checksum
+ // URG is not set therefore the urgent pointer is zero.
+ buf.putShort((short) 0); // Urgent pointer
+
+ buf.putShort(ipChecksumOffset, com.android.net.module.util.IpUtils.ipChecksum(buf, 0));
+ buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
+ buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
+
+ return buf.array();
+ }
+
+ // TODO: add buildV6Packet.
+
+ /**
+ * Get a {@link TcpKeepalivePacketDataParcelable} from {@link KeepalivePacketData}, if the
+ * generic class actually contains TCP keepalive data.
+ *
+ * @deprecated This method is used on R platforms where android.net.TcpKeepalivePacketData was
+ * not yet system API. Newer platforms should use android.net.TcpKeepalivePacketData directly.
+ *
+ * @param data A {@link KeepalivePacketData} that may contain TCP keepalive data.
+ * @return A parcelable containing TCP keepalive data, or null if the input data does not
+ * contain TCP keepalive data.
+ */
+ @Deprecated
+ @SuppressWarnings("AndroidFrameworkCompatChange") // API version check used to Log.wtf
+ @Nullable
+ public static TcpKeepalivePacketDataParcelable parseTcpKeepalivePacketData(
+ @Nullable KeepalivePacketData data) {
+ if (data == null) return null;
+
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
+ Log.wtf(TAG, "parseTcpKeepalivePacketData should not be used after R, use "
+ + "TcpKeepalivePacketData instead.");
+ }
+
+ // Reconstruct TcpKeepalivePacketData from the packet contained in KeepalivePacketData
+ final ByteBuffer buffer = ByteBuffer.wrap(data.getPacket());
+ buffer.order(ByteOrder.BIG_ENDIAN);
+
+ // Most of the fields are accessible from the KeepalivePacketData superclass: instead of
+ // using Struct to parse everything, just extract the extra fields necessary for
+ // TcpKeepalivePacketData.
+ final int tcpSeq;
+ final int tcpAck;
+ final int wndSize;
+ final int ipTos;
+ final int ttl;
+ try {
+ // This only support IPv4, because TcpKeepalivePacketData only supports IPv4 for R and
+ // below, and this method should not be used on newer platforms.
+ tcpSeq = buffer.getInt(IPV4_HEADER_LENGTH + 4);
+ tcpAck = buffer.getInt(IPV4_HEADER_LENGTH + 8);
+ wndSize = buffer.getShort(IPV4_HEADER_LENGTH + 14);
+ ipTos = buffer.get(1);
+ ttl = buffer.get(8);
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+
+ final TcpKeepalivePacketDataParcelable p = new TcpKeepalivePacketDataParcelable();
+ p.srcAddress = data.getSrcAddress().getAddress();
+ p.srcPort = data.getSrcPort();
+ p.dstAddress = data.getDstAddress().getAddress();
+ p.dstPort = data.getDstPort();
+ p.seq = tcpSeq;
+ p.ack = tcpAck;
+ // TcpKeepalivePacketData could actually use non-zero wndScale, but this does not affect
+ // actual functionality as generated packets will be the same (no wndScale option added)
+ p.rcvWnd = wndSize;
+ p.rcvWndScale = 0;
+ p.tos = ipTos;
+ p.ttl = ttl;
+ return p;
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OWNERS b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/OWNERS b/services/tests/mockingservicestests/src/com/android/server/location/OWNERS
new file mode 100644
index 0000000..696a0c2
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/OWNERS
@@ -0,0 +1 @@
+file:/location/java/android/location/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
new file mode 100644
index 0000000..d825dfd
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pm/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index a9cef20..609af8d 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -21,6 +21,8 @@
import android.media.AudioSystem;
import android.util.Log;
+import java.util.List;
+
/**
* Provides an adapter for AudioSystem that does nothing.
* Overridden methods can be configured.
@@ -66,13 +68,13 @@
}
@Override
- public int setPreferredDeviceForStrategy(int strategy,
- @NonNull AudioDeviceAttributes device) {
+ public int setDevicesRoleForStrategy(int strategy, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
return AudioSystem.AUDIO_STATUS_OK;
}
@Override
- public int removePreferredDeviceForStrategy(int strategy) {
+ public int removeDevicesRoleForStrategy(int strategy, int role) {
return AudioSystem.AUDIO_STATUS_OK;
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/OWNERS b/services/tests/servicestests/src/com/android/server/audio/OWNERS
new file mode 100644
index 0000000..894a1f5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/audio/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index e588370..8c63bfc 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -190,6 +190,22 @@
}
@Test
+ public void testIsChangeEnabledForInvalidApp() throws Exception {
+ final long disabledChangeId = 1234L;
+ final long enabledChangeId = 1235L;
+ final long targetSdkChangeId = 1236L;
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addEnabledChangeWithId(enabledChangeId)
+ .addDisabledChangeWithId(disabledChangeId)
+ .addEnableSinceSdkChangeWithId(42, targetSdkChangeId)
+ .build();
+
+ assertThat(compatConfig.isChangeEnabled(enabledChangeId, null)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId, null)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(targetSdkChangeId, null)).isTrue();
+ }
+
+ @Test
public void testPreventAddOverride() throws Exception {
final long changeId = 1234L;
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 5d8131f..d905b69 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -28,6 +28,8 @@
import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import android.content.Context;
import android.hardware.tv.cec.V1_0.SendMessageResult;
@@ -189,4 +191,19 @@
mTestLooper.dispatchAll();
assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
}
+
+ @Test
+ public void testIsLanguage() {
+ assertTrue(HdmiCecController.isLanguage("en"));
+ assertTrue(HdmiCecController.isLanguage("eng"));
+ assertTrue(HdmiCecController.isLanguage("ger"));
+ assertTrue(HdmiCecController.isLanguage("zh"));
+ assertTrue(HdmiCecController.isLanguage("zhi"));
+ assertTrue(HdmiCecController.isLanguage("zho"));
+
+ assertFalse(HdmiCecController.isLanguage(null));
+ assertFalse(HdmiCecController.isLanguage(""));
+ assertFalse(HdmiCecController.isLanguage("e"));
+ assertFalse(HdmiCecController.isLanguage("一")); // language code must be ASCII
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 24a01003..ae7f422 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -121,6 +121,16 @@
}
@Test
+ public void isValid_systemAudioModeRequest() {
+ assertMessageValidity("40:70:00:00").isEqualTo(OK);
+ assertMessageValidity("40:70").isEqualTo(OK);
+
+ assertMessageValidity("F0:70").isEqualTo(ERROR_SOURCE);
+ // Invalid physical address
+ assertMessageValidity("40:70:10:10").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
public void isValid_setSystemAudioMode() {
assertMessageValidity("40:72:00").isEqualTo(OK);
assertMessageValidity("4F:72:01:03").isEqualTo(OK);
@@ -340,6 +350,8 @@
assertMessageValidity("40:A2:14:09:12:28:4B:19:10:08:10:00").isEqualTo(ERROR_PARAMETER);
// Invalid External PLug
assertMessageValidity("04:A1:0C:08:15:05:04:1E:02:04:00").isEqualTo(ERROR_PARAMETER);
+ // Invalid Physical Address
+ assertMessageValidity("40:A2:14:09:12:28:4B:19:10:05:10:10").isEqualTo(ERROR_PARAMETER);
}
@Test
@@ -476,6 +488,51 @@
}
@Test
+ public void isValid_tunerDeviceStatus() {
+ // Displaying digital tuner
+ assertMessageValidity("40:07:00:00:11:CE:90:0F:00:78").isEqualTo(OK);
+ assertMessageValidity("40:07:80:10:13:0B:34:38").isEqualTo(OK);
+ assertMessageValidity("40:07:00:9A:06:F9:D3:E6").isEqualTo(OK);
+ assertMessageValidity("40:07:00:91:09:F4:40:C8").isEqualTo(OK);
+ // Not displaying tuner
+ assertMessageValidity("40:07:01").isEqualTo(OK);
+ assertMessageValidity("40:07:81:07:64:B9:02").isEqualTo(OK);
+ // Displaying analogue tuner
+ assertMessageValidity("40:07:02:00:13:0F:00:96").isEqualTo(OK);
+ assertMessageValidity("40:07:82:02:EA:60:1F").isEqualTo(OK);
+
+ assertMessageValidity("4F:07:00:00:11:CE:90:0F:00:78").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("F0:07:82:02:EA:60:1F").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("40:07").isEqualTo(ERROR_PARAMETER_SHORT);
+
+ // Invalid display info
+ assertMessageValidity("40:07:09:A1:8C:17:51").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:07:A7:0C:29").isEqualTo(ERROR_PARAMETER);
+ // Invalid Digital Broadcast System
+ assertMessageValidity("40:07:00:14:11:CE:90:0F:00:78").isEqualTo(ERROR_PARAMETER);
+ // Invalid Digital Broadcast System
+ assertMessageValidity("40:07:80:A0:07:95:F1").isEqualTo(ERROR_PARAMETER);
+ // Insufficient data for ARIB Broadcast system
+ assertMessageValidity("40:07:00:00:11:CE:90:0F:00").isEqualTo(ERROR_PARAMETER);
+ // Insufficient data for ATSC Broadcast system
+ assertMessageValidity("40:07:80:10:13:0B:34").isEqualTo(ERROR_PARAMETER);
+ // Insufficient data for DVB Broadcast system
+ assertMessageValidity("40:07:00:18:BE:77:00:7D:01").isEqualTo(ERROR_PARAMETER);
+ // Invalid channel number format
+ assertMessageValidity("40:07:80:9A:10:F9:D3").isEqualTo(ERROR_PARAMETER);
+ // Insufficient data for 1 part channel number
+ assertMessageValidity("40:07:00:90:04:F7").isEqualTo(ERROR_PARAMETER);
+ // Insufficient data for 2 part channel number
+ assertMessageValidity("40:07:80:91:09:F4:40").isEqualTo(ERROR_PARAMETER);
+ // Invalid Analogue Broadcast type
+ assertMessageValidity("40:07:02:03:EA:60:1F").isEqualTo(ERROR_PARAMETER);
+ // Invalid Analogue Frequency
+ assertMessageValidity("40:07:82:00:FF:FF:00").isEqualTo(ERROR_PARAMETER);
+ // Invalid Broadcast system
+ assertMessageValidity("40:07:02:02:EA:60:20").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
public void isValid_UserControlPressed() {
assertMessageValidity("40:44:07").isEqualTo(OK);
assertMessageValidity("40:44:52:A7").isEqualTo(OK);
@@ -518,6 +575,59 @@
assertMessageValidity("40:44:57:40").isEqualTo(ERROR_PARAMETER);
}
+ @Test
+ public void isValid_physicalAddress() {
+ assertMessageValidity("4F:82:10:00").isEqualTo(OK);
+ assertMessageValidity("4F:82:12:34").isEqualTo(OK);
+ assertMessageValidity("0F:82:00:00").isEqualTo(OK);
+ assertMessageValidity("40:9D:14:00").isEqualTo(OK);
+ assertMessageValidity("40:9D:10:00").isEqualTo(OK);
+ assertMessageValidity("0F:81:44:20").isEqualTo(OK);
+ assertMessageValidity("4F:81:13:10").isEqualTo(OK);
+ assertMessageValidity("4F:86:14:14").isEqualTo(OK);
+ assertMessageValidity("0F:86:15:24").isEqualTo(OK);
+
+ assertMessageValidity("4F:82:10").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:9D:14").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("0F:81:44").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("0F:86:15").isEqualTo(ERROR_PARAMETER_SHORT);
+
+ assertMessageValidity("4F:82:10:10").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("4F:82:10:06").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:9D:14:04").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:9D:10:01").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("0F:81:44:02").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("4F:81:13:05").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("4F:86:10:14").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("0F:86:10:24").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
+ public void isValid_reportPhysicalAddress() {
+ assertMessageValidity("4F:84:10:00:04").isEqualTo(OK);
+ assertMessageValidity("0F:84:00:00:00").isEqualTo(OK);
+
+ assertMessageValidity("4F:84:10:00").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("0F:84:00").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:84:10:00:04").isEqualTo(ERROR_DESTINATION);
+ // Invalid Physical Address
+ assertMessageValidity("4F:84:10:10:04").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("0F:84:00:30:00").isEqualTo(ERROR_PARAMETER);
+ // Invalid Device Type
+ assertMessageValidity("4F:84:12:34:08").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
+ public void isValid_routingChange() {
+ assertMessageValidity("0F:80:10:00:40:00").isEqualTo(OK);
+ assertMessageValidity("4F:80:12:00:50:00").isEqualTo(OK);
+
+ assertMessageValidity("0F:80:10:00:40").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:80:12:00:50:00").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("0F:80:10:01:40:00").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("4F:80:12:00:50:50").isEqualTo(ERROR_PARAMETER);
+ }
+
private IntegerSubject assertMessageValidity(String message) {
return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message)));
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index c4d1211..98d6452 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -95,13 +95,24 @@
static class MockInjector extends RebootEscrowManager.Injector {
private final IRebootEscrow mRebootEscrow;
+ private final RebootEscrowProviderInterface mRebootEscrowProvider;
private final UserManager mUserManager;
private final MockableRebootEscrowInjected mInjected;
- MockInjector(Context context, UserManager userManager, IRebootEscrow rebootEscrow,
+ MockInjector(Context context, UserManager userManager,
+ IRebootEscrow rebootEscrow,
MockableRebootEscrowInjected injected) {
super(context);
mRebootEscrow = rebootEscrow;
+
+ RebootEscrowProviderHalImpl.Injector halInjector =
+ new RebootEscrowProviderHalImpl.Injector() {
+ @Override
+ public IRebootEscrow getRebootEscrow() {
+ return mRebootEscrow;
+ }
+ };
+ mRebootEscrowProvider = new RebootEscrowProviderHalImpl(halInjector);
mUserManager = userManager;
mInjected = injected;
}
@@ -112,8 +123,8 @@
}
@Override
- public IRebootEscrow getRebootEscrow() {
- return mRebootEscrow;
+ public RebootEscrowProviderInterface getRebootEscrowProvider() {
+ return mRebootEscrowProvider;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/OWNERS b/services/tests/servicestests/src/com/android/server/recoverysystem/OWNERS
new file mode 100644
index 0000000..3880038
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/recoverysystem/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index 035a2f1..b07b8fa 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
@@ -33,6 +34,7 @@
import android.content.Context;
import android.content.IntentSender;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IRecoverySystemProgressListener;
@@ -67,6 +69,9 @@
private FileWriter mUncryptUpdateFileWriter;
private LockSettingsInternal mLockSettingsInternal;
+ private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.package";
+ private static final String FAKE_OTHER_PACKAGE_NAME = "fake.other.package";
+
@Before
public void setup() {
mContext = mock(Context.class);
@@ -209,65 +214,99 @@
@Test(expected = SecurityException.class)
public void requestLskf_protected() {
- doThrow(SecurityException.class).when(mContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.RECOVERY), any());
- mRecoverySystemService.requestLskf("test", null);
- }
-
-
- @Test
- public void requestLskf_nullToken_failure() {
- assertThat(mRecoverySystemService.requestLskf(null, null), is(false));
+ when(mContext.checkCallingOrSelfPermission(anyString())).thenReturn(
+ PackageManager.PERMISSION_DENIED);
+ mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null);
}
@Test
public void requestLskf_success() throws Exception {
IntentSender intentSender = mock(IntentSender.class);
- assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
+ is(true));
mRecoverySystemService.onPreparedForReboot(true);
verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
}
@Test
- public void requestLskf_subsequentRequestClearsPrepared() throws Exception {
+ public void requestLskf_subsequentRequestNotClearPrepared() throws Exception {
IntentSender intentSender = mock(IntentSender.class);
- assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
+ is(true));
mRecoverySystemService.onPreparedForReboot(true);
verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
- assertThat(mRecoverySystemService.requestLskf("test2", null), is(true));
- assertThat(mRecoverySystemService.rebootWithLskf("test", null), is(false));
- assertThat(mRecoverySystemService.rebootWithLskf("test2", "foobar"), is(false));
-
- mRecoverySystemService.onPreparedForReboot(true);
- assertThat(mRecoverySystemService.rebootWithLskf("test2", "foobar"), is(true));
- verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
+ assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "foobar", true),
+ is(true));
verify(mIPowerManager).reboot(anyBoolean(), eq("foobar"), anyBoolean());
}
-
@Test
public void requestLskf_requestedButNotPrepared() throws Exception {
IntentSender intentSender = mock(IntentSender.class);
- assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
+ is(true));
verify(intentSender, never()).sendIntent(any(), anyInt(), any(), any(), any());
}
+ @Test
+ public void isLskfCaptured_requestedButNotPrepared() throws Exception {
+ IntentSender intentSender = mock(IntentSender.class);
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
+ is(true));
+ assertThat(mRecoverySystemService.isLskfCaptured(FAKE_OTA_PACKAGE_NAME), is(false));
+ }
+
+ @Test
+ public void isLskfCaptured_Prepared() throws Exception {
+ IntentSender intentSender = mock(IntentSender.class);
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
+ is(true));
+ mRecoverySystemService.onPreparedForReboot(true);
+ verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+ assertThat(mRecoverySystemService.isLskfCaptured(FAKE_OTA_PACKAGE_NAME), is(true));
+ }
+
@Test(expected = SecurityException.class)
public void clearLskf_protected() {
- doThrow(SecurityException.class).when(mContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.RECOVERY), any());
- mRecoverySystemService.clearLskf();
+ when(mContext.checkCallingOrSelfPermission(anyString())).thenReturn(
+ PackageManager.PERMISSION_DENIED);
+ mRecoverySystemService.clearLskf(FAKE_OTA_PACKAGE_NAME);
}
@Test
public void clearLskf_requestedThenCleared() throws Exception {
IntentSender intentSender = mock(IntentSender.class);
- assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
+ is(true));
mRecoverySystemService.onPreparedForReboot(true);
verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
- assertThat(mRecoverySystemService.clearLskf(), is(true));
+ assertThat(mRecoverySystemService.clearLskf(FAKE_OTA_PACKAGE_NAME), is(true));
+ verify(mLockSettingsInternal).clearRebootEscrow();
+ }
+
+ @Test
+ public void clearLskf_callerNotRequested_Success() throws Exception {
+ IntentSender intentSender = mock(IntentSender.class);
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
+ is(true));
+ assertThat(mRecoverySystemService.clearLskf(FAKE_OTHER_PACKAGE_NAME), is(true));
+ verify(mLockSettingsInternal, never()).clearRebootEscrow();
+ }
+
+ @Test
+ public void clearLskf_multiClient_BothClientsClear() throws Exception {
+ IntentSender intentSender = mock(IntentSender.class);
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
+ is(true));
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, intentSender),
+ is(true));
+
+ assertThat(mRecoverySystemService.clearLskf(FAKE_OTA_PACKAGE_NAME), is(true));
+ verify(mLockSettingsInternal, never()).clearRebootEscrow();
+ assertThat(mRecoverySystemService.clearLskf(FAKE_OTHER_PACKAGE_NAME), is(true));
verify(mLockSettingsInternal).clearRebootEscrow();
}
@@ -279,27 +318,84 @@
@Test(expected = SecurityException.class)
public void rebootWithLskf_protected() {
- doThrow(SecurityException.class).when(mContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.RECOVERY), any());
- mRecoverySystemService.rebootWithLskf("test1", null);
+ when(mContext.checkCallingOrSelfPermission(anyString())).thenReturn(
+ PackageManager.PERMISSION_DENIED);
+ mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true);
}
@Test
public void rebootWithLskf_Success() throws Exception {
- assertThat(mRecoverySystemService.requestLskf("test", null), is(true));
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
mRecoverySystemService.onPreparedForReboot(true);
- assertThat(mRecoverySystemService.rebootWithLskf("test", "ab-update"), is(true));
+ assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
+ is(true));
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
}
@Test
public void rebootWithLskf_withoutPrepare_Failure() throws Exception {
- assertThat(mRecoverySystemService.rebootWithLskf("test1", null), is(false));
+ assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
+ is(false));
}
@Test
- public void rebootWithLskf_withNullUpdateToken_Failure() throws Exception {
- assertThat(mRecoverySystemService.rebootWithLskf(null, null), is(false));
+ public void rebootWithLskf_withNullCallerId_Failure() throws Exception {
+ assertThat(mRecoverySystemService.rebootWithLskf(null, null, true), is(false));
verifyNoMoreInteractions(mIPowerManager);
}
+
+ @Test
+ public void rebootWithLskf_multiClient_ClientASuccess() throws Exception {
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true));
+ mRecoverySystemService.onPreparedForReboot(true);
+
+ // Client B's clear won't affect client A's preparation.
+ assertThat(mRecoverySystemService.clearLskf(FAKE_OTHER_PACKAGE_NAME), is(true));
+ assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
+ is(true));
+ verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
+ }
+
+
+ @Test
+ public void rebootWithLskf_multiClient_ClientBSuccess() throws Exception {
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
+ mRecoverySystemService.onPreparedForReboot(true);
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true));
+
+ assertThat(mRecoverySystemService.clearLskf(FAKE_OTA_PACKAGE_NAME), is(true));
+ assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
+ is(false));
+ verifyNoMoreInteractions(mIPowerManager);
+
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true));
+ assertThat(
+ mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true),
+ is(true));
+ verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
+ }
+
+ @Test
+ public void rebootWithLskf_multiClient_BothClientsClear_Failure() throws Exception {
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
+ mRecoverySystemService.onPreparedForReboot(true);
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true));
+
+ // Client A clears
+ assertThat(mRecoverySystemService.clearLskf(FAKE_OTA_PACKAGE_NAME), is(true));
+ assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
+ is(false));
+ verifyNoMoreInteractions(mIPowerManager);
+
+ // Client B clears
+ assertThat(mRecoverySystemService.clearLskf(FAKE_OTHER_PACKAGE_NAME), is(true));
+ verify(mLockSettingsInternal).clearRebootEscrow();
+ assertThat(
+ mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true),
+ is(false));
+ verifyNoMoreInteractions(mIPowerManager);
+ }
+
+ // TODO(xunchang) add more multi client tests
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS b/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
index 09447a97..8f80897 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 22addf9..f0b1be5 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -172,6 +173,36 @@
mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion);
}
+ @Test(expected = SecurityException.class)
+ public void testSuggestGnssTime_withoutPermission() {
+ doThrow(new SecurityException("Mock"))
+ .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion();
+
+ try {
+ mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion);
+ fail();
+ } finally {
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SET_TIME), anyString());
+ }
+ }
+
+ @Test
+ public void testSuggestGnssTime() throws Exception {
+ doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+
+ GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion();
+ mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion);
+ mTestHandler.assertTotalMessagesEnqueued(1);
+
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SET_TIME), anyString());
+
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeDetectorStrategy.verifySuggestGnssTimeCalled(gnssTimeSuggestion);
+ }
+
@Test
public void testDump() {
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
@@ -216,12 +247,18 @@
return new NetworkTimeSuggestion(timeValue);
}
+ private static GnssTimeSuggestion createGnssTimeSuggestion() {
+ TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
+ return new GnssTimeSuggestion(timeValue);
+ }
+
private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
// Call tracking.
private TelephonyTimeSuggestion mLastTelephonySuggestion;
private ManualTimeSuggestion mLastManualSuggestion;
private NetworkTimeSuggestion mLastNetworkSuggestion;
+ private GnssTimeSuggestion mLastGnssSuggestion;
private boolean mHandleAutoTimeDetectionChangedCalled;
private boolean mDumpCalled;
@@ -242,6 +279,11 @@
}
@Override
+ public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) {
+ mLastGnssSuggestion = timeSuggestion;
+ }
+
+ @Override
public void handleAutoTimeConfigChanged() {
mHandleAutoTimeDetectionChangedCalled = true;
}
@@ -255,6 +297,7 @@
mLastTelephonySuggestion = null;
mLastManualSuggestion = null;
mLastNetworkSuggestion = null;
+ mLastGnssSuggestion = null;
mHandleAutoTimeDetectionChangedCalled = false;
mDumpCalled = false;
}
@@ -271,6 +314,10 @@
assertEquals(expectedSuggestion, mLastNetworkSuggestion);
}
+ void verifySuggestGnssTimeCalled(GnssTimeSuggestion expectedSuggestion) {
+ assertEquals(expectedSuggestion, mLastGnssSuggestion);
+ }
+
void verifyHandleAutoTimeDetectionChangedCalled() {
assertTrue(mHandleAutoTimeDetectionChangedCalled);
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index 21396fd..b1adb0b 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -16,6 +16,7 @@
package com.android.server.timedetector;
+import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_GNSS;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY;
@@ -25,6 +26,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -569,7 +571,53 @@
}
@Test
- public void highPrioritySuggestionsShouldBeatLowerPrioritySuggestions() {
+ public void testSuggestGnssTime_autoTimeEnabled() {
+ mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+ .pokeAutoOriginPriorities(ORIGIN_GNSS)
+ .pokeAutoTimeDetectionEnabled(true);
+
+ GnssTimeSuggestion timeSuggestion =
+ mScript.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME);
+
+ mScript.simulateTimePassing();
+
+ long expectedSystemClockMillis =
+ mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
+ mScript.simulateGnssTimeSuggestion(timeSuggestion)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
+ }
+
+ @Test
+ public void testSuggestGnssTime_autoTimeDisabled() {
+ mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+ .pokeAutoOriginPriorities(ORIGIN_GNSS)
+ .pokeAutoTimeDetectionEnabled(false);
+
+ GnssTimeSuggestion timeSuggestion =
+ mScript.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME);
+
+ mScript.simulateTimePassing()
+ .simulateGnssTimeSuggestion(timeSuggestion)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+ }
+
+ @Test
+ public void gnssTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() {
+ mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+ .pokeAutoOriginPriorities(ORIGIN_GNSS)
+ .pokeAutoTimeDetectionEnabled(true);
+
+ Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1));
+ GnssTimeSuggestion timeSuggestion = mScript
+ .generateGnssTimeSuggestion(suggestedTime);
+
+ mScript.simulateGnssTimeSuggestion(timeSuggestion)
+ .verifySystemClockWasNotSetAndResetCallTracking()
+ .assertLatestGnssSuggestion(null);
+ }
+
+ @Test
+ public void highPrioritySuggestionsBeatLowerPrioritySuggestions_telephonyNetworkOrigins() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK);
@@ -672,22 +720,130 @@
}
@Test
+ public void highPrioritySuggestionsBeatLowerPrioritySuggestions_networkGnssOrigins() {
+ mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+ .pokeAutoTimeDetectionEnabled(true)
+ .pokeAutoOriginPriorities(ORIGIN_NETWORK, ORIGIN_GNSS);
+
+ // Three obviously different times that could not be mistaken for each other.
+ Instant gnssTime1 = ARBITRARY_TEST_TIME;
+ Instant gnssTime2 = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
+ Instant networkTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60));
+ // A small increment used to simulate the passage of time, but not enough to interfere with
+ // macro-level time changes associated with suggestion age.
+ final long smallTimeIncrementMillis = 101;
+
+ // A gnss suggestion is made. It should be used because there is no network suggestion.
+ GnssTimeSuggestion gnssTimeSuggestion1 =
+ mScript.generateGnssTimeSuggestion(gnssTime1);
+ mScript.simulateTimePassing(smallTimeIncrementMillis)
+ .simulateGnssTimeSuggestion(gnssTimeSuggestion1)
+ .verifySystemClockWasSetAndResetCallTracking(
+ mScript.calculateTimeInMillisForNow(gnssTimeSuggestion1.getUtcTime()));
+
+ // Check internal state.
+ mScript.assertLatestNetworkSuggestion(null)
+ .assertLatestGnssSuggestion(gnssTimeSuggestion1);
+ assertEquals(gnssTimeSuggestion1, mScript.peekLatestValidGnssSuggestion());
+ assertNull("No network suggestions were made:", mScript.peekLatestValidNetworkSuggestion());
+
+ // Simulate a little time passing.
+ mScript.simulateTimePassing(smallTimeIncrementMillis)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Now a network suggestion is made. Network suggestions are prioritized over gnss
+ // suggestions so it should "win".
+ NetworkTimeSuggestion networkTimeSuggestion =
+ mScript.generateNetworkTimeSuggestion(networkTime);
+ mScript.simulateTimePassing(smallTimeIncrementMillis)
+ .simulateNetworkTimeSuggestion(networkTimeSuggestion)
+ .verifySystemClockWasSetAndResetCallTracking(
+ mScript.calculateTimeInMillisForNow(networkTimeSuggestion.getUtcTime()));
+
+ // Check internal state.
+ mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
+ .assertLatestGnssSuggestion(gnssTimeSuggestion1);
+ assertEquals(gnssTimeSuggestion1, mScript.peekLatestValidGnssSuggestion());
+ assertEquals(networkTimeSuggestion, mScript.peekLatestValidNetworkSuggestion());
+
+ // Simulate some significant time passing: half the time allowed before a time signal
+ // becomes "too old to use".
+ mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Now another gnss suggestion is made. Network suggestions are prioritized over
+ // gnss suggestions so the latest network suggestion should still "win".
+ GnssTimeSuggestion gnssTimeSuggestion2 =
+ mScript.generateGnssTimeSuggestion(gnssTime2);
+ mScript.simulateTimePassing(smallTimeIncrementMillis)
+ .simulateGnssTimeSuggestion(gnssTimeSuggestion2)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Check internal state.
+ mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
+ .assertLatestGnssSuggestion(gnssTimeSuggestion2);
+ assertEquals(gnssTimeSuggestion2, mScript.peekLatestValidGnssSuggestion());
+ assertEquals(networkTimeSuggestion, mScript.peekLatestValidNetworkSuggestion());
+
+ // Simulate some significant time passing: half the time allowed before a time signal
+ // becomes "too old to use". This should mean that telephonyTimeSuggestion is now too old to
+ // be used but networkTimeSuggestion2 is not.
+ mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2);
+
+ // NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last
+ // suggestion it used becomes too old: it requires a new suggestion or an auto-time toggle
+ // to re-run the detection logic. This may change in future but until then we rely on a
+ // steady stream of suggestions to re-evaluate.
+ mScript.verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Check internal state.
+ mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
+ .assertLatestGnssSuggestion(gnssTimeSuggestion2);
+ assertEquals(gnssTimeSuggestion2, mScript.peekLatestValidGnssSuggestion());
+ assertNull(
+ "Network suggestion should be expired:",
+ mScript.peekLatestValidNetworkSuggestion());
+
+ // Toggle auto-time off and on to force the detection logic to run.
+ mScript.simulateAutoTimeDetectionToggle()
+ .simulateTimePassing(smallTimeIncrementMillis)
+ .simulateAutoTimeDetectionToggle();
+
+ // Verify the latest gnss time now wins.
+ mScript.verifySystemClockWasSetAndResetCallTracking(
+ mScript.calculateTimeInMillisForNow(gnssTimeSuggestion2.getUtcTime()));
+
+ // Check internal state.
+ mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
+ .assertLatestGnssSuggestion(gnssTimeSuggestion2);
+ assertEquals(gnssTimeSuggestion2, mScript.peekLatestValidGnssSuggestion());
+ assertNull(
+ "Network suggestion should still be expired:",
+ mScript.peekLatestValidNetworkSuggestion());
+ }
+
+ @Test
public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_lowerPriorityComesFirst() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
- .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK);
+ .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK, ORIGIN_GNSS);
Instant networkTime = ARBITRARY_TEST_TIME;
- Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
+ Instant gnssTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
+ Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60));
NetworkTimeSuggestion networkTimeSuggestion =
mScript.generateNetworkTimeSuggestion(networkTime);
+ GnssTimeSuggestion gnssTimeSuggestion =
+ mScript.generateGnssTimeSuggestion(gnssTime);
TelephonyTimeSuggestion telephonyTimeSuggestion =
mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTime);
mScript.simulateNetworkTimeSuggestion(networkTimeSuggestion)
+ .simulateGnssTimeSuggestion(gnssTimeSuggestion)
.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion)
.assertLatestNetworkSuggestion(networkTimeSuggestion)
+ .assertLatestGnssSuggestion(gnssTimeSuggestion)
.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(telephonyTime.toEpochMilli());
}
@@ -696,20 +852,25 @@
public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_higherPriorityComesFirst() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
- .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK);
+ .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK, ORIGIN_GNSS);
Instant networkTime = ARBITRARY_TEST_TIME;
Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
+ Instant gnssTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60));
NetworkTimeSuggestion networkTimeSuggestion =
mScript.generateNetworkTimeSuggestion(networkTime);
TelephonyTimeSuggestion telephonyTimeSuggestion =
mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTime);
+ GnssTimeSuggestion gnssTimeSuggestion =
+ mScript.generateGnssTimeSuggestion(gnssTime);
mScript.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion)
.simulateNetworkTimeSuggestion(networkTimeSuggestion)
+ .simulateGnssTimeSuggestion(gnssTimeSuggestion)
.assertLatestNetworkSuggestion(networkTimeSuggestion)
.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion)
+ .assertLatestGnssSuggestion(gnssTimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(telephonyTime.toEpochMilli());
}
@@ -728,7 +889,37 @@
}
@Test
- public void suggestionsFromSourceNotListedInPrioritiesList_areIgnored() {
+ public void whenHigherPrioritySuggestionsAreNotAvailable_fallbacksToNext() {
+ mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+ .pokeAutoTimeDetectionEnabled(true)
+ .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK, ORIGIN_GNSS);
+
+ GnssTimeSuggestion timeSuggestion =
+ mScript.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME);
+
+ mScript.simulateGnssTimeSuggestion(timeSuggestion)
+ .assertLatestGnssSuggestion(timeSuggestion)
+ .verifySystemClockWasSetAndResetCallTracking(ARBITRARY_TEST_TIME.toEpochMilli());
+ }
+
+ @Test
+ public void suggestionsFromTelephonyOriginNotInPriorityList_areIgnored() {
+ mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+ .pokeAutoTimeDetectionEnabled(true)
+ .pokeAutoOriginPriorities(ORIGIN_NETWORK);
+
+ int slotIndex = ARBITRARY_SLOT_INDEX;
+ Instant testTime = ARBITRARY_TEST_TIME;
+ TelephonyTimeSuggestion timeSuggestion =
+ mScript.generateTelephonyTimeSuggestion(slotIndex, testTime);
+
+ mScript.simulateTelephonyTimeSuggestion(timeSuggestion)
+ .assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, timeSuggestion)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+ }
+
+ @Test
+ public void suggestionsFromNetworkOriginNotInPriorityList_areIgnored() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY);
@@ -742,6 +933,20 @@
}
@Test
+ public void suggestionsFromGnssOriginNotInPriorityList_areIgnored() {
+ mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+ .pokeAutoTimeDetectionEnabled(true)
+ .pokeAutoOriginPriorities(ORIGIN_TELEPHONY);
+
+ GnssTimeSuggestion timeSuggestion = mScript.generateGnssTimeSuggestion(
+ ARBITRARY_TEST_TIME);
+
+ mScript.simulateGnssTimeSuggestion(timeSuggestion)
+ .assertLatestGnssSuggestion(timeSuggestion)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+ }
+
+ @Test
public void autoOriginPrioritiesList_doesNotAffectManualSuggestion() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(false)
@@ -945,6 +1150,11 @@
return this;
}
+ Script simulateGnssTimeSuggestion(GnssTimeSuggestion timeSuggestion) {
+ mTimeDetectorStrategy.suggestGnssTime(timeSuggestion);
+ return this;
+ }
+
Script simulateAutoTimeDetectionToggle() {
mFakeCallback.simulateAutoTimeZoneDetectionToggle();
mTimeDetectorStrategy.handleAutoTimeConfigChanged();
@@ -995,6 +1205,14 @@
}
/**
+ * White box test info: Asserts the latest gnss suggestion is as expected.
+ */
+ Script assertLatestGnssSuggestion(GnssTimeSuggestion expected) {
+ assertEquals(expected, mTimeDetectorStrategy.getLatestGnssSuggestion());
+ return this;
+ }
+
+ /**
* White box test info: Returns the telephony suggestion that would be used, if any, given
* the current elapsed real time clock and regardless of origin prioritization.
*/
@@ -1011,6 +1229,14 @@
}
/**
+ * White box test info: Returns the gnss suggestion that would be used, if any, given the
+ * current elapsed real time clock and regardless of origin prioritization.
+ */
+ GnssTimeSuggestion peekLatestValidGnssSuggestion() {
+ return mTimeDetectorStrategy.findLatestValidGnssSuggestionForTests();
+ }
+
+ /**
* Generates a ManualTimeSuggestion using the current elapsed realtime clock for the
* reference time.
*/
@@ -1057,6 +1283,18 @@
}
/**
+ * Generates a GnssTimeSuggestion using the current elapsed realtime clock for the
+ * reference time.
+ */
+ GnssTimeSuggestion generateGnssTimeSuggestion(Instant suggestedTime) {
+ TimestampedValue<Long> utcTime =
+ new TimestampedValue<>(
+ mFakeCallback.peekElapsedRealtimeMillis(),
+ suggestedTime.toEpochMilli());
+ return new GnssTimeSuggestion(utcTime);
+ }
+
+ /**
* Calculates what the supplied time would be when adjusted for the movement of the fake
* elapsed realtime clock.
*/
diff --git a/services/tests/servicestests/src/com/android/server/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/timezone/OWNERS
index 09447a97..8f80897 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/timezone/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS b/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
index 09447a97..8f80897 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
@@ -1 +1,3 @@
-include /core/java/android/app/timezone/OWNERS
+# Bug component: 847766
+mingaleev@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
new file mode 100644
index 0000000..e5d0370
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
@@ -0,0 +1,2 @@
+ytai@google.com
+elaurent@google.com
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index 3104c7e..f923721 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -34,6 +34,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.Log;
@@ -154,9 +155,27 @@
@VisibleForTesting
protected boolean isIorapEnabled() {
+ // These two mendel flags should match those in iorapd native process
+ // system/iorapd/src/common/property.h
+ boolean isTracingEnabled =
+ getMendelFlag("iorap_perfetto_enable", "iorapd.perfetto.enable", false);
+ boolean isReadAheadEnabled =
+ getMendelFlag("iorap_readahead_enable", "iorapd.readahead.enable", false);
// Same as the property in iorapd.rc -- disabling this will mean the 'iorapd' binder process
// never comes up, so all binder connections will fail indefinitely.
- return IS_ENABLED;
+ return IS_ENABLED && (isTracingEnabled || isReadAheadEnabled);
+ }
+
+ private boolean getMendelFlag(String mendelFlag, String sysProperty, boolean defaultValue) {
+ // TODO(yawanng) use DeviceConfig to get mendel property.
+ // DeviceConfig doesn't work and the reason is not clear.
+ // Provider service is already up before IORapForwardService.
+ String mendelProperty = "persist.device_config."
+ + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT
+ + "."
+ + mendelFlag;
+ return SystemProperties.getBoolean(mendelProperty,
+ SystemProperties.getBoolean(sysProperty, defaultValue));
}
//</editor-fold>
@@ -239,7 +258,9 @@
//
// TODO: it would be good to get nodified of 'adb shell stop iorapd' to avoid
// printing this warning.
- Log.w(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime);
+ if (DEBUG) {
+ Log.v(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime);
+ }
// Use a handler instead of Thread#sleep to avoid backing up the binder thread
// when this is called from the death recipient callback.
@@ -275,7 +296,9 @@
// Connect to the native binder service.
mIorapRemote = provideIorapRemote();
if (mIorapRemote == null) {
- Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?");
+ if (DEBUG) {
+ Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?");
+ }
return false;
}
invokeRemote(mIorapRemote,
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 0c46394..225e3f76 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -651,4 +651,23 @@
throw new SecurityException(message + ": Only shell user can call it");
}
+
+ /**
+ * Returns the target SDK version number for a given package name.
+ *
+ * This call MUST be invoked before clearing the calling UID.
+ *
+ * @return target SDK if the package is found or INT_MAX.
+ */
+ public static int getTargetSdk(Context c, String packageName) {
+ try {
+ final ApplicationInfo ai = c.getPackageManager().getApplicationInfoAsUser(
+ packageName, 0, UserHandle.getUserHandleForUid(Binder.getCallingUid()));
+ if (ai != null) return ai.targetSdkVersion;
+ } catch (PackageManager.NameNotFoundException unexpected) {
+ Log.e(LOG_TAG, "Failed to get package info for pkg="
+ + packageName + ", uid=" + Binder.getCallingUid());
+ }
+ return Integer.MAX_VALUE;
+ }
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 27ea690..4e9e6a8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2046,7 +2046,13 @@
* via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
* and can choose to hide or show the video calling icon based on whether a contact supports
* video.
+ *
+ * @deprecated No longer used in framework code, however it may still be used by applications
+ * that have not updated their code. This config should still be set to {@code true} if
+ * {@link Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is set to {@code true} and
+ * {@link Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL} is set to {@code true}.
*/
+ @Deprecated
public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
/**
@@ -3862,13 +3868,51 @@
* <p>
* If this key's value is set to false, the procedure for RCS contact capability exchange
* via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and
- * {@link #KEY_USE_RCS_PRESENCE_BOOL} must also be set to false to ensure apps do not
- * improperly think that capability exchange via SIP PUBLISH is enabled.
+ * {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be set to false to ensure
+ * apps do not improperly think that capability exchange via SIP PUBLISH is enabled.
* <p> The default value for this key is {@code false}.
*/
public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL =
KEY_PREFIX + "enable_presence_publish_bool";
+ /**
+ * Flag indicating whether or not this carrier supports the exchange of phone numbers with
+ * the carrier's RCS presence server in order to retrieve the RCS capabilities of requested
+ * contacts used in the RCS User Capability Exchange (UCE) procedure. See RCC.71, section 3
+ * for more information.
+ * <p>
+ * When presence is supported, the device uses the SIP SUBSCRIBE/NOTIFY procedure internally
+ * to retrieve the requested RCS capabilities. See
+ * {@link android.telephony.ims.RcsUceAdapter} for more information on how RCS capabilities
+ * can be retrieved from the carrier's network.
+ */
+ public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL =
+ KEY_PREFIX + "enable_presence_capability_exchange_bool";
+
+
+ /**
+ * Flag indicating whether or not the carrier expects the RCS UCE service to periodically
+ * refresh the RCS capabilities cache of the user's contacts as well as request the
+ * capabilities of call contacts when the SIM card is first inserted or when a new contact
+ * is added, removed, or modified. This corresponds to the RCC.07 A.19
+ * "DISABLE INITIAL ADDRESS BOOK SCAN" parameter.
+ * <p>
+ * If this flag is disabled, the capabilities cache will not be refreshed internally at all
+ * and will only be updated if the cached capabilities are stale when an application
+ * requests them.
+ */
+ public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL =
+ KEY_PREFIX + "rcs_bulk_capability_exchange_bool";
+
+ /**
+ * Flag indicating whether or not the carrier supports capability exchange with a list of
+ * contacts. When {@code true}, the device will batch together multiple requests and
+ * construct a RLMI document in the SIP SUBSCRIBE request (see RFC 4662). If {@code false},
+ * the request will be split up into one SIP SUBSCRIBE request per contact.
+ */
+ public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
+ KEY_PREFIX + "enable_presence_group_subscribe_bool";
+
private Ims() {}
private static PersistableBundle getDefaults() {
@@ -3876,6 +3920,9 @@
defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000);
defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false);
+ defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
+ defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
+ defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
return defaults;
}
}
@@ -4018,6 +4065,12 @@
public static final String KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED =
"use_lower_mtu_value_if_both_received";
+ /**
+ * Indicates if auto-configuration server is used for the RCS config
+ * Reference: GSMA RCC.14
+ */
+ public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -4561,6 +4614,7 @@
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
+ sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index db7d10a..7addf33 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -443,10 +443,12 @@
/**
* Get table index for channel quality indicator
*
+ * Reference: 3GPP TS 136.213 section 7.2.3.
+ *
* @return the CQI table index if available or
* {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
- /** @hide */
+ @IntRange(from = 1, to = 6)
public int getCqiTableIndex() {
return mCqiTableIndex;
}
@@ -454,9 +456,12 @@
/**
* Get channel quality indicator
*
+ * Reference: 3GPP TS 136.213 section 7.2.3.
+ *
* @return the CQI if available or
* {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
+ @IntRange(from = 0, to = 15)
public int getCqi() {
return mCqi;
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index 1518190..bde62fb 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -294,9 +294,10 @@
*
* Reference: 3GPP TS 138.214 section 5.2.2.1.
*
- * Range [1, 3].
+ * @return the CQI table index if available or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
- /** @hide */
+ @IntRange(from = 1, to = 3)
public int getCsiCqiTableIndex() {
return mCsiCqiTableIndex;
}
@@ -310,10 +311,10 @@
*
* Reference: 3GPP TS 138.214 section 5.2.2.1.
*
- * Range [0, 15] for each CQI.
+ * @return the CQIs for all subbands if available or empty list if unavailable.
*/
- /** @hide */
@NonNull
+ @IntRange(from = 0, to = 15)
public List<Integer> getCsiCqiReport() {
return mCsiCqiReport;
}
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index 8b7a243..d502da9 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -916,6 +916,84 @@
/** System preference change back to SRAT during handoff */
public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB;
+ //IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2).
+
+ /** The PDN connection corresponding to the requested APN has been rejected. */
+ public static final int IWLAN_PDN_CONNECTION_REJECTION = 0x2000;
+ /**
+ * The PDN connection has been rejected. No additional PDN connections can be established
+ * for the UE due to the network policies or capabilities.
+ */
+ public static final int IWLAN_MAX_CONNECTION_REACHED = 0x2001;
+ /**
+ * The PDN connection has been rejected due to a semantic error in TFT operation.
+ */
+ public static final int IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 0x2031;
+ /**
+ * The PDN connection has been rejected due to a syntactic error in TFT operation.
+ */
+ public static final int IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION = 0x2032;
+ /**
+ * The PDN connection has been rejected due to sematic errors in the packet filter.
+ */
+ public static final int IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS = 0x2034;
+ /**
+ * The PDN connection has been rejected due to syntactic errors in the packet filter.
+ */
+ public static final int IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS = 0x2035;
+ /**
+ * No non-3GPP subscription is associated with the IMSI.
+ * The UE is not allowed to use non-3GPP access to EPC.
+ */
+ public static final int IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED = 0x2328;
+ /** The user identified by the IMSI is unknown. */
+ public static final int IWLAN_USER_UNKNOWN = 0x2329;
+ /**
+ * The requested APN is not included in the user's profile,
+ * and therefore is not authorized for that user.
+ */
+ public static final int IWLAN_NO_APN_SUBSCRIPTION = 0x232A;
+ /** The user is barred from using the non-3GPP access or the subscribed APN. */
+ public static final int IWLAN_AUTHORIZATION_REJECTED = 0x232B;
+ /** The Mobile Equipment used is not acceptable to the network */
+ public static final int IWLAN_ILLEGAL_ME = 0x232E;
+ /**
+ * The network has determined that the requested procedure cannot be completed successfully
+ * due to network failure.
+ */
+ public static final int IWLAN_NETWORK_FAILURE = 0x2904;
+ /** The access type is restricted to the user. */
+ public static final int IWLAN_RAT_TYPE_NOT_ALLOWED = 0x2AF9;
+ /** The network does not accept emergency PDN bringup request using an IMEI */
+ public static final int IWLAN_IMEI_NOT_ACCEPTED = 0x2AFD;
+ /**
+ * The ePDG performs PLMN filtering (based on roaming agreements) and rejects
+ * the request from the UE.
+ * The UE requests service in a PLMN where the UE is not allowed to operate.
+ */
+ public static final int IWLAN_PLMN_NOT_ALLOWED = 0x2B03;
+ /** The ePDG does not support un-authenticated IMSI based emergency PDN bringup **/
+ public static final int IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 0x2B2F;
+
+ // Device is unable to establish an IPSec tunnel with the ePDG for any reason
+ // e.g authentication fail or certificate validation or DNS Resolution and timeout failure.
+
+ /** IKE configuration error resulting in failure */
+ public static final int IWLAN_IKEV2_CONFIG_FAILURE = 0x4000;
+ /**
+ * Sent in the response to an IKE_AUTH message when, for some reason,
+ * the authentication failed.
+ */
+ public static final int IWLAN_IKEV2_AUTH_FAILURE = 0x4001;
+ /** IKE message timeout, tunnel setup failed due to no response from EPDG */
+ public static final int IWLAN_IKEV2_MSG_TIMEOUT = 0x4002;
+ /** IKE Certification validation failure */
+ public static final int IWLAN_IKEV2_CERT_INVALID = 0x4003;
+ /** Unable to resolve FQDN for the ePDG to an IP address */
+ public static final int IWLAN_DNS_RESOLUTION_NAME_FAILURE = 0x4004;
+ /** No response received from the DNS Server due to a timeout*/
+ public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 0x4005;
+
// OEM sepecific error codes. To be used by OEMs when they don't
// want to reveal error code which would be replaced by ERROR_UNSPECIFIED
public static final int OEM_DCFAILCAUSE_1 = 0x1001;
@@ -1341,6 +1419,34 @@
sFailCauseMap.put(VSNCP_RECONNECT_NOT_ALLOWED, "VSNCP_RECONNECT_NOT_ALLOWED");
sFailCauseMap.put(IPV6_PREFIX_UNAVAILABLE, "IPV6_PREFIX_UNAVAILABLE");
sFailCauseMap.put(HANDOFF_PREFERENCE_CHANGED, "HANDOFF_PREFERENCE_CHANGED");
+ sFailCauseMap.put(IWLAN_PDN_CONNECTION_REJECTION, "IWLAN_PDN_CONNECTION_REJECTION");
+ sFailCauseMap.put(IWLAN_MAX_CONNECTION_REACHED, "IWLAN_MAX_CONNECTION_REACHED");
+ sFailCauseMap.put(IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION,
+ "IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION");
+ sFailCauseMap.put(IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION,
+ "IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION");
+ sFailCauseMap.put(IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS,
+ "IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS");
+ sFailCauseMap.put(IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS,
+ "IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS");
+ sFailCauseMap.put(IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED,
+ "IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED");
+ sFailCauseMap.put(IWLAN_USER_UNKNOWN, "IWLAN_USER_UNKNOWN");
+ sFailCauseMap.put(IWLAN_NO_APN_SUBSCRIPTION, "IWLAN_NO_APN_SUBSCRIPTION");
+ sFailCauseMap.put(IWLAN_AUTHORIZATION_REJECTED, "IWLAN_AUTHORIZATION_REJECTED");
+ sFailCauseMap.put(IWLAN_ILLEGAL_ME, "IWLAN_ILLEGAL_ME");
+ sFailCauseMap.put(IWLAN_NETWORK_FAILURE, "IWLAN_NETWORK_FAILURE");
+ sFailCauseMap.put(IWLAN_RAT_TYPE_NOT_ALLOWED, "IWLAN_RAT_TYPE_NOT_ALLOWED");
+ sFailCauseMap.put(IWLAN_IMEI_NOT_ACCEPTED, "IWLAN_IMEI_NOT_ACCEPTED");
+ sFailCauseMap.put(IWLAN_PLMN_NOT_ALLOWED, "IWLAN_PLMN_NOT_ALLOWED");
+ sFailCauseMap.put(IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED,
+ "IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED");
+ sFailCauseMap.put(IWLAN_IKEV2_CONFIG_FAILURE, "IWLAN_IKEV2_CONFIG_FAILURE");
+ sFailCauseMap.put(IWLAN_IKEV2_AUTH_FAILURE, "IWLAN_IKEV2_AUTH_FAILURE");
+ sFailCauseMap.put(IWLAN_IKEV2_MSG_TIMEOUT, "IWLAN_IKEV2_MSG_TIMEOUT");
+ sFailCauseMap.put(IWLAN_IKEV2_CERT_INVALID, "IWLAN_IKEV2_CERT_INVALID");
+ sFailCauseMap.put(IWLAN_DNS_RESOLUTION_NAME_FAILURE, "IWLAN_DNS_RESOLUTION_NAME_FAILURE");
+ sFailCauseMap.put(IWLAN_DNS_RESOLUTION_TIMEOUT, "IWLAN_DNS_RESOLUTION_TIMEOUT");
sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 83e63ef..904232b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -791,6 +791,13 @@
public static final String IMS_RCS_UCE_ENABLED = SimInfo.COLUMN_IMS_RCS_UCE_ENABLED;
/**
+ * Determines if the user has enabled cross SIM calling for this subscription.
+ *
+ * @hide
+ */
+ public static final String CROSS_SIM_CALLING_ENABLED = SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED;
+
+ /**
* TelephonyProvider column name for whether a subscription is opportunistic, that is,
* whether the network it connects to is limited in functionality or coverage.
* For example, CBRS.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8368e3a..dd4b642 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1494,6 +1494,16 @@
public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4;
/**
+ * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+ * to indicate that default subscription for data/sms/voice is now determined, that
+ * it should dismiss any dialog or pop-ups that is asking user to select default sub.
+ * This is used when, for example, opportunistic subscription is configured. At that
+ * time the primary becomes default sub there's no need to ask user to select anymore.
+ * @hide
+ */
+ public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS = 5;
+
+ /**
* Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
* to indicate if the SIM combination in DSDS has limitation or compatible issue.
* e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios.
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 0d8e5bc..bd623e0 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -30,7 +30,6 @@
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.BinderCacheManager;
-import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRcsController;
@@ -62,9 +61,10 @@
* been enabled by the user can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}.
* <p>
* This intent will always be handled by the system, however the application should only send
- * this Intent if the carrier supports RCS contact discovery, which can be queried using the key
- * {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL}. Otherwise, the RCS contact discovery
- * opt-in dialog will not be shown.
+ * this Intent if the carrier supports bulk RCS contact exchange, which will be true if either
+ * key {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL}
+ * or {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set to true.
+ * Otherwise, the RCS contact discovery opt-in dialog will not be shown.
* <p>
* Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the
* setting will be be shown for.
@@ -396,6 +396,7 @@
* rather the subscription is capable of this service over IMS.
* @see #isAvailable(int)
* @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
+ * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
* @throws ImsException if the IMS service is not available when calling this method.
* See {@link ImsException#getCode()} for more information on the error codes.
* @hide
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 0fe76a04..f3c38bc 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.WorkerThread;
@@ -31,6 +32,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.aidl.IRcsConfigCallback;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
@@ -936,6 +938,115 @@
private int mSubId;
/**
+ * The callback for RCS provisioning changes.
+ */
+ public static class RcsProvisioningCallback {
+ private static class CallbackBinder extends IRcsConfigCallback.Stub {
+
+ private final RcsProvisioningCallback mLocalCallback;
+ private Executor mExecutor;
+
+ private CallbackBinder(RcsProvisioningCallback localCallback) {
+ mLocalCallback = localCallback;
+ }
+
+ @Override
+ public void onConfigurationChanged(byte[] configXml) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onConfigurationChanged(configXml));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void onAutoConfigurationErrorReceived(int errorCode, String errorString) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onAutoConfigurationErrorReceived(
+ errorCode, errorString));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void onConfigurationReset() {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onConfigurationReset());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void onRemoved() {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onRemoved());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final CallbackBinder mBinder = new CallbackBinder(this);
+
+ /**
+ * RCS configuration received via OTA provisioning. Configuration may change
+ * due to various triggers defined in GSMA RCC.14 for ACS(auto configuration
+ * server) or other operator defined triggers. If RCS provisioning is already
+ * completed at the time of callback registration, then this method shall be
+ * invoked with the current configuration
+ * @param configXml The RCS configurationXML received OTA.
+ */
+ public void onConfigurationChanged(@NonNull byte[] configXml) {}
+
+ /**
+ * Errors during autoconfiguration connection setup are notified by the
+ * ACS(auto configuration server) client using this interface.
+ * @param errorCode HTTP error received during connection setup defined in
+ * GSMA RCC.14 2.4.3, like {@link java.net.HttpURLConnection#HTTP_UNAUTHORIZED},
+ * {@link java.net.HttpURLConnection#HTTP_FORBIDDEN}, etc.
+ * @param errorString reason phrase received with the error
+ */
+ public void onAutoConfigurationErrorReceived(int errorCode,
+ @NonNull String errorString) {}
+
+ /**
+ * When the previously valid RCS configuration is cleaned up by telephony for
+ * any case like SIM removed, default messaging application changed, etc.,
+ * this method will be invoked to notify the application regarding this change.
+ */
+ public void onConfigurationReset() {}
+
+ /**
+ * When the RCS application is no longer the Default messaging application,
+ * or when the subscription associated with this callback is removed (SIM
+ * removed, ESIM swap,etc...), callback will automatically be removed and
+ * the below method is invoked. There is a possibility that the method is
+ * invoked after the subscription has become inactive
+ */
+ public void onRemoved() {}
+
+ /**@hide*/
+ public final IRcsConfigCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ /**
* Create a new {@link ProvisioningManager} for the subscription specified.
*
* @param subId The ID of the subscription that this ProvisioningManager will use.
@@ -1207,6 +1318,174 @@
}
+ /**
+ * Provides the single registration capability of the device and the carrier.
+ *
+ * <p>This intent only provides the capability and not the current provisioning status of
+ * the RCS VoLTE single registration feature. Only default messaging application may receive
+ * the intent.
+ *
+ * <p>Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to specify the subscription index for which
+ * the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
+ * status.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE =
+ "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE";
+
+ /**
+ * Integer extra to specify subscription index.
+ */
+ public static final String EXTRA_SUBSCRIPTION_ID =
+ "android.telephony.ims.extra.SUBSCRIPTION_ID";
+
+ /**
+ * Integer extra to specify RCS single registration status
+ *
+ * <p>The value can be {@link #STATUS_CAPABLE}, {@link #STATUS_DEVICE_NOT_CAPABLE},
+ * {@link #STATUS_CARRIER_NOT_CAPABLE}, or bitwise OR of
+ * {@link #STATUS_DEVICE_NOT_CAPABLE} and {@link #STATUS_CARRIER_NOT_CAPABLE}.
+ */
+ public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS";
+
+ /**
+ * RCS VoLTE single registration is supported by the device and carrier.
+ */
+ public static final int STATUS_CAPABLE = 0;
+
+ /**
+ * RCS VoLTE single registration is not supported by the device.
+ */
+ public static final int STATUS_DEVICE_NOT_CAPABLE = 0x01;
+
+ /**
+ * RCS VoLTE single registration is not supported by the carrier
+ */
+ public static final int STATUS_CARRIER_NOT_CAPABLE = 0x01 << 1;
+
+ /**
+ * Provide the client configuration parameters of the RCS application.
+ *
+ * <p>When this application is also the default messaging application, and RCS
+ * provisioning is done using autoconfiguration, then these parameters shall be
+ * sent in the HTTP get request to fetch the RCS provisioning. RCS client
+ * configuration must be provided by the application before registering for the
+ * provisioning status events {@link #registerRcsProvisioningChangedCallback()}
+ * @param rcc RCS client configuration {@link RcsClientConfiguration}
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setRcsClientConfiguration(
+ @NonNull RcsClientConfiguration rcc) throws ImsException {
+ try {
+ getITelephony().setRcsClientConfiguration(mSubId, rcc);
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Returns a flag to indicate if the device software and the carrier
+ * have the capability to support RCS Volte single IMS registration.
+ * @return true if this single registration is capable, false otherwise
+ * @throws ImsException If the remote ImsService is not available for
+ * any reason or the subscription associated with this instance is no
+ * longer active. See {@link ImsException#getCode()} for more
+ * information.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
+ try {
+ return getITelephony().isRcsVolteSingleRegistrationCapable(mSubId);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Registers a new {@link RcsProvisioningCallback} to listen to changes to
+ * RCS provisioning xml.
+ *
+ * <p>RCS application must be the default messaging application and must
+ * have already registered its {@link RcsClientConfiguration} by using
+ * {@link #setRcsClientConfiguration} before it registers the provisioning
+ * callback. If ProvisioningManager has a valid RCS configuration at the
+ * time of callback registration and a reconfiguration is not required
+ * due to RCS client parameters change, then the callback shall be invoked
+ * immediately with the xml.
+ * When the subscription associated with this callback is removed (SIM removed,
+ * ESIM swap,etc...), this callback will automatically be removed.
+ *
+ * @param executor The {@link Executor} to call the callback methods on
+ * @param callback The rcs provisioning callback to be registered.
+ * @see #unregisterRcsProvisioningChangedCallback(RcsProvisioningCallback)
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ * @throws IllegalArgumentException if the subscription associated with this
+ * callback is not active (SIM is not inserted, ESIM inactive) or the
+ * subscription is invalid.
+ * @throws ImsException if the subscription associated with this callback is
+ * valid, but the {@link ImsService} associated with the subscription is not
+ * available. This can happen if the service crashed, for example.
+ * It shall also throw this exception when the RCS client parameters for the
+ * application are not valid. In that case application must set the client
+ * params (See {@link #setRcsClientConfiguration()}) and re register the
+ * callback.
+ * See {@link ImsException#getCode()} for a more detailed reason.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerRcsProvisioningChangedCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull RcsProvisioningCallback callback) throws ImsException {
+ callback.setExecutor(executor);
+ try {
+ getITelephony().registerRcsProvisioningChangedCallback(mSubId, callback.getBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregister an existing {@link RcsProvisioningCallback}. Application can
+ * unregister when its no longer interested in the provisioning updates
+ * like when a user disables RCS from the UI/settings.
+ * When the subscription associated with this callback is removed (SIM
+ * removed, ESIM swap, etc...), this callback will automatically be
+ * removed. If this method is called for an inactive subscription, it
+ * will result in a no-op.
+ * @param callback The existing {@link RcsProvisioningCallback} to be
+ * removed.
+ * @see #registerRcsProvisioningChangedCallback(RcsClientConfiguration,
+ * Executor, RcsProvisioningCallback) @throws IllegalArgumentException
+ * if the subscription associated with this callback is invalid.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterRcsProvisioningChangedCallback(
+ @NonNull RcsProvisioningCallback callback) {
+ try {
+ getITelephony().unregisterRcsProvisioningChangedCallback(
+ mSubId, callback.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Reconfiguration triggered by the RCS application. Most likely cause
+ * is the 403 forbidden to a HTTP request.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void triggerRcsReconfiguration() {
+ try {
+ getITelephony().triggerRcsReconfiguration(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(
TelephonyFrameworkInitializer
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/telephony/java/android/telephony/ims/RcsClientConfiguration.aidl
similarity index 66%
copy from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
copy to telephony/java/android/telephony/ims/RcsClientConfiguration.aidl
index b1f99e6..a702f0f 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.aidl
@@ -14,17 +14,6 @@
* limitations under the License.
*/
-package android.media;
+package android.telephony.ims;
-import android.media.AudioDeviceAttributes;
-
-/**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
- *
- * {@hide}
- */
-oneway interface IStrategyPreferredDeviceDispatcher {
-
- void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
-
-}
+parcelable RcsClientConfiguration;
diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.java b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
new file mode 100644
index 0000000..793c377
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The container of RCS application related configs.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RcsClientConfiguration implements Parcelable {
+
+ /**@hide*/
+ @StringDef(prefix = "RCS_PROFILE_",
+ value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3})
+ public @interface StringRcsProfile {}
+
+ /**
+ * RCS profile UP 1.0
+ */
+ public static final String RCS_PROFILE_1_0 = "UP_1.0";
+ /**
+ * RCS profile UP 2.3
+ */
+ public static final String RCS_PROFILE_2_3 = "UP_2.3";
+
+ private String mRcsVersion;
+ private String mRcsProfile;
+ private String mClientVendor;
+ private String mClientVersion;
+
+ /**
+ * Create a RcsClientConfiguration object.
+ * Default messaging application must pass a valid configuration object
+ * @param rcsVersion The parameter identifies the RCS version supported
+ * by the client. Refer to GSMA RCC.07 "rcs_version" parameter.
+ * @param rcsProfile Identifies a fixed set of RCS services that are
+ * supported by the client. See {@link #RCS_PROFILE_1_0 } or
+ * {@link #RCS_PROFILE_2_3 }
+ * @param clientVendor Identifies the vendor providing the RCS client.
+ * @param clientVersion Identifies the RCS client version. Refer to GSMA
+ * RCC.07 "client_version" parameter.
+ * Example:client_version=RCSAndrd-1.0
+ */
+ public RcsClientConfiguration(@NonNull String rcsVersion,
+ @NonNull @StringRcsProfile String rcsProfile,
+ @NonNull String clientVendor, @NonNull String clientVersion) {
+ mRcsVersion = rcsVersion;
+ mRcsProfile = rcsProfile;
+ mClientVendor = clientVendor;
+ mClientVersion = clientVersion;
+ }
+
+ /**
+ * Returns RCS version supported.
+ */
+ public @NonNull String getRcsVersion() {
+ return mRcsVersion;
+ }
+
+ /**
+ * Returns RCS profile supported.
+ */
+ public @NonNull @StringRcsProfile String getRcsProfile() {
+ return mRcsProfile;
+ }
+
+ /**
+ * Returns the name of the vendor providing the RCS client.
+ */
+ public @NonNull String getClientVendor() {
+ return mClientVendor;
+ }
+
+ /**
+ * Returns the RCS client version.
+ */
+ public @NonNull String getClientVersion() {
+ return mClientVersion;
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeString(mRcsVersion);
+ out.writeString(mRcsProfile);
+ out.writeString(mClientVendor);
+ out.writeString(mClientVersion);
+ }
+
+ /**
+ * {@link Parcelable.Creator}
+ *
+ */
+ public static final @android.annotation.NonNull Parcelable.Creator<
+ RcsClientConfiguration> CREATOR = new Creator<RcsClientConfiguration>() {
+ @Override
+ public RcsClientConfiguration createFromParcel(Parcel in) {
+ String rcsVersion = in.readString();
+ String rcsProfile = in.readString();
+ String clientVendor = in.readString();
+ String clientVersion = in.readString();
+ return new RcsClientConfiguration(rcsVersion, rcsProfile,
+ clientVendor, clientVersion);
+ }
+
+ @Override
+ public RcsClientConfiguration[] newArray(int size) {
+ return new RcsClientConfiguration[size];
+ }
+ };
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof RcsClientConfiguration)) {
+ return false;
+ }
+
+ RcsClientConfiguration other = (RcsClientConfiguration) obj;
+
+ return mRcsVersion.equals(other.mRcsVersion) && mRcsProfile.equals(other.mRcsProfile)
+ && mClientVendor.equals(other.mClientVendor)
+ && mClientVersion.equals(other.mClientVersion);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRcsVersion, mRcsProfile, mClientVendor, mClientVersion);
+ }
+}
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/telephony/java/android/telephony/ims/RcsConfig.aidl
similarity index 66%
copy from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
copy to telephony/java/android/telephony/ims/RcsConfig.aidl
index b1f99e6..cfd93fb 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/telephony/java/android/telephony/ims/RcsConfig.aidl
@@ -14,17 +14,6 @@
* limitations under the License.
*/
-package android.media;
+package android.telephony.ims;
-import android.media.AudioDeviceAttributes;
-
-/**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
- *
- * {@hide}
- */
-oneway interface IStrategyPreferredDeviceDispatcher {
-
- void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
-
-}
+parcelable RcsConfig;
diff --git a/telephony/java/android/telephony/ims/RcsConfig.java b/telephony/java/android/telephony/ims/RcsConfig.java
new file mode 100644
index 0000000..07e95cc
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsConfig.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Telephony.SimInfo;
+import android.text.TextUtils;
+
+import com.android.telephony.Rlog;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * RCS config data and methods to process the config
+ * @hide
+ */
+public final class RcsConfig implements Parcelable {
+ private static final String LOG_TAG = "RcsConfig";
+ private static final boolean DBG = Build.IS_ENG;
+
+ private final HashMap<String, String> mValues = new HashMap<>();
+
+ private RcsConfig(HashMap<String, String> values) {
+ mValues.putAll(values);
+ }
+
+ public RcsConfig(byte[] data) throws IllegalArgumentException {
+ if (data == null || data.length == 0) {
+ throw new IllegalArgumentException("Empty data");
+ }
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ try {
+ XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ XmlPullParser xpp = factory.newPullParser();
+ xpp.setInput(inputStream, null);
+ int eventType = xpp.getEventType();
+ String tag = null;
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ tag = xpp.getName().trim();
+ } else if (eventType == XmlPullParser.END_TAG) {
+ tag = null;
+ } else if (eventType == XmlPullParser.TEXT) {
+ String value = xpp.getText().trim();
+ if (!TextUtils.isEmpty(tag) && !TextUtils.isEmpty(value)) {
+ mValues.put(tag, value);
+ }
+ }
+ eventType = xpp.next();
+ }
+ } catch (IOException | XmlPullParserException e) {
+ throw new IllegalArgumentException(e);
+ } finally {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ loge("error to close input stream, skip.");
+ }
+ }
+ }
+
+ /**
+ * Retrieve a String value of the config item with the tag
+ *
+ * @param tag The name of the config to retrieve.
+ * @param defaultVal Value to return if the config does not exist.
+ *
+ * @return Returns the config value if it exists, or defaultVal.
+ */
+ public @Nullable String getString(@NonNull String tag, @Nullable String defaultVal) {
+ return mValues.containsKey(tag) ? mValues.get(tag) : defaultVal;
+ }
+
+ /**
+ * Retrieve a int value of the config item with the tag
+ *
+ * @param tag The name of the config to retrieve.
+ * @param defaultVal Value to return if the config does not exist or not valid.
+ *
+ * @return Returns the config value if it exists and is a valid int, or defaultVal.
+ */
+ public int getInteger(@NonNull String tag, int defaultVal) {
+ try {
+ return Integer.parseInt(mValues.get(tag));
+ } catch (NumberFormatException e) {
+ logd("error to getInteger for " + tag + " due to " + e);
+ }
+ return defaultVal;
+ }
+
+ /**
+ * Retrieve a boolean value of the config item with the tag
+ *
+ * @param tag The name of the config to retrieve.
+ * @param defaultVal Value to return if the config does not exist.
+ *
+ * @return Returns the config value if it exists, or defaultVal.
+ */
+ public boolean getBoolean(@NonNull String tag, boolean defaultVal) {
+ if (!mValues.containsKey(tag)) {
+ return defaultVal;
+ }
+ return Boolean.parseBoolean(mValues.get(tag));
+ }
+
+ /**
+ * Check whether the config item exists
+ *
+ * @param tag The name of the config to retrieve.
+ *
+ * @return Returns true if it exists, or false.
+ */
+ public boolean hasConfig(@NonNull String tag) {
+ return mValues.containsKey(tag);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("[RCS Config]");
+ if (DBG) {
+ mValues.forEach((t, v) -> {
+ sb.append("\n");
+ sb.append(t);
+ sb.append(" : ");
+ sb.append(v);
+ });
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof RcsConfig)) {
+ return false;
+ }
+
+ RcsConfig other = (RcsConfig) obj;
+
+ return mValues.equals(other.mValues);
+ }
+
+ @Override
+ public int hashCode() {
+ return mValues.hashCode();
+ }
+
+ /**
+ * compress the gzip format data
+ */
+ public static @Nullable byte[] compressGzip(@NonNull byte[] data) {
+ if (data == null || data.length == 0) {
+ return data;
+ }
+ byte[] out = null;
+ try {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
+ GZIPOutputStream gzipCompressingStream =
+ new GZIPOutputStream(outputStream);
+ gzipCompressingStream.write(data);
+ gzipCompressingStream.close();
+ out = outputStream.toByteArray();
+ outputStream.close();
+ } catch (IOException e) {
+ loge("Error to compressGzip due to " + e);
+ }
+ return out;
+ }
+
+ /**
+ * decompress the gzip format data
+ */
+ public static @Nullable byte[] decompressGzip(@NonNull byte[] data) {
+ if (data == null || data.length == 0) {
+ return data;
+ }
+ byte[] out = null;
+ try {
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ GZIPInputStream gzipDecompressingStream =
+ new GZIPInputStream(inputStream);
+ byte[] buf = new byte[1024];
+ int size = gzipDecompressingStream.read(buf);
+ while (size >= 0) {
+ outputStream.write(buf, 0, size);
+ size = gzipDecompressingStream.read(buf);
+ }
+ gzipDecompressingStream.close();
+ inputStream.close();
+ out = outputStream.toByteArray();
+ outputStream.close();
+ } catch (IOException e) {
+ loge("Error to decompressGzip due to " + e);
+ }
+ return out;
+ }
+
+ /**
+ * save the config to siminfo db. It is only used internally.
+ */
+ public static void updateConfigForSub(@NonNull Context cxt, int subId,
+ @NonNull byte[] config, boolean isCompressed) {
+ //always store gzip compressed data
+ byte[] data = isCompressed ? config : compressGzip(config);
+ ContentValues values = new ContentValues();
+ values.put(SimInfo.COLUMN_RCS_CONFIG, data);
+ cxt.getContentResolver().update(SimInfo.CONTENT_URI, values,
+ SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
+ }
+
+ /**
+ * load the config from siminfo db. It is only used internally.
+ */
+ public static @Nullable byte[] loadRcsConfigForSub(@NonNull Context cxt,
+ int subId, boolean isCompressed) {
+
+ byte[] data = null;
+
+ Cursor cursor = cxt.getContentResolver().query(SimInfo.CONTENT_URI, null,
+ SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ data = cursor.getBlob(cursor.getColumnIndexOrThrow(SimInfo.COLUMN_RCS_CONFIG));
+ }
+ } catch (Exception e) {
+ loge("error to load rcs config for sub:" + subId + " due to " + e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return isCompressed ? data : decompressGzip(data);
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeMap(mValues);
+ }
+
+ /**
+ * {@link Parcelable.Creator}
+ *
+ */
+ public static final @NonNull Parcelable.Creator<RcsConfig>
+ CREATOR = new Creator<RcsConfig>() {
+ @Override
+ public RcsConfig createFromParcel(Parcel in) {
+ HashMap<String, String> values = in.readHashMap(null);
+ return values == null ? null : new RcsConfig(values);
+ }
+
+ @Override
+ public RcsConfig[] newArray(int size) {
+ return new RcsConfig[size];
+ }
+ };
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ private static void logd(String msg) {
+ Rlog.d(LOG_TAG, msg);
+ }
+
+ private static void loge(String msg) {
+ Rlog.e(LOG_TAG, msg);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 2ec88ff..2e9eb94 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -18,7 +18,9 @@
import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
@@ -372,4 +374,37 @@
+ " into this method");
}
}
+
+ /**
+ * Trigger a full network registration as required by receiving a SIP message containing a
+ * permanent error from the network or never receiving a response to a SIP transaction request.
+ *
+ * @param connection The {@link SipDelegateConnection} that was being used when this error was
+ * received.
+ * @param sipCode The SIP code response associated with the SIP message request that
+ * triggered this condition.
+ * @param sipReason The SIP reason code associated with the SIP message request that triggered
+ * this condition. May be {@code null} if there was no reason String provided from the
+ * network.
+ */
+ public void triggerFullNetworkRegistration(@NonNull SipDelegateConnection connection,
+ @IntRange(from = 100, to = 699) int sipCode, @Nullable String sipReason) {
+ if (connection == null) {
+ throw new IllegalArgumentException("invalid connection.");
+ }
+ if (connection instanceof SipDelegateConnectionAidlWrapper) {
+ SipDelegateConnectionAidlWrapper w = (SipDelegateConnectionAidlWrapper) connection;
+ try {
+ IImsRcsController controller = mBinderCache.getBinder();
+ controller.triggerNetworkRegistration(mSubId, w.getSipDelegateBinder(), sipCode,
+ sipReason);
+ } catch (RemoteException e) {
+ // Connection to telephony died, but this will signal destruction of SipDelegate
+ // eventually anyway, so return.
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown SipDelegateConnection implementation passed"
+ + " into this method");
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
index 57206c9..5eee389 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
@@ -18,8 +18,9 @@
package android.telephony.ims.aidl;
import android.os.PersistableBundle;
-
import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.aidl.IRcsConfigCallback;
+import android.telephony.ims.RcsClientConfiguration;
import com.android.ims.ImsConfigListener;
@@ -41,4 +42,9 @@
int setConfigString(int item, String value);
void updateImsCarrierConfigs(in PersistableBundle bundle);
void notifyRcsAutoConfigurationReceived(in byte[] config, boolean isCompressed);
+ void notifyRcsAutoConfigurationRemoved();
+ void addRcsConfigCallback(IRcsConfigCallback c);
+ void removeRcsConfigCallback(IRcsConfigCallback c);
+ void triggerRcsReconfiguration();
+ void setRcsClientConfiguration(in RcsClientConfiguration rcc);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index c6d9a86..3634989 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -67,6 +67,8 @@
ISipDelegateConnectionStateCallback delegateState,
ISipDelegateMessageCallback delegateMessage);
void destroySipDelegate(int subId, ISipDelegate connection, int reason);
+ void triggerNetworkRegistration(int subId, ISipDelegate connection, int sipCode,
+ String sipReason);
// Internal commands that should not be made public
void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback);
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl
index 4ae0a75..4fd9040 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl
@@ -28,4 +28,7 @@
int getRegistrationTechnology();
oneway void addRegistrationCallback(IImsRegistrationCallback c);
oneway void removeRegistrationCallback(IImsRegistrationCallback c);
-}
\ No newline at end of file
+ oneway void triggerFullNetworkRegistration(int sipCode, String sipReason);
+ oneway void triggerUpdateSipDelegateRegistration();
+ oneway void triggerSipDelegateDeregistration();
+}
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
similarity index 61%
copy from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
copy to telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
index b1f99e6..5a8973e 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (c) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-package android.media;
-
-import android.media.AudioDeviceAttributes;
+package android.telephony.ims.aidl;
/**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
- *
+ * The callback for RCS provisioning changes.
* {@hide}
*/
-oneway interface IStrategyPreferredDeviceDispatcher {
-
- void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
-
+oneway interface IRcsConfigCallback {
+ void onConfigurationChanged(in byte[] config);
+ void onAutoConfigurationErrorReceived(int errorCode, String errorString);
+ void onConfigurationReset();
+ void onRemoved();
}
+
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index e757d9f..cc050be 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -23,8 +23,11 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RcsClientConfiguration;
+import android.telephony.ims.RcsConfig;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.aidl.IRcsConfigCallback;
import android.util.Log;
import com.android.ims.ImsConfig;
@@ -202,7 +205,13 @@
@Override
public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
throws RemoteException {
- getImsConfigImpl().notifyRcsAutoConfigurationReceived(config, isCompressed);
+ getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed);
+ }
+
+ @Override
+ public void notifyRcsAutoConfigurationRemoved()
+ throws RemoteException {
+ getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved();
}
private void notifyImsConfigChanged(int item, int value) throws RemoteException {
@@ -228,6 +237,26 @@
notifyImsConfigChanged(item, value);
}
}
+
+ @Override
+ public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
+ getImsConfigImpl().addRcsConfigCallback(c);
+ }
+
+ @Override
+ public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
+ getImsConfigImpl().removeRcsConfigCallback(c);
+ }
+
+ @Override
+ public void triggerRcsReconfiguration() throws RemoteException {
+ getImsConfigImpl().triggerAutoConfiguration();
+ }
+
+ @Override
+ public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException {
+ getImsConfigImpl().setRcsClientConfiguration(rcc);
+ }
}
/**
@@ -257,6 +286,9 @@
private final RemoteCallbackListExt<IImsConfigCallback> mCallbacks =
new RemoteCallbackListExt<>();
+ private final RemoteCallbackListExt<IRcsConfigCallback> mRcsCallbacks =
+ new RemoteCallbackListExt<>();
+ private byte[] mRcsConfigData;
ImsConfigStub mImsConfigStub;
/**
@@ -320,6 +352,50 @@
});
}
+ private void addRcsConfigCallback(IRcsConfigCallback c) {
+ mRcsCallbacks.register(c);
+ if (mRcsConfigData != null) {
+ try {
+ c.onConfigurationChanged(mRcsConfigData);
+ } catch (RemoteException e) {
+ Log.w(TAG, "dead binder to call onConfigurationChanged, skipping.");
+ }
+ }
+ }
+
+ private void removeRcsConfigCallback(IRcsConfigCallback c) {
+ mRcsCallbacks.unregister(c);
+ }
+
+ private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) {
+ mRcsConfigData = isCompressed ? RcsConfig.decompressGzip(config) : config;
+ // can be null in testing
+ if (mRcsCallbacks != null) {
+ mRcsCallbacks.broadcastAction(c -> {
+ try {
+ c.onConfigurationChanged(mRcsConfigData);
+ } catch (RemoteException e) {
+ Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping.");
+ }
+ });
+ }
+ notifyRcsAutoConfigurationReceived(config, isCompressed);
+ }
+
+ private void onNotifyRcsAutoConfigurationRemoved() {
+ mRcsConfigData = null;
+ if (mRcsCallbacks != null) {
+ mRcsCallbacks.broadcastAction(c -> {
+ try {
+ c.onConfigurationReset();
+ } catch (RemoteException e) {
+ Log.w(TAG, "dead binder in notifyRcsAutoConfigurationRemoved, skipping.");
+ }
+ });
+ }
+ notifyRcsAutoConfigurationRemoved();
+ }
+
/**
* @hide
*/
@@ -369,6 +445,12 @@
}
/**
+ * The RCS autoconfiguration XML file is removed or invalid.
+ */
+ public void notifyRcsAutoConfigurationRemoved() {
+ }
+
+ /**
* Sets the configuration value for this ImsService.
*
* @param item an integer key.
@@ -421,4 +503,43 @@
public void updateImsCarrierConfigs(PersistableBundle bundle) {
// Base Implementation - Should be overridden
}
+
+ /**
+ * Default messaging application parameters are sent to the ACS client
+ * using this interface.
+ * @param rcc RCS client configuration {@link RcsClientConfiguration}
+ */
+ public void setRcsClientConfiguration(@NonNull RcsClientConfiguration rcc) {
+ // Base Implementation - Should be overridden
+ }
+
+ /**
+ * Reconfiguration triggered by the RCS application. Most likely cause
+ * is the 403 forbidden to a SIP/HTTP request
+ */
+ public void triggerAutoConfiguration() {
+ // Base Implementation - Should be overridden
+ }
+
+ /**
+ * Errors during autoconfiguration connection setup are notified by the
+ * ACS client using this interface.
+ * @param errorCode HTTP error received during connection setup.
+ * @param errorString reason phrase received with the error
+ */
+ public final void notifyAutoConfigurationErrorReceived(int errorCode,
+ @NonNull String errorString) {
+ // can be null in testing
+ if (mRcsCallbacks == null) {
+ return;
+ }
+ mRcsCallbacks.broadcastAction(c -> {
+ try {
+ //TODO compressed by default?
+ c.onAutoConfigurationErrorReceived(errorCode, errorString);
+ } catch (RemoteException e) {
+ Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping.");
+ }
+ });
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 153d687..088a7e2 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -38,6 +38,9 @@
/**
* Controls IMS registration for this ImsService and notifies the framework when the IMS
* registration for this ImsService has changed status.
+ * <p>
+ * Note: There is no guarantee on the thread that the calls from the framework will be called on. It
+ * is the implementors responsibility to handle moving the calls to a working thread if required.
* @hide
*/
@SystemApi
@@ -92,6 +95,21 @@
public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
ImsRegistrationImplBase.this.removeRegistrationCallback(c);
}
+
+ @Override
+ public void triggerFullNetworkRegistration(int sipCode, String sipReason) {
+ ImsRegistrationImplBase.this.triggerFullNetworkRegistration(sipCode, sipReason);
+ }
+
+ @Override
+ public void triggerUpdateSipDelegateRegistration() {
+ ImsRegistrationImplBase.this.updateSipDelegateRegistration();
+ }
+
+ @Override
+ public void triggerSipDelegateDeregistration() {
+ ImsRegistrationImplBase.this.triggerSipDelegateDeregistration();
+ }
};
private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks =
@@ -133,7 +151,6 @@
* If the SIP delegate feature tag configuration has changed, then this method will be
* called in order to let the ImsService know that it can pick up these changes in the IMS
* registration.
- * @hide
*/
public void updateSipDelegateRegistration() {
// Stub implementation, ImsService should implement this
@@ -150,7 +167,6 @@
* <p>
* This should not affect the registration of features managed by the ImsService itself, such as
* feature tags related to MMTEL registration.
- * @hide
*/
public void triggerSipDelegateDeregistration() {
// Stub implementation, ImsService should implement this
@@ -169,9 +185,8 @@
* be carrier specific.
* @param sipReason The reason associated with the SIP error code. {@code null} if there was no
* reason associated with the error.
- * @hide
*/
- public void triggerNetworkReregistration(@IntRange(from = 100, to = 699) int sipCode,
+ public void triggerFullNetworkRegistration(@IntRange(from = 100, to = 699) int sipCode,
@Nullable String sipReason) {
// Stub implementation, ImsService should implement this
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 205a425..7c5047c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -52,6 +52,7 @@
import android.telephony.TelephonyHistogram;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.RcsClientConfiguration;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
@@ -59,6 +60,7 @@
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.aidl.IRcsConfigCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
@@ -2303,4 +2305,51 @@
* Return the release time for telephony to unbind GbaService.
*/
int getGbaReleaseTime(int subId);
+
+ /**
+ * Provide the client configuration parameters of the RCS application.
+ */
+ void setRcsClientConfiguration(int subId, in RcsClientConfiguration rcc);
+
+ /**
+ * return value to indicate whether the device and the carrier can support RCS VoLTE
+ * single registration.
+ */
+ boolean isRcsVolteSingleRegistrationCapable(int subId);
+
+ /**
+ * Register RCS provisioning callback.
+ */
+ void registerRcsProvisioningChangedCallback(int subId,
+ IRcsConfigCallback callback);
+
+ /**
+ * Unregister RCS provisioning callback.
+ */
+ void unregisterRcsProvisioningChangedCallback(int subId, IRcsConfigCallback callback);
+
+ /**
+ * trigger RCS reconfiguration.
+ */
+ void triggerRcsReconfiguration(int subId);
+
+ /**
+ * Overrides the config of RCS VoLTE single registration enabled for the device.
+ */
+ void setDeviceSingleRegistrationEnabledOverride(String enabled);
+
+ /**
+ * Gets the config of RCS VoLTE single registration enabled for the device.
+ */
+ boolean getDeviceSingleRegistrationEnabled();
+
+ /**
+ * Overrides the config of RCS VoLTE single registration enabled for the carrier/subscription.
+ */
+ boolean setCarrierSingleRegistrationEnabledOverride(int subId, String enabled);
+
+ /**
+ * Gets the config of RCS VoLTE single registration enabled for the carrier/subscription.
+ */
+ boolean getCarrierSingleRegistrationEnabled(int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 21cca4c..52f263f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -521,6 +521,8 @@
int RIL_REQUEST_CANCEL_HANDOVER = 218;
int RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS = 219;
int RIL_REQUEST_SET_DATA_THROTTLING = 221;
+ int RIL_REQUEST_SET_ALLOWED_NETWORK_TYPE_BITMAP = 222;
+ int RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP = 223;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/test-mock/src/android/test/mock/OWNERS b/test-mock/src/android/test/mock/OWNERS
new file mode 100644
index 0000000..72e8ffc
--- /dev/null
+++ b/test-mock/src/android/test/mock/OWNERS
@@ -0,0 +1 @@
+*
diff --git a/tests/BatteryStatsPerfTest/OWNERS b/tests/BatteryStatsPerfTest/OWNERS
new file mode 100644
index 0000000..4068e2b
--- /dev/null
+++ b/tests/BatteryStatsPerfTest/OWNERS
@@ -0,0 +1 @@
+include /BATTERY_STATS_OWNERS
diff --git a/tests/BatteryWaster/OWNERS b/tests/BatteryWaster/OWNERS
new file mode 100644
index 0000000..4068e2b
--- /dev/null
+++ b/tests/BatteryWaster/OWNERS
@@ -0,0 +1 @@
+include /BATTERY_STATS_OWNERS
diff --git a/tests/Compatibility/Android.bp b/tests/Compatibility/Android.bp
index 7dc44fa..c14e705 100644
--- a/tests/Compatibility/Android.bp
+++ b/tests/Compatibility/Android.bp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-android_test {
+android_test_helper_app {
name: "AppCompatibilityTest",
static_libs: ["androidx.test.rules"],
// Include all test java files.
diff --git a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
new file mode 100644
index 0000000..6770066
--- /dev/null
+++ b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net
+
+import android.net.InetAddresses.parseNumericAddress
+import android.os.Build
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertParcelSane
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.net.InetAddress
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) // TcpKeepalivePacketData added to SDK in S
+class TcpKeepalivePacketDataTest {
+ private fun makeData(
+ srcAddress: InetAddress = parseNumericAddress("192.0.2.123"),
+ srcPort: Int = 1234,
+ dstAddress: InetAddress = parseNumericAddress("192.0.2.231"),
+ dstPort: Int = 4321,
+ data: ByteArray = byteArrayOf(1, 2, 3),
+ tcpSeq: Int = 135,
+ tcpAck: Int = 246,
+ tcpWnd: Int = 1234,
+ tcpWndScale: Int = 2,
+ ipTos: Int = 0x12,
+ ipTtl: Int = 10
+ ) = TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data, tcpSeq, tcpAck,
+ tcpWnd, tcpWndScale, ipTos, ipTtl)
+
+ @Test
+ fun testEquals() {
+ val data1 = makeData()
+ val data2 = makeData()
+ assertEquals(data1, data2)
+ assertEquals(data1.hashCode(), data2.hashCode())
+ }
+
+ @Test
+ fun testNotEquals() {
+ assertNotEquals(makeData(srcAddress = parseNumericAddress("192.0.2.124")), makeData())
+ assertNotEquals(makeData(srcPort = 1235), makeData())
+ assertNotEquals(makeData(dstAddress = parseNumericAddress("192.0.2.232")), makeData())
+ assertNotEquals(makeData(dstPort = 4322), makeData())
+ // .equals does not test .packet, as it should be generated from the other fields
+ assertNotEquals(makeData(tcpSeq = 136), makeData())
+ assertNotEquals(makeData(tcpAck = 247), makeData())
+ assertNotEquals(makeData(tcpWnd = 1235), makeData())
+ assertNotEquals(makeData(tcpWndScale = 3), makeData())
+ assertNotEquals(makeData(ipTos = 0x14), makeData())
+ assertNotEquals(makeData(ipTtl = 11), makeData())
+
+ // Update above assertions if field is added
+ assertFieldCountEquals(5, KeepalivePacketData::class.java)
+ assertFieldCountEquals(6, TcpKeepalivePacketData::class.java)
+ }
+
+ @Test
+ fun testParcelUnparcel() {
+ assertParcelSane(makeData(), fieldCount = 6) { a, b ->
+ // .equals() does not verify .packet
+ a == b && a.packet contentEquals b.packet
+ }
+ }
+
+ @Test
+ fun testToString() {
+ val data = makeData()
+ val str = data.toString()
+
+ assertTrue(str.contains(data.srcAddress.hostAddress))
+ assertTrue(str.contains(data.srcPort.toString()))
+ assertTrue(str.contains(data.dstAddress.hostAddress))
+ assertTrue(str.contains(data.dstPort.toString()))
+ // .packet not included in toString()
+ assertTrue(str.contains(data.tcpSeq.toString()))
+ assertTrue(str.contains(data.tcpAck.toString()))
+ assertTrue(str.contains(data.tcpWindow.toString()))
+ assertTrue(str.contains(data.tcpWindowScale.toString()))
+ assertTrue(str.contains(data.ipTos.toString()))
+ assertTrue(str.contains(data.ipTtl.toString()))
+
+ // Update above assertions if field is added
+ assertFieldCountEquals(5, KeepalivePacketData::class.java)
+ assertFieldCountEquals(6, TcpKeepalivePacketData::class.java)
+ }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/KeepalivePacketDataUtilTest.java
similarity index 65%
rename from tests/net/java/android/net/TcpKeepalivePacketDataTest.java
rename to tests/net/java/android/net/KeepalivePacketDataUtilTest.java
index c5b25bd..fc739fb 100644
--- a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
+++ b/tests/net/java/android/net/KeepalivePacketDataUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,8 +20,11 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.net.util.KeepalivePacketDataUtil;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,7 +34,7 @@
import java.nio.ByteBuffer;
@RunWith(JUnit4.class)
-public final class TcpKeepalivePacketDataTest {
+public final class KeepalivePacketDataUtilTest {
private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 1};
private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 5};
@@ -39,7 +42,7 @@
public void setUp() {}
@Test
- public void testV4TcpKeepalivePacket() throws Exception {
+ public void testFromTcpKeepaliveStableParcelable() throws Exception {
final int srcPort = 1234;
final int dstPort = 4321;
final int seq = 0x11111111;
@@ -61,7 +64,7 @@
testInfo.tos = tos;
testInfo.ttl = ttl;
try {
- resultData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo);
+ resultData = KeepalivePacketDataUtil.fromStableParcelable(testInfo);
} catch (InvalidPacketException e) {
fail("InvalidPacketException: " + e);
}
@@ -72,8 +75,8 @@
assertEquals(testInfo.dstPort, resultData.getDstPort());
assertEquals(testInfo.seq, resultData.tcpSeq);
assertEquals(testInfo.ack, resultData.tcpAck);
- assertEquals(testInfo.rcvWnd, resultData.tcpWnd);
- assertEquals(testInfo.rcvWndScale, resultData.tcpWndScale);
+ assertEquals(testInfo.rcvWnd, resultData.tcpWindow);
+ assertEquals(testInfo.rcvWndScale, resultData.tcpWindowScale);
assertEquals(testInfo.tos, resultData.ipTos);
assertEquals(testInfo.ttl, resultData.ipTtl);
@@ -113,7 +116,7 @@
//TODO: add ipv6 test when ipv6 supported
@Test
- public void testParcel() throws Exception {
+ public void testToTcpKeepaliveStableParcelable() throws Exception {
final int srcPort = 1234;
final int dstPort = 4321;
final int sequence = 0x11111111;
@@ -135,8 +138,8 @@
testInfo.ttl = ttl;
TcpKeepalivePacketData testData = null;
TcpKeepalivePacketDataParcelable resultData = null;
- testData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo);
- resultData = testData.toStableParcelable();
+ testData = KeepalivePacketDataUtil.fromStableParcelable(testInfo);
+ resultData = KeepalivePacketDataUtil.toStableParcelable(testData);
assertArrayEquals(resultData.srcAddress, IPV4_KEEPALIVE_SRC_ADDR);
assertArrayEquals(resultData.dstAddress, IPV4_KEEPALIVE_DST_ADDR);
assertEquals(resultData.srcPort, srcPort);
@@ -154,4 +157,49 @@
+ " ack: 572662306, rcvWnd: 48000, rcvWndScale: 2, tos: 4, ttl: 64}";
assertEquals(expected, resultData.toString());
}
+
+ @Test
+ public void testParseTcpKeepalivePacketData() throws Exception {
+ final int srcPort = 1234;
+ final int dstPort = 4321;
+ final int sequence = 0x11111111;
+ final int ack = 0x22222222;
+ final int wnd = 4800;
+ final int wndScale = 2;
+ final int tos = 4;
+ final int ttl = 64;
+ final TcpKeepalivePacketDataParcelable testParcel = new TcpKeepalivePacketDataParcelable();
+ testParcel.srcAddress = IPV4_KEEPALIVE_SRC_ADDR;
+ testParcel.srcPort = srcPort;
+ testParcel.dstAddress = IPV4_KEEPALIVE_DST_ADDR;
+ testParcel.dstPort = dstPort;
+ testParcel.seq = sequence;
+ testParcel.ack = ack;
+ testParcel.rcvWnd = wnd;
+ testParcel.rcvWndScale = wndScale;
+ testParcel.tos = tos;
+ testParcel.ttl = ttl;
+
+ final KeepalivePacketData testData =
+ KeepalivePacketDataUtil.fromStableParcelable(testParcel);
+ final TcpKeepalivePacketDataParcelable parsedParcelable =
+ KeepalivePacketDataUtil.parseTcpKeepalivePacketData(testData);
+ final TcpKeepalivePacketData roundTripData =
+ KeepalivePacketDataUtil.fromStableParcelable(parsedParcelable);
+
+ // Generated packet is the same, but rcvWnd / wndScale will differ if scale is non-zero
+ assertTrue(testData.getPacket().length > 0);
+ assertArrayEquals(testData.getPacket(), roundTripData.getPacket());
+
+ testParcel.rcvWndScale = 0;
+ final KeepalivePacketData noScaleTestData =
+ KeepalivePacketDataUtil.fromStableParcelable(testParcel);
+ final TcpKeepalivePacketDataParcelable noScaleParsedParcelable =
+ KeepalivePacketDataUtil.parseTcpKeepalivePacketData(noScaleTestData);
+ final TcpKeepalivePacketData noScaleRoundTripData =
+ KeepalivePacketDataUtil.fromStableParcelable(noScaleParsedParcelable);
+ assertEquals(noScaleTestData, noScaleRoundTripData);
+ assertTrue(noScaleTestData.getPacket().length > 0);
+ assertArrayEquals(noScaleTestData.getPacket(), noScaleRoundTripData.getPacket());
+ }
}
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 91c9a2a..6de31f6 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -22,11 +22,11 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.net.util.MacAddressUtils;
-
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.MacAddressUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 9d308c5..a68044a 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -39,6 +39,7 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
+import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
@@ -101,6 +102,8 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.AdditionalMatchers.aryEq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
@@ -132,6 +135,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -140,6 +144,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.location.LocationManager;
@@ -176,6 +182,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
@@ -188,6 +195,7 @@
import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.UidRange;
+import android.net.UidRangeParcel;
import android.net.Uri;
import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
@@ -331,12 +339,13 @@
private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
private static final String VPN_IFNAME = "tun10042";
private static final String TEST_PACKAGE_NAME = "com.android.test.package";
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
+ private static final String ALWAYS_ON_PACKAGE = "com.android.test.alwaysonvpn";
private static final String INTERFACE_NAME = "interface";
private MockContext mServiceContext;
private HandlerThread mCsHandlerThread;
+ private ConnectivityService.Dependencies mDeps;
private ConnectivityService mService;
private WrappedConnectivityManager mCm;
private TestNetworkAgentWrapper mWiFiNetworkAgent;
@@ -352,6 +361,7 @@
@Mock IIpConnectivityMetrics mIpConnectivityMetrics;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
+ @Mock DeviceIdleInternal mDeviceIdleInternal;
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
@Mock IBatteryStats mBatteryStatsService;
@@ -449,6 +459,15 @@
}
@Override
+ public ComponentName startService(Intent service) {
+ final String action = service.getAction();
+ if (!VpnConfig.SERVICE_INTERFACE.equals(action)) {
+ fail("Attempt to start unknown service, action=" + action);
+ }
+ return new ComponentName(service.getPackage(), "com.android.test.Service");
+ }
+
+ @Override
public Object getSystemService(String name) {
if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
@@ -1054,9 +1073,19 @@
private VpnInfo mVpnInfo;
public MockVpn(int userId) {
- super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
- userId, mock(KeyStore.class));
- mConfig = new VpnConfig();
+ super(startHandlerThreadAndReturnLooper(), mServiceContext,
+ new Dependencies() {
+ @Override
+ public boolean isCallerSystem() {
+ return true;
+ }
+
+ @Override
+ public DeviceIdleInternal getDeviceIdleInternal() {
+ return mDeviceIdleInternal;
+ }
+ },
+ mNetworkManagementService, mMockNetd, userId, mock(KeyStore.class));
}
public void setUids(Set<UidRange> uids) {
@@ -1085,26 +1114,35 @@
return mVpnType;
}
+ private LinkProperties makeLinkProperties() {
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(VPN_IFNAME);
+ return lp;
+ }
+
private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp)
throws Exception {
if (mAgentRegistered) throw new IllegalStateException("already registered");
+ mConfig = new VpnConfig();
setUids(uids);
if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
mInterface = VPN_IFNAME;
mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
mNetworkCapabilities);
mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
- verify(mNetworkManagementService, times(1))
- .addVpnUidRanges(eq(mMockVpn.getNetId()), eq(uids.toArray(new UidRange[0])));
- verify(mNetworkManagementService, never())
- .removeVpnUidRanges(eq(mMockVpn.getNetId()), any());
+
+ verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()),
+ eq(toUidRangeStableParcels(uids)));
+ verify(mMockNetd, never())
+ .networkRemoveUidRanges(eq(mMockVpn.getNetId()), any());
mAgentRegistered = true;
+ updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent");
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
}
private void registerAgent(Set<UidRange> uids) throws Exception {
- registerAgent(false /* isAlwaysMetered */, uids, new LinkProperties());
+ registerAgent(false /* isAlwaysMetered */, uids, makeLinkProperties());
}
private void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
@@ -1140,12 +1178,12 @@
public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode)
throws Exception {
final int uid = Process.myUid();
- establish(new LinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet,
+ establish(makeLinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet,
isStrictMode);
}
public void establishForMyUid() throws Exception {
- establishForMyUid(new LinkProperties());
+ establishForMyUid(makeLinkProperties());
}
public void sendLinkProperties(LinkProperties lp) {
@@ -1153,7 +1191,10 @@
}
public void disconnect() {
- if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect();
+ if (mMockNetworkAgent != null) {
+ mMockNetworkAgent.disconnect();
+ updateState(NetworkInfo.DetailedState.DISCONNECTED, "disconnect");
+ }
mAgentRegistered = false;
}
@@ -1169,6 +1210,11 @@
}
}
+ private UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set<UidRange> ranges) {
+ return ranges.stream().map(
+ r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new);
+ }
+
private void mockVpn(int uid) {
synchronized (mService.mVpns) {
int userId = UserHandle.getUserId(uid);
@@ -1222,6 +1268,17 @@
fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
}
+ private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
+ int uid) {
+ when(mDeps.getCallingUid()).thenReturn(uid);
+ try {
+ mCm.registerNetworkCallback(request, callback);
+ waitForIdle();
+ } finally {
+ returnRealCallingUid();
+ }
+ }
+
private static final int VPN_USER = 0;
private static final int APP1_UID = UserHandle.getUid(VPN_USER, 10100);
private static final int APP2_UID = UserHandle.getUid(VPN_USER, 10101);
@@ -1264,7 +1321,8 @@
initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler());
mCsHandlerThread = new HandlerThread("TestConnectivityService");
- final ConnectivityService.Dependencies deps = makeDependencies();
+ mDeps = makeDependencies();
+ returnRealCallingUid();
mService = new ConnectivityService(mServiceContext,
mNetworkManagementService,
mStatsService,
@@ -1272,9 +1330,9 @@
mMockDnsResolver,
mock(IpConnectivityLog.class),
mMockNetd,
- deps);
+ mDeps);
mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
- verify(deps).makeMultinetworkPolicyTracker(any(), any(), any());
+ verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
ArgumentCaptor.forClass(INetworkPolicyListener.class);
@@ -1294,6 +1352,10 @@
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
}
+ private void returnRealCallingUid() {
+ doAnswer((invocationOnMock) -> Binder.getCallingUid()).when(mDeps).getCallingUid();
+ }
+
private ConnectivityService.Dependencies makeDependencies() {
doReturn(TEST_TCP_INIT_RWND).when(mSystemProperties)
.getInt("net.tcp.default_init_rwnd", 0);
@@ -1369,13 +1431,13 @@
}
private void mockDefaultPackages() throws Exception {
- final String testPackageName = mContext.getPackageName();
- final PackageInfo testPackageInfo = mContext.getPackageManager().getPackageInfo(
- testPackageName, PackageManager.GET_PERMISSIONS);
+ final String myPackageName = mContext.getPackageName();
+ final PackageInfo myPackageInfo = mContext.getPackageManager().getPackageInfo(
+ myPackageName, PackageManager.GET_PERMISSIONS);
when(mPackageManager.getPackagesForUid(Binder.getCallingUid())).thenReturn(
- new String[] {testPackageName});
- when(mPackageManager.getPackageInfoAsUser(eq(testPackageName), anyInt(),
- eq(UserHandle.getCallingUserId()))).thenReturn(testPackageInfo);
+ new String[] {myPackageName});
+ when(mPackageManager.getPackageInfoAsUser(eq(myPackageName), anyInt(),
+ eq(UserHandle.getCallingUserId()))).thenReturn(myPackageInfo);
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
@@ -1383,6 +1445,25 @@
buildPackageInfo(/* SYSTEM */ false, APP2_UID),
buildPackageInfo(/* SYSTEM */ false, VPN_UID)
}));
+
+ // Create a fake always-on VPN package.
+ final int userId = UserHandle.getCallingUserId();
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; // Always-on supported in N+.
+ when(mPackageManager.getApplicationInfoAsUser(eq(ALWAYS_ON_PACKAGE), anyInt(),
+ eq(userId))).thenReturn(applicationInfo);
+
+ // Minimal mocking to keep Vpn#isAlwaysOnPackageSupported happy.
+ ResolveInfo rInfo = new ResolveInfo();
+ rInfo.serviceInfo = new ServiceInfo();
+ rInfo.serviceInfo.metaData = new Bundle();
+ final List<ResolveInfo> services = Arrays.asList(new ResolveInfo[]{rInfo});
+ when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA),
+ eq(userId))).thenReturn(services);
+ when(mPackageManager.getPackageUidAsUser(TEST_PACKAGE_NAME, userId))
+ .thenReturn(Process.myUid());
+ when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, userId))
+ .thenReturn(VPN_UID);
}
private void verifyActiveNetwork(int transport) {
@@ -2245,10 +2326,10 @@
}
private void grantUsingBackgroundNetworksPermissionForUid(final int uid) throws Exception {
- final String testPackageName = mContext.getPackageName();
- when(mPackageManager.getPackageInfo(eq(testPackageName), eq(GET_PERMISSIONS)))
+ final String myPackageName = mContext.getPackageName();
+ when(mPackageManager.getPackageInfo(eq(myPackageName), eq(GET_PERMISSIONS)))
.thenReturn(buildPackageInfo(true, uid));
- mService.mPermissionMonitor.onPackageAdded(testPackageName, uid);
+ mService.mPermissionMonitor.onPackageAdded(myPackageName, uid);
}
@Test
@@ -4947,8 +5028,8 @@
expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
- // Captive portal change shouldn't update ifaces
- mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+ // Temp metered change shouldn't update ifaces
+ mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
waitForIdle();
verify(mStatsService, never())
.forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
@@ -5464,6 +5545,7 @@
final Network wifi = mWiFiNetworkAgent.getNetwork();
final NetworkCapabilities initialCaps = new NetworkCapabilities();
+ initialCaps.addTransportType(TRANSPORT_VPN);
initialCaps.addCapability(NET_CAPABILITY_INTERNET);
initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN);
@@ -5495,44 +5577,45 @@
withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20);
+ final NetworkCapabilities initialCapsNotMetered = new NetworkCapabilities(initialCaps);
+ initialCapsNotMetered.addCapability(NET_CAPABILITY_NOT_METERED);
+
NetworkCapabilities caps = new NetworkCapabilities(initialCaps);
- final boolean notDeclaredMetered = false;
- mService.applyUnderlyingCapabilities(new Network[]{}, caps, notDeclaredMetered);
+ mService.applyUnderlyingCapabilities(new Network[]{}, initialCapsNotMetered, caps);
assertEquals(withNoUnderlying, caps);
caps = new NetworkCapabilities(initialCaps);
- mService.applyUnderlyingCapabilities(new Network[]{null}, caps, notDeclaredMetered);
+ mService.applyUnderlyingCapabilities(new Network[]{null}, initialCapsNotMetered, caps);
assertEquals(withNoUnderlying, caps);
caps = new NetworkCapabilities(initialCaps);
- mService.applyUnderlyingCapabilities(new Network[]{mobile}, caps, notDeclaredMetered);
+ mService.applyUnderlyingCapabilities(new Network[]{mobile}, initialCapsNotMetered, caps);
assertEquals(withMobileUnderlying, caps);
- mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, notDeclaredMetered);
+ mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCapsNotMetered, caps);
assertEquals(withWifiUnderlying, caps);
- final boolean isDeclaredMetered = true;
withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
caps = new NetworkCapabilities(initialCaps);
- mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, isDeclaredMetered);
+ mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCaps, caps);
assertEquals(withWifiUnderlying, caps);
caps = new NetworkCapabilities(initialCaps);
- mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, caps, isDeclaredMetered);
+ mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, initialCaps, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
- caps, notDeclaredMetered);
+ initialCapsNotMetered, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
- caps, notDeclaredMetered);
+ initialCapsNotMetered, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
- mService.applyUnderlyingCapabilities(null, caps, notDeclaredMetered);
+ mService.applyUnderlyingCapabilities(null, initialCapsNotMetered, caps);
assertEquals(withWifiUnderlying, caps);
}
@@ -5822,10 +5905,21 @@
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
}
+ private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) {
+ final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser(
+ userId, "com.android.calling.package");
+ final String defaultCapsString = Arrays.toString(defaultCaps);
+ assertEquals(defaultCapsString, defaultCaps.length, networks.length);
+ final Set<NetworkCapabilities> defaultCapsSet = new ArraySet<>(defaultCaps);
+ for (NetworkAgentWrapper network : networks) {
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
+ final String msg = "Did not find " + nc + " in " + Arrays.toString(defaultCaps);
+ assertTrue(msg, defaultCapsSet.contains(nc));
+ }
+ }
+
@Test
public void testVpnSetUnderlyingNetworks() throws Exception {
- final int uid = Process.myUid();
-
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
.removeCapability(NET_CAPABILITY_NOT_VPN)
@@ -5848,6 +5942,9 @@
// A VPN without underlying networks is not suspended.
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ final int userId = UserHandle.getUserId(Process.myUid());
+ assertDefaultNetworkCapabilities(userId /* no networks */);
+
// Connect cell and use it as an underlying network.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -5861,6 +5958,7 @@
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -5875,6 +5973,7 @@
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Don't disconnect, but note the VPN is not using wifi any more.
mService.setUnderlyingNetworksForVpn(
@@ -5885,6 +5984,9 @@
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ // The return value of getDefaultNetworkCapabilitiesForUser always includes the default
+ // network (wifi) as well as the underlying networks (cell).
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Remove NOT_SUSPENDED from the only network and observe VPN is now suspended.
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -5913,6 +6015,7 @@
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
// Use both again.
mService.setUnderlyingNetworksForVpn(
@@ -5923,6 +6026,7 @@
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Cell is suspended again. As WiFi is not, this should not cause a callback.
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -5940,6 +6044,7 @@
// a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
// been public and are deprecated and slated for removal, there is no sense in spending
// resources fixing this bug now.
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Use both again.
mService.setUnderlyingNetworksForVpn(
@@ -5952,6 +6057,7 @@
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
// As above, the RESUMED callback not being sent here is a bug, but not a bug that's
// worth anybody's time to fix.
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Disconnect cell. Receive update without even removing the dead network from the
// underlying networks – it's dead anyway. Not metered any more.
@@ -5960,6 +6066,7 @@
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
// Disconnect wifi too. No underlying networks means this is now metered.
mWiFiNetworkAgent.disconnect();
@@ -5967,6 +6074,11 @@
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ // When a network disconnects, the callbacks are fired before all state is updated, so for a
+ // short time, synchronous calls will behave as if the network is still connected. Wait for
+ // things to settle.
+ waitForIdle();
+ assertDefaultNetworkCapabilities(userId /* no networks */);
mMockVpn.disconnect();
}
@@ -6267,6 +6379,7 @@
// Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered.
assertTrue(mCm.isActiveNetworkMetered());
+
// VPN explicitly declares WiFi as its underlying network.
mService.setUnderlyingNetworksForVpn(
new Network[] { mWiFiNetworkAgent.getNetwork() });
@@ -6380,6 +6493,189 @@
mCm.unregisterNetworkCallback(defaultCallback);
}
+ private void expectNetworkRejectNonSecureVpn(InOrder inOrder, boolean add,
+ UidRangeParcel... expected) throws Exception {
+ inOrder.verify(mMockNetd).networkRejectNonSecureVpn(eq(add), aryEq(expected));
+ }
+
+ private void checkNetworkInfo(NetworkInfo ni, int type, DetailedState state) {
+ assertNotNull(ni);
+ assertEquals(type, ni.getType());
+ assertEquals(ConnectivityManager.getNetworkTypeName(type), state, ni.getDetailedState());
+ }
+
+ private void assertActiveNetworkInfo(int type, DetailedState state) {
+ checkNetworkInfo(mCm.getActiveNetworkInfo(), type, state);
+ }
+ private void assertNetworkInfo(int type, DetailedState state) {
+ checkNetworkInfo(mCm.getNetworkInfo(type), type, state);
+ }
+
+ @Test
+ public void testNetworkBlockedStatusAlwaysOnVpn() throws Exception {
+ mServiceContext.setPermission(
+ Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED);
+ mServiceContext.setPermission(
+ Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
+ mServiceContext.setPermission(
+ Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .build();
+ mCm.registerNetworkCallback(request, callback);
+
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ final TestNetworkCallback vpnUidCallback = new TestNetworkCallback();
+ final NetworkRequest vpnUidRequest = new NetworkRequest.Builder().build();
+ registerNetworkCallbackAsUid(vpnUidRequest, vpnUidCallback, VPN_UID);
+
+ final int uid = Process.myUid();
+ final int userId = UserHandle.getUserId(uid);
+ final ArrayList<String> allowList = new ArrayList<>();
+ mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+
+ UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
+ UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999);
+ InOrder inOrder = inOrder(mMockNetd);
+ expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
+
+ // Connect a network when lockdown is active, expect to see it blocked.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false /* validated */);
+ callback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+ vpnUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+ // Mobile is BLOCKED even though it's not actually connected.
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+
+ // Disable lockdown, expect to see the network unblocked.
+ // There are no callbacks because they are not implemented yet.
+ mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
+ vpnUidCallback.assertNoCallback();
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+ // Add our UID to the allowlist and re-enable lockdown, expect network is not blocked.
+ allowList.add(TEST_PACKAGE_NAME);
+ mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ callback.assertNoCallback();
+ defaultCallback.assertNoCallback();
+ vpnUidCallback.assertNoCallback();
+
+ // The following requires that the UID of this test package is greater than VPN_UID. This
+ // is always true in practice because a plain AOSP build with no apps installed has almost
+ // 200 packages installed.
+ final UidRangeParcel piece1 = new UidRangeParcel(1, VPN_UID - 1);
+ final UidRangeParcel piece2 = new UidRangeParcel(VPN_UID + 1, uid - 1);
+ final UidRangeParcel piece3 = new UidRangeParcel(uid + 1, 99999);
+ expectNetworkRejectNonSecureVpn(inOrder, true, piece1, piece2, piece3);
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+ // Connect a new network, expect it to be unblocked.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(false /* validated */);
+ callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ defaultCallback.assertNoCallback();
+ vpnUidCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ // Cellular is DISCONNECTED because it's not the default and there are no requests for it.
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+ // Disable lockdown, remove our UID from the allowlist, and re-enable lockdown.
+ // Everything should now be blocked.
+ mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3);
+ allowList.clear();
+ mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
+ vpnUidCallback.assertNoCallback();
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+
+ // Disable lockdown. Everything is unblocked.
+ mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ vpnUidCallback.assertNoCallback();
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+ // Enable and disable an always-on VPN package without lockdown. Expect no changes.
+ reset(mMockNetd);
+ mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, false /* lockdown */, allowList);
+ inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any());
+ callback.assertNoCallback();
+ defaultCallback.assertNoCallback();
+ vpnUidCallback.assertNoCallback();
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+ mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any());
+ callback.assertNoCallback();
+ defaultCallback.assertNoCallback();
+ vpnUidCallback.assertNoCallback();
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+ // Enable lockdown and connect a VPN. The VPN is not blocked.
+ mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ vpnUidCallback.assertNoCallback();
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+
+ mMockVpn.establishForMyUid();
+ defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability.
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(null, mCm.getActiveNetworkForUid(VPN_UID)); // BUG?
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+ mMockVpn.disconnect();
+ defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+ assertNull(mCm.getActiveNetwork());
+
+ mCm.unregisterNetworkCallback(callback);
+ mCm.unregisterNetworkCallback(defaultCallback);
+ mCm.unregisterNetworkCallback(vpnUidCallback);
+ }
+
@Test
public final void testLoseTrusted() throws Exception {
final NetworkRequest trustedRequest = new NetworkRequest.Builder()
@@ -7813,7 +8109,16 @@
@Test
public void testDumpDoesNotCrash() {
- StringWriter stringWriter = new StringWriter();
+ // Filing a couple requests prior to testing the dump.
+ final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
+ final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest genericRequest = new NetworkRequest.Builder()
+ .clearCapabilities().build();
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ mCm.registerNetworkCallback(genericRequest, genericNetworkCallback);
+ mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+ final StringWriter stringWriter = new StringWriter();
mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
@@ -7835,11 +8140,11 @@
mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
- ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
+ final ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
assertTrue(nriOutput.length > 1);
for (int i = 0; i < nriOutput.length - 1; i++) {
- boolean isRequestIdInOrder =
+ final boolean isRequestIdInOrder =
nriOutput[i].mRequests.get(0).requestId
< nriOutput[i + 1].mRequests.get(0).requestId;
assertTrue(isRequestIdInOrder);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 337507a..cc47317 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -58,6 +58,7 @@
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.ConnectivityManager;
+import android.net.INetd;
import android.net.Ikev2VpnProfile;
import android.net.InetAddresses;
import android.net.IpPrefix;
@@ -70,6 +71,7 @@
import android.net.NetworkInfo.DetailedState;
import android.net.RouteInfo;
import android.net.UidRange;
+import android.net.UidRangeParcel;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.ipsec.ike.IkeSessionCallback;
@@ -172,11 +174,13 @@
mPackages.put(PKGS[i], PKG_UIDS[i]);
}
}
+ private static final UidRange PRI_USER_RANGE = UidRange.createForUser(primaryUser.id);
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext;
@Mock private UserManager mUserManager;
@Mock private PackageManager mPackageManager;
@Mock private INetworkManagementService mNetService;
+ @Mock private INetd mNetd;
@Mock private AppOpsManager mAppOps;
@Mock private NotificationManager mNotificationManager;
@Mock private Vpn.SystemServices mSystemServices;
@@ -224,7 +228,6 @@
R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS))
.thenReturn(true);
- when(mSystemServices.isCallerSystem()).thenReturn(true);
// Used by {@link Notification.Builder}
ApplicationInfo applicationInfo = new ApplicationInfo();
@@ -256,8 +259,7 @@
null, null);
assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
- UidRange.createForUser(primaryUser.id),
- UidRange.createForUser(restrictedProfileA.id)
+ PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
})), ranges);
}
@@ -269,9 +271,7 @@
final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
null, null);
- assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
- UidRange.createForUser(primaryUser.id)
- })), ranges);
+ assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { PRI_USER_RANGE })), ranges);
}
@Test
@@ -282,15 +282,13 @@
final Set<UidRange> ranges = new ArraySet<>();
vpn.addUserToRanges(ranges, primaryUser.id, null, null);
- assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
- UidRange.createForUser(primaryUser.id)
- })), ranges);
+ assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { PRI_USER_RANGE })), ranges);
}
@Test
public void testUidAllowAndDenylist() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
- final UidRange user = UidRange.createForUser(primaryUser.id);
+ final UidRange user = PRI_USER_RANGE;
final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
// Allowed list
@@ -339,62 +337,67 @@
@Test
public void testLockdownChangingPackage() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
- final UidRange user = UidRange.createForUser(primaryUser.id);
+ final UidRange user = PRI_USER_RANGE;
// Default state.
- assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+ assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
+ user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on without lockdown.
assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore));
- assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+ assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
+ user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on with lockdown.
assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
- new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
- assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+
+ assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2],
+ user.start + PKG_UIDS[3]);
assertUnblocked(vpn, user.start + PKG_UIDS[1]);
// Switch to another app.
assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
- new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
- new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
- new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
}));
- assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
+ assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
+ user.start + PKG_UIDS[2]);
assertUnblocked(vpn, user.start + PKG_UIDS[3]);
}
@Test
public void testLockdownAllowlist() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
- final UidRange user = UidRange.createForUser(primaryUser.id);
+ final UidRange user = PRI_USER_RANGE;
// Set always-on with lockdown and allow app PKGS[2] from lockdown.
assertTrue(vpn.setAlwaysOnPackage(
PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
- new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
}));
assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
-
// Change allowed app list to PKGS[3].
assertTrue(vpn.setAlwaysOnPackage(
PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
}));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
- new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
}));
assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2]);
assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]);
@@ -402,25 +405,25 @@
// Change the VPN app.
assertTrue(vpn.setAlwaysOnPackage(
PKGS[0], true, Collections.singletonList(PKGS[3]), mKeyStore));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
- new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
- new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
+ verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
}));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start, user.start + PKG_UIDS[0] - 1),
- new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start, user.start + PKG_UIDS[0] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
}));
assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
// Remove the list of allowed packages.
assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
- new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
}));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[0] + 1, user.stop),
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop),
}));
assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2],
user.start + PKG_UIDS[3]);
@@ -429,12 +432,12 @@
// Add the list of allowed packages.
assertTrue(vpn.setAlwaysOnPackage(
PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[0] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop)
}));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
- new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
assertBlocked(vpn, user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]);
@@ -447,13 +450,13 @@
// allowed package should change from PGKS[1] to PKGS[2].
assertTrue(vpn.setAlwaysOnPackage(
PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{
- new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
- new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[]{
+ new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[]{
- new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
- new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[]{
+ new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
}));
}
@@ -467,86 +470,86 @@
restrictedProfileA.flags);
tempProfile.restrictedProfileParentId = primaryUser.id;
- final UidRange user = UidRange.createForUser(primaryUser.id);
+ final UidRange user = PRI_USER_RANGE;
final UidRange profile = UidRange.createForUser(tempProfile.id);
// Set lockdown.
assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
- new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
+ new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
}));
-
// Verify restricted user isn't affected at first.
assertUnblocked(vpn, profile.start + PKG_UIDS[0]);
// Add the restricted user.
setMockedUsers(primaryUser, tempProfile);
vpn.onUserAdded(tempProfile.id);
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
- new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
+ new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
}));
// Remove the restricted user.
tempProfile.partial = true;
vpn.onUserRemoved(tempProfile.id);
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
- new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
- new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
+ verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
+ new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
}));
}
@Test
public void testLockdownRuleRepeatability() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
-
+ final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
+ new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
// Given legacy lockdown is already enabled,
vpn.setLockdown(true);
- verify(mNetService, times(1)).setAllowOnlyVpnForUids(
- eq(true), aryEq(new UidRange[] {UidRange.createForUser(primaryUser.id)}));
+
+ verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(primaryUserRangeParcel));
// Enabling legacy lockdown twice should do nothing.
vpn.setLockdown(true);
- verify(mNetService, times(1)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
+ verify(mNetd, times(1))
+ .networkRejectNonSecureVpn(anyBoolean(), any(UidRangeParcel[].class));
// And disabling should remove the rules exactly once.
vpn.setLockdown(false);
- verify(mNetService, times(1)).setAllowOnlyVpnForUids(
- eq(false), aryEq(new UidRange[] {UidRange.createForUser(primaryUser.id)}));
+ verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(primaryUserRangeParcel));
// Removing the lockdown again should have no effect.
vpn.setLockdown(false);
- verify(mNetService, times(2)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
+ verify(mNetd, times(2)).networkRejectNonSecureVpn(
+ anyBoolean(), any(UidRangeParcel[].class));
}
@Test
public void testLockdownRuleReversibility() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
-
- final UidRange[] entireUser = {
- UidRange.createForUser(primaryUser.id)
+ final UidRangeParcel[] entireUser = {
+ new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
};
- final UidRange[] exceptPkg0 = {
- new UidRange(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
- new UidRange(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
+ final UidRangeParcel[] exceptPkg0 = {
+ new UidRangeParcel(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
+ new UidRangeParcel(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
};
- final InOrder order = inOrder(mNetService);
+ final InOrder order = inOrder(mNetd);
// Given lockdown is enabled with no package (legacy VPN),
vpn.setLockdown(true);
- order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
+ order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(entireUser));
// When a new VPN package is set the rules should change to cover that package.
vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
- order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
- order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
+ order.verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(entireUser));
+ order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(exceptPkg0));
// When that VPN package is unset, everything should be undone again in reverse.
vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
- order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
- order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
+ order.verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(exceptPkg0));
+ order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(entireUser));
}
@Test
@@ -1098,6 +1101,11 @@
}
@Override
+ public boolean isCallerSystem() {
+ return true;
+ }
+
+ @Override
public void startService(final String serviceName) {
mRunningServices.put(serviceName, true);
}
@@ -1186,7 +1194,7 @@
.thenReturn(asUserContext);
final TestLooper testLooper = new TestLooper();
final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
- userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
+ mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat(
provider -> provider.getName().contains("VpnNetworkProvider")
));
diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
new file mode 100644
index 0000000..77944de
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnConfigTest {
+ private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS =
+ Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig());
+
+ // Public visibility for VcnManagementServiceTest
+ public static VcnConfig buildTestConfig() {
+ VcnConfig.Builder builder = new VcnConfig.Builder();
+
+ for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) {
+ builder.addGatewayConnectionConfig(gatewayConnectionConfig);
+ }
+
+ return builder.build();
+ }
+
+ @Test
+ public void testBuilderRequiresGatewayConnectionConfig() {
+ try {
+ new VcnConfig.Builder().build();
+ fail("Expected exception due to no VcnGatewayConnectionConfigs provided");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ final VcnConfig config = buildTestConfig();
+
+ assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs());
+ }
+
+ @Test
+ public void testPersistableBundle() {
+ final VcnConfig config = buildTestConfig();
+
+ assertEquals(config, new VcnConfig(config.toPersistableBundle()));
+ }
+
+ @Test
+ public void testParceling() {
+ final VcnConfig config = buildTestConfig();
+
+ Parcel parcel = Parcel.obtain();
+ config.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ assertEquals(config, VcnConfig.CREATOR.createFromParcel(parcel));
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
new file mode 100644
index 0000000..e98b6ef
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.net.NetworkCapabilities;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConfigTest {
+ private static final int[] EXPOSED_CAPS =
+ new int[] {
+ NetworkCapabilities.NET_CAPABILITY_INTERNET, NetworkCapabilities.NET_CAPABILITY_MMS
+ };
+ private static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
+ private static final long[] RETRY_INTERVALS_MS =
+ new long[] {
+ TimeUnit.SECONDS.toMillis(5),
+ TimeUnit.SECONDS.toMillis(30),
+ TimeUnit.MINUTES.toMillis(1),
+ TimeUnit.MINUTES.toMillis(5),
+ TimeUnit.MINUTES.toMillis(15),
+ TimeUnit.MINUTES.toMillis(30)
+ };
+ private static final int MAX_MTU = 1360;
+
+ // Package protected for use in VcnConfigTest
+ static VcnGatewayConnectionConfig buildTestConfig() {
+ final VcnGatewayConnectionConfig.Builder builder =
+ new VcnGatewayConnectionConfig.Builder()
+ .setRetryInterval(RETRY_INTERVALS_MS)
+ .setMaxMtu(MAX_MTU);
+
+ for (int caps : EXPOSED_CAPS) {
+ builder.addExposedCapability(caps);
+ }
+
+ for (int caps : UNDERLYING_CAPS) {
+ builder.addRequiredUnderlyingCapability(caps);
+ }
+
+ return builder.build();
+ }
+
+ @Test
+ public void testBuilderRequiresNonEmptyExposedCaps() {
+ try {
+ new VcnGatewayConnectionConfig.Builder()
+ .addRequiredUnderlyingCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+
+ fail("Expected exception due to invalid exposed capabilities");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresNonEmptyUnderlyingCaps() {
+ try {
+ new VcnGatewayConnectionConfig.Builder()
+ .addExposedCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+
+ fail("Expected exception due to invalid required underlying capabilities");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresNonNullRetryInterval() {
+ try {
+ new VcnGatewayConnectionConfig.Builder().setRetryInterval(null);
+ fail("Expected exception due to invalid retryIntervalMs");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresNonEmptyRetryInterval() {
+ try {
+ new VcnGatewayConnectionConfig.Builder().setRetryInterval(new long[0]);
+ fail("Expected exception due to invalid retryIntervalMs");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresValidMtu() {
+ try {
+ new VcnGatewayConnectionConfig.Builder()
+ .setMaxMtu(VcnGatewayConnectionConfig.MIN_MTU_V6 - 1);
+ fail("Expected exception due to invalid mtu");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ final VcnGatewayConnectionConfig config = buildTestConfig();
+
+ for (int cap : EXPOSED_CAPS) {
+ config.hasExposedCapability(cap);
+ }
+ for (int cap : UNDERLYING_CAPS) {
+ config.requiresUnderlyingCapability(cap);
+ }
+
+ assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
+ assertEquals(MAX_MTU, config.getMaxMtu());
+ }
+
+ @Test
+ public void testPersistableBundle() {
+ final VcnGatewayConnectionConfig config = buildTestConfig();
+
+ assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
+ }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index c91fdbf..1cc9532 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -16,42 +16,123 @@
package com.android.server;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnConfigTest;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.UserHandle;
import android.os.test.TestLooper;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.FileNotFoundException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.UUID;
+
/** Tests for {@link VcnManagementService}. */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnManagementServiceTest {
+ private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0));
+ private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1));
+ private static final VcnConfig TEST_VCN_CONFIG = VcnConfigTest.buildTestConfig();
+ private static final Map<ParcelUuid, VcnConfig> TEST_VCN_CONFIG_MAP =
+ Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG));
+
+ private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO =
+ new SubscriptionInfo(
+ 1 /* id */,
+ "" /* iccId */,
+ 0 /* simSlotIndex */,
+ "Carrier" /* displayName */,
+ "Carrier" /* carrierName */,
+ 0 /* nameSource */,
+ 255 /* iconTint */,
+ "12345" /* number */,
+ 0 /* roaming */,
+ null /* icon */,
+ "0" /* mcc */,
+ "0" /* mnc */,
+ "0" /* countryIso */,
+ false /* isEmbedded */,
+ null /* nativeAccessRules */,
+ null /* cardString */,
+ false /* isOpportunistic */,
+ TEST_UUID_1.toString() /* groupUUID */,
+ 0 /* carrierId */,
+ 0 /* profileClass */);
+
private final Context mMockContext = mock(Context.class);
private final VcnManagementService.Dependencies mMockDeps =
mock(VcnManagementService.Dependencies.class);
private final TestLooper mTestLooper = new TestLooper();
private final ConnectivityManager mConnMgr = mock(ConnectivityManager.class);
+ private final TelephonyManager mTelMgr = mock(TelephonyManager.class);
+ private final SubscriptionManager mSubMgr = mock(SubscriptionManager.class);
private final VcnManagementService mVcnMgmtSvc;
+ private final PersistableBundleUtils.LockingReadWriteHelper mConfigReadWriteHelper =
+ mock(PersistableBundleUtils.LockingReadWriteHelper.class);
- public VcnManagementServiceTest() {
- doReturn(Context.CONNECTIVITY_SERVICE)
- .when(mMockContext)
- .getSystemServiceName(ConnectivityManager.class);
- doReturn(mConnMgr).when(mMockContext).getSystemService(Context.CONNECTIVITY_SERVICE);
+ public VcnManagementServiceTest() throws Exception {
+ setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
+ setupSystemService(
+ mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class);
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
+ doReturn(Process.FIRST_APPLICATION_UID).when(mMockDeps).getBinderCallingUid();
+ doReturn(mConfigReadWriteHelper)
+ .when(mMockDeps)
+ .newPersistableBundleLockingReadWriteHelper(any());
+
+ final PersistableBundle bundle =
+ PersistableBundleUtils.fromMap(
+ TEST_VCN_CONFIG_MAP,
+ PersistableBundleUtils::fromParcelUuid,
+ VcnConfig::toPersistableBundle);
+ doReturn(bundle).when(mConfigReadWriteHelper).readFromDisk();
+
+ setupMockedCarrierPrivilege(true);
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
}
+ private void setupSystemService(Object service, String name, Class<?> serviceClass) {
+ doReturn(name).when(mMockContext).getSystemServiceName(serviceClass);
+ doReturn(service).when(mMockContext).getSystemService(name);
+ }
+
+ private void setupMockedCarrierPrivilege(boolean isPrivileged) {
+ doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
+ .when(mSubMgr)
+ .getSubscriptionsInGroup(any());
+ doReturn(isPrivileged)
+ .when(mTelMgr)
+ .hasCarrierPrivileges(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId()));
+ }
+
@Test
public void testSystemReady() throws Exception {
mVcnMgmtSvc.systemReady();
@@ -59,4 +140,119 @@
verify(mConnMgr)
.registerNetworkProvider(any(VcnManagementService.VcnNetworkProvider.class));
}
+
+ @Test
+ public void testNonSystemServerRealConfigFileAccessPermission() throws Exception {
+ // Attempt to build a real instance of the dependencies, and verify we cannot write to the
+ // file.
+ VcnManagementService.Dependencies deps = new VcnManagementService.Dependencies();
+ PersistableBundleUtils.LockingReadWriteHelper configReadWriteHelper =
+ deps.newPersistableBundleLockingReadWriteHelper(
+ VcnManagementService.VCN_CONFIG_FILE);
+
+ // Even tests should not be able to read/write configs from disk; SELinux policies restrict
+ // it to only the system server.
+ // Reading config should always return null since the file "does not exist", and writing
+ // should throw an IOException.
+ assertNull(configReadWriteHelper.readFromDisk());
+
+ try {
+ configReadWriteHelper.writeToDisk(new PersistableBundle());
+ fail("Expected IOException due to SELinux policy");
+ } catch (FileNotFoundException expected) {
+ }
+ }
+
+ @Test
+ public void testLoadVcnConfigsOnStartup() throws Exception {
+ mTestLooper.dispatchAll();
+
+ assertEquals(TEST_VCN_CONFIG_MAP, mVcnMgmtSvc.getConfigs());
+ verify(mConfigReadWriteHelper).readFromDisk();
+ }
+
+ @Test
+ public void testSetVcnConfigRequiresNonSystemServer() throws Exception {
+ doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
+
+ try {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
+ fail("Expected IllegalStateException exception for system server");
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ @Test
+ public void testSetVcnConfigRequiresSystemUser() throws Exception {
+ doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID))
+ .when(mMockDeps)
+ .getBinderCallingUid();
+
+ try {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
+ fail("Expected security exception for non system user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testSetVcnConfigRequiresCarrierPrivileges() throws Exception {
+ setupMockedCarrierPrivilege(false);
+
+ try {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
+ fail("Expected security exception for missing carrier privileges");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testSetVcnConfig() throws Exception {
+ // Use a different UUID to simulate a new VCN config.
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG);
+ assertEquals(TEST_VCN_CONFIG, mVcnMgmtSvc.getConfigs().get(TEST_UUID_2));
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+ }
+
+ @Test
+ public void testClearVcnConfigRequiresNonSystemServer() throws Exception {
+ doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
+
+ try {
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+ fail("Expected IllegalStateException exception for system server");
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ @Test
+ public void testClearVcnConfigRequiresSystemUser() throws Exception {
+ doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID))
+ .when(mMockDeps)
+ .getBinderCallingUid();
+
+ try {
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+ fail("Expected security exception for non system user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testClearVcnConfigRequiresCarrierPrivileges() throws Exception {
+ setupMockedCarrierPrivilege(false);
+
+ try {
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+ fail("Expected security exception for missing carrier privileges");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testClearVcnConfig() throws Exception {
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+ assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
new file mode 100644
index 0000000..17b8f64
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
+import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/** Tests for TelephonySubscriptionTracker */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TelephonySubscriptionTrackerTest {
+ private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
+ private static final int TEST_SIM_SLOT_INDEX = 1;
+ private static final int TEST_SUBSCRIPTION_ID_1 = 2;
+ private static final SubscriptionInfo TEST_SUBINFO_1 = mock(SubscriptionInfo.class);
+ private static final int TEST_SUBSCRIPTION_ID_2 = 3;
+ private static final SubscriptionInfo TEST_SUBINFO_2 = mock(SubscriptionInfo.class);
+ private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP;
+
+ static {
+ final Map<Integer, ParcelUuid> subIdToGroupMap = new HashMap<>();
+ subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_PARCEL_UUID);
+ subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_PARCEL_UUID);
+ TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap);
+ }
+
+ @NonNull private final Context mContext;
+ @NonNull private final TestLooper mTestLooper;
+ @NonNull private final Handler mHandler;
+ @NonNull private final TelephonySubscriptionTracker.Dependencies mDeps;
+
+ @NonNull private final SubscriptionManager mSubscriptionManager;
+ @NonNull private final CarrierConfigManager mCarrierConfigManager;
+
+ @NonNull private TelephonySubscriptionTrackerCallback mCallback;
+ @NonNull private TelephonySubscriptionTracker mTelephonySubscriptionTracker;
+
+ public TelephonySubscriptionTrackerTest() {
+ mContext = mock(Context.class);
+ mTestLooper = new TestLooper();
+ mHandler = new Handler(mTestLooper.getLooper());
+ mDeps = mock(TelephonySubscriptionTracker.Dependencies.class);
+
+ mSubscriptionManager = mock(SubscriptionManager.class);
+ mCarrierConfigManager = mock(CarrierConfigManager.class);
+
+ doReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
+ .when(mContext)
+ .getSystemServiceName(SubscriptionManager.class);
+ doReturn(mSubscriptionManager)
+ .when(mContext)
+ .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+
+ doReturn(Context.CARRIER_CONFIG_SERVICE)
+ .when(mContext)
+ .getSystemServiceName(CarrierConfigManager.class);
+ doReturn(mCarrierConfigManager)
+ .when(mContext)
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+
+ // subId 1, 2 are in same subGrp, only subId 1 is active
+ doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid();
+ doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_2).getGroupUuid();
+ doReturn(TEST_SIM_SLOT_INDEX).when(TEST_SUBINFO_1).getSimSlotIndex();
+ doReturn(INVALID_SIM_SLOT_INDEX).when(TEST_SUBINFO_2).getSimSlotIndex();
+ doReturn(TEST_SUBSCRIPTION_ID_1).when(TEST_SUBINFO_1).getSubscriptionId();
+ doReturn(TEST_SUBSCRIPTION_ID_2).when(TEST_SUBINFO_2).getSubscriptionId();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mCallback = mock(TelephonySubscriptionTrackerCallback.class);
+ mTelephonySubscriptionTracker =
+ new TelephonySubscriptionTracker(mContext, mHandler, mCallback, mDeps);
+ mTelephonySubscriptionTracker.register();
+
+ doReturn(true).when(mDeps).isConfigForIdentifiedCarrier(any());
+ doReturn(Arrays.asList(TEST_SUBINFO_1, TEST_SUBINFO_2))
+ .when(mSubscriptionManager)
+ .getAllSubscriptionInfoList();
+ }
+
+ private IntentFilter getIntentFilter() {
+ final ArgumentCaptor<IntentFilter> captor = ArgumentCaptor.forClass(IntentFilter.class);
+ verify(mContext).registerReceiver(any(), captor.capture(), any(), any());
+
+ return captor.getValue();
+ }
+
+ private OnSubscriptionsChangedListener getOnSubscriptionsChangedListener() {
+ final ArgumentCaptor<OnSubscriptionsChangedListener> captor =
+ ArgumentCaptor.forClass(OnSubscriptionsChangedListener.class);
+ verify(mSubscriptionManager).addOnSubscriptionsChangedListener(any(), captor.capture());
+
+ return captor.getValue();
+ }
+
+ private Intent buildTestBroadcastIntent(boolean hasValidSubscription) {
+ Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED);
+ intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX);
+ intent.putExtra(
+ EXTRA_SUBSCRIPTION_INDEX,
+ hasValidSubscription ? TEST_SUBSCRIPTION_ID_1 : INVALID_SUBSCRIPTION_ID);
+
+ return intent;
+ }
+
+ private TelephonySubscriptionSnapshot buildExpectedSnapshot(Set<ParcelUuid> activeSubGroups) {
+ return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, activeSubGroups);
+ }
+
+ private TelephonySubscriptionSnapshot buildExpectedSnapshot(
+ Map<Integer, ParcelUuid> subIdToGroupMap, Set<ParcelUuid> activeSubGroups) {
+ return new TelephonySubscriptionSnapshot(subIdToGroupMap, activeSubGroups);
+ }
+
+ private void verifyNoActiveSubscriptions() {
+ verify(mCallback).onNewSnapshot(
+ argThat(snapshot -> snapshot.getActiveSubscriptionGroups().isEmpty()));
+ }
+
+ private void setupReadySubIds() {
+ mTelephonySubscriptionTracker.setReadySubIdsBySlotId(
+ Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1));
+ }
+
+ @Test
+ public void testRegister() throws Exception {
+ verify(mContext)
+ .registerReceiver(
+ eq(mTelephonySubscriptionTracker),
+ any(IntentFilter.class),
+ any(),
+ eq(mHandler));
+ final IntentFilter filter = getIntentFilter();
+ assertEquals(1, filter.countActions());
+ assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED));
+
+ verify(mSubscriptionManager)
+ .addOnSubscriptionsChangedListener(any(HandlerExecutor.class), any());
+ assertNotNull(getOnSubscriptionsChangedListener());
+ }
+
+ @Test
+ public void testUnregister() throws Exception {
+ mTelephonySubscriptionTracker.unregister();
+
+ verify(mContext).unregisterReceiver(eq(mTelephonySubscriptionTracker));
+
+ final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
+ verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(eq(listener));
+ }
+
+ @Test
+ public void testOnSubscriptionsChangedFired_NoReadySubIds() throws Exception {
+ final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
+ listener.onSubscriptionsChanged();
+ mTestLooper.dispatchAll();
+
+ verifyNoActiveSubscriptions();
+ }
+
+ @Test
+ public void testOnSubscriptionsChangedFired_WithReadySubIds() throws Exception {
+ setupReadySubIds();
+
+ final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
+ listener.onSubscriptionsChanged();
+ mTestLooper.dispatchAll();
+
+ final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
+ }
+
+ @Test
+ public void testReceiveBroadcast_ConfigReadyWithSubscriptions() throws Exception {
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+
+ final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
+ }
+
+ @Test
+ public void testReceiveBroadcast_ConfigReadyNoSubscriptions() throws Exception {
+ doReturn(new ArrayList<SubscriptionInfo>())
+ .when(mSubscriptionManager)
+ .getAllSubscriptionInfoList();
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+
+ // Expect an empty snapshot
+ verify(mCallback).onNewSnapshot(
+ eq(buildExpectedSnapshot(Collections.emptyMap(), Collections.emptySet())));
+ }
+
+ @Test
+ public void testReceiveBroadcast_SlotCleared() throws Exception {
+ setupReadySubIds();
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
+ mTestLooper.dispatchAll();
+
+ verifyNoActiveSubscriptions();
+ assertTrue(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().isEmpty());
+ }
+
+ @Test
+ public void testReceiveBroadcast_ConfigNotReady() throws Exception {
+ doReturn(false).when(mDeps).isConfigForIdentifiedCarrier(any());
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+
+ // No interactions expected; config was not loaded
+ verifyNoMoreInteractions(mCallback);
+ }
+
+ @Test
+ public void testSubscriptionsClearedAfterValidTriggersCallbacks() throws Exception {
+ final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
+ assertNotNull(
+ mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
+
+ doReturn(Collections.emptyList()).when(mSubscriptionManager).getAllSubscriptionInfoList();
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+ verify(mCallback).onNewSnapshot(
+ eq(buildExpectedSnapshot(Collections.emptyMap(), Collections.emptySet())));
+ }
+
+ @Test
+ public void testSlotClearedAfterValidTriggersCallbacks() throws Exception {
+ final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
+ assertNotNull(
+ mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
+ mTestLooper.dispatchAll();
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(Collections.emptySet())));
+ assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
+ }
+
+ @Test
+ public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
+ final TelephonySubscriptionSnapshot snapshot =
+ new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, Collections.emptySet());
+
+ assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
+ assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
+ }
+
+ @Test
+ public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
+ final TelephonySubscriptionSnapshot snapshot =
+ new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, Collections.emptySet());
+
+ assertEquals(
+ new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
+ snapshot.getAllSubIdsInGroup(TEST_PARCEL_UUID));
+ }
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index ab6dced..dd3ebdb 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -519,7 +519,7 @@
String8(parser.getElementName(&len)).string(), attr);
return ATTR_NOT_FOUND;
}
- if ((str=pool->stringAt(value.data, &len)) == NULL) {
+ if ((str = UnpackOptionalString(pool->stringAt(value.data), &len)) == NULL) {
fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n",
path.string(), parser.getLineNumber(),
String8(parser.getElementName(&len)).string(), attr);
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index d02f44e..257e96b 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -3066,7 +3066,7 @@
for (size_t ti=0; ti<N; ti++) {
// Retrieve them in the same order as the type string block.
size_t len;
- String16 typeName(p->getTypeStrings().stringAt(ti, &len));
+ String16 typeName(UnpackOptionalString(p->getTypeStrings().stringAt(ti), &len));
sp<Type> t = p->getTypes().valueFor(typeName);
LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
"Type name %s not found",
@@ -4169,7 +4169,7 @@
const size_t N = strings->size();
for (size_t i=0; i<N; i++) {
size_t len;
- mappings->add(String16(strings->stringAt(i, &len)), i);
+ mappings->add(String16(UnpackOptionalString(strings->stringAt(i), &len)), i);
}
}
return err;
diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp
index 37b61bf..6cacd32 100644
--- a/tools/aapt/StringPool.cpp
+++ b/tools/aapt/StringPool.cpp
@@ -52,9 +52,9 @@
for (size_t i=0; i<N; i++) {
size_t len;
if (pool->isUTF8()) {
- uniqueStrings.add(pool->string8At(i, &len));
+ uniqueStrings.add(UnpackOptionalString(pool->string8At(i), &len));
} else {
- uniqueStrings.add(pool->stringAt(i, &len));
+ uniqueStrings.add(UnpackOptionalString(pool->stringAt(i), &len));
}
}
@@ -66,8 +66,8 @@
const size_t NS = pool->size();
for (size_t s=0; s<NS; s++) {
- String8 str = pool->string8ObjectAt(s);
- printf("String #" ZD ": %s\n", (ZD_TYPE) s, str.string());
+ auto str = pool->string8ObjectAt(s);
+ printf("String #" ZD ": %s\n", (ZD_TYPE) s, (str.has_value() ? str->string() : ""));
}
}
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 439f231..82da249 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -436,9 +436,9 @@
for (size_t i=0; i<N; i++) {
size_t len;
if (pool->isUTF8()) {
- uniqueStrings.add(pool->string8At(i, &len));
+ uniqueStrings.add(UnpackOptionalString(pool->string8At(i), &len));
} else {
- uniqueStrings.add(pool->stringAt(i, &len));
+ uniqueStrings.add(UnpackOptionalString(pool->stringAt(i), &len));
}
}
@@ -450,8 +450,8 @@
const size_t NS = pool->size();
for (size_t s=0; s<NS; s++) {
- String8 str = pool->string8ObjectAt(s);
- printer->Print(StringPrintf("String #%zd : %s\n", s, str.string()));
+ auto str = pool->string8ObjectAt(s);
+ printer->Print(StringPrintf("String #%zd : %s\n", s, str.has_value() ? str->string() : ""));
}
}
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 469128b..f26e995 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -751,10 +751,12 @@
switch (res_value.dataType) {
case android::Res_value::TYPE_STRING: {
const std::string str = util::GetString(src_pool, data);
- const android::ResStringPool_span* spans = src_pool.styleAt(data);
+ auto spans_result = src_pool.styleAt(data);
// Check if the string has a valid style associated with it.
- if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) {
+ if (spans_result.has_value() &&
+ (*spans_result)->name.index != android::ResStringPool_span::END) {
+ const android::ResStringPool_span* spans = spans_result->unsafe_ptr();
StyleString style_str = {str};
while (spans->name.index != android::ResStringPool_span::END) {
style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index),
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 9a7238b..6e5200b 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -223,11 +223,11 @@
std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
ResStringPool test;
ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
- size_t len = 0;
- const char16_t* str = test.stringAt(0, &len);
- EXPECT_THAT(len, Eq(1u));
- EXPECT_THAT(str, Pointee(Eq(u'\u093f')));
- EXPECT_THAT(str[1], Eq(0u));
+ auto str = test.stringAt(0);
+ ASSERT_TRUE(str.has_value());
+ EXPECT_THAT(str->size(), Eq(1u));
+ EXPECT_THAT(str->data(), Pointee(Eq(u'\u093f')));
+ EXPECT_THAT(str->data()[1], Eq(0u));
}
constexpr const char* sLongString =
@@ -278,14 +278,15 @@
EXPECT_THAT(util::GetString(test, 3), Eq(sLongString));
EXPECT_THAT(util::GetString16(test, 3), Eq(util::Utf8ToUtf16(sLongString)));
- size_t len;
- EXPECT_TRUE(test.stringAt(4, &len) != nullptr || test.string8At(4, &len) != nullptr);
+ EXPECT_TRUE(test.stringAt(4).has_value() || test.string8At(4).has_value());
EXPECT_THAT(util::GetString(test, 0), Eq("style"));
EXPECT_THAT(util::GetString16(test, 0), Eq(u"style"));
- const ResStringPool_span* span = test.styleAt(0);
- ASSERT_THAT(span, NotNull());
+ auto span_result = test.styleAt(0);
+ ASSERT_TRUE(span_result.has_value());
+
+ const ResStringPool_span* span = span_result->unsafe_ptr();
EXPECT_THAT(util::GetString(test, span->name.index), Eq("b"));
EXPECT_THAT(util::GetString16(test, span->name.index), Eq(u"b"));
EXPECT_THAT(span->firstChar, Eq(0u));
@@ -318,16 +319,17 @@
// Check that the codepoints are encoded using two three-byte surrogate pairs
ResStringPool test;
ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
- size_t len;
- const char* str = test.string8At(0, &len);
- ASSERT_THAT(str, NotNull());
- EXPECT_THAT(std::string(str, len), Eq("\xED\xA0\x81\xED\xB0\x80"));
- str = test.string8At(1, &len);
- ASSERT_THAT(str, NotNull());
- EXPECT_THAT(std::string(str, len), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
- str = test.string8At(2, &len);
- ASSERT_THAT(str, NotNull());
- EXPECT_THAT(std::string(str, len), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
+ auto str = test.string8At(0);
+ ASSERT_TRUE(str.has_value());
+ EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80"));
+
+ str = test.string8At(1);
+ ASSERT_TRUE(str.has_value());
+ EXPECT_THAT(str->to_string(), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
+
+ str = test.string8At(2);
+ ASSERT_TRUE(str.has_value());
+ EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
// Check that retrieving the strings returns the original UTF-8 character bytes
EXPECT_THAT(util::GetString(test, 0), Eq("\xF0\x90\x90\x80"));
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index fb786a3..ae9f792 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -252,5 +252,4 @@
AssertTranslations(this, "donottranslate", expected_not_translatable);
AssertTranslations(this, "donottranslate_foo", expected_not_translatable);
}
-
} // namespace aapt
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index fd12d02..faabe46 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -730,22 +730,6 @@
return true;
}
-static android::ApkAssetsCookie FindFrameworkAssetManagerCookie(
- const android::AssetManager2& assets) {
- using namespace android;
-
- // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
- // we're looking for the first attribute resource in the system package.
- Res_value val{};
- ResTable_config config{};
- uint32_t type_spec_flags;
- ApkAssetsCookie idx = assets.GetResource(0x01010000, true /** may_be_bag */,
- 0 /** density_override */, &val, &config,
- &type_spec_flags);
-
- return idx;
-}
-
class Linker {
public:
Linker(LinkContext* context, const LinkOptions& options)
@@ -758,8 +742,12 @@
void ExtractCompileSdkVersions(android::AssetManager2* assets) {
using namespace android;
- android::ApkAssetsCookie cookie = FindFrameworkAssetManagerCookie(*assets);
- if (cookie == android::kInvalidCookie) {
+ // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
+ // we're looking for the first attribute resource in the system package.
+ android::ApkAssetsCookie cookie;
+ if (auto value = assets->GetResource(0x01010000, true /** may_be_bag */); value.has_value()) {
+ cookie = value->cookie;
+ } else {
// No Framework assets loaded. Not a failure.
return;
}
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 6932baf..f8b8a1c 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -189,16 +189,16 @@
ResTable_config::CONFIG_VERSION));
std::u16string foo_str = u"foo";
- ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
- ASSERT_GE(idx, 0);
+ auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
+ ASSERT_TRUE(idx.has_value());
EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
- Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+ Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
std::u16string bar_path = u"res/layout/bar.xml";
idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
- ASSERT_GE(idx, 0);
+ ASSERT_TRUE(idx.has_value());
EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar", ResourceId(0x7f050000), {},
- Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+ Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
}
TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
@@ -603,16 +603,16 @@
2u, ResTable_config::CONFIG_VERSION));
std::u16string foo_str = u"foo";
- ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
- ASSERT_GE(idx, 0);
+ auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
+ ASSERT_TRUE(idx.has_value());
EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated",
- ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+ ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
std::u16string bar_path = u"res/layout/bar.xml";
idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
- ASSERT_GE(idx, 0);
+ ASSERT_TRUE(idx.has_value());
EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
- ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+ ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
}
TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) {
@@ -659,16 +659,16 @@
2u, ResTable_config::CONFIG_VERSION));
std::u16string foo_str = u"foo";
- ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
- ASSERT_GE(idx, 0);
+ auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
+ ASSERT_TRUE(idx.has_value());
EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
- Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+ Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
std::u16string bar_path = u"res/layout/bar.xml";
idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
- ASSERT_GE(idx, 0);
+ ASSERT_TRUE(idx.has_value());
EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
- ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+ ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
}
TEST_F(TableFlattenerTest, FlattenOverlayable) {
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 897fa80..ad716c7 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -265,21 +265,22 @@
static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
android::AssetManager2& am, ResourceId id) {
+ using namespace android;
if (am.GetApkAssets().empty()) {
return {};
}
- const android::ResolvedBag* bag = am.GetBag(id.id);
- if (bag == nullptr) {
+ auto bag_result = am.GetBag(id.id);
+ if (!bag_result.has_value()) {
return nullptr;
}
// We found a resource.
std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id);
-
+ const ResolvedBag* bag = *bag_result;
const size_t count = bag->entry_count;
for (uint32_t i = 0; i < count; i++) {
- if (bag->entries[i].key == android::ResTable_map::ATTR_TYPE) {
+ if (bag->entries[i].key == ResTable_map::ATTR_TYPE) {
s->attribute = std::make_shared<Attribute>(bag->entries[i].value.data);
break;
}
@@ -287,25 +288,25 @@
if (s->attribute) {
for (size_t i = 0; i < count; i++) {
- const android::ResolvedBag::Entry& map_entry = bag->entries[i];
+ const ResolvedBag::Entry& map_entry = bag->entries[i];
if (Res_INTERNALID(map_entry.key)) {
switch (map_entry.key) {
- case android::ResTable_map::ATTR_MIN:
+ case ResTable_map::ATTR_MIN:
s->attribute->min_int = static_cast<int32_t>(map_entry.value.data);
break;
- case android::ResTable_map::ATTR_MAX:
+ case ResTable_map::ATTR_MAX:
s->attribute->max_int = static_cast<int32_t>(map_entry.value.data);
break;
}
continue;
}
- android::AssetManager2::ResourceName name;
- if (!am.GetResourceName(map_entry.key, &name)) {
+ auto name = am.GetResourceName(map_entry.key);
+ if (!name.has_value()) {
return nullptr;
}
- Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(name);
+ Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(*name);
if (!parsed_name) {
return nullptr;
}
@@ -328,7 +329,7 @@
bool found = false;
ResourceId res_id = 0;
- uint32_t type_spec_flags;
+ uint32_t type_spec_flags = 0;
ResourceName real_name;
// There can be mangled resources embedded within other packages. Here we will
@@ -340,8 +341,19 @@
real_name.package = package_name;
}
- res_id = asset_manager_.GetResourceId(real_name.to_string());
- if (res_id.is_valid_static() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) {
+ auto real_res_id = asset_manager_.GetResourceId(real_name.to_string());
+ if (!real_res_id.has_value()) {
+ return true;
+ }
+
+ res_id.id = *real_res_id;
+ if (!res_id.is_valid_static()) {
+ return true;
+ }
+
+ auto value = asset_manager_.GetResource(res_id.id, true /* may_be_bag */);
+ if (value.has_value()) {
+ type_spec_flags = value->flags;
found = true;
return false;
}
@@ -371,11 +383,11 @@
static Maybe<ResourceName> GetResourceName(android::AssetManager2& am,
ResourceId id) {
- android::AssetManager2::ResourceName name;
- if (!am.GetResourceName(id.id, &name)) {
+ auto name = am.GetResourceName(id.id);
+ if (!name.has_value()) {
return {};
}
- return ResourceUtils::ToResourceName(name);
+ return ResourceUtils::ToResourceName(*name);
}
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
@@ -394,9 +406,8 @@
return {};
}
-
- uint32_t type_spec_flags = 0;
- if (!asset_manager_.GetResourceFlags(id.id, &type_spec_flags)) {
+ auto value = asset_manager_.GetResource(id.id, true /* may_be_bag */);
+ if (!value.has_value()) {
return {};
}
@@ -411,7 +422,7 @@
}
if (s) {
- s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
+ s->is_public = (value->flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
return s;
}
return {};
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 37ce65e..793a236 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -531,19 +531,15 @@
}
StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) {
- size_t len;
- const char16_t* str = pool.stringAt(idx, &len);
- if (str != nullptr) {
- return StringPiece16(str, len);
+ if (auto str = pool.stringAt(idx); str.ok()) {
+ return *str;
}
return StringPiece16();
}
std::string GetString(const android::ResStringPool& pool, size_t idx) {
- size_t len;
- const char* str = pool.string8At(idx, &len);
- if (str != nullptr) {
- return ModifiedUtf8ToUtf8(std::string(str, len));
+ if (auto str = pool.string8At(idx); str.ok()) {
+ return ModifiedUtf8ToUtf8(str->to_string());
}
return Utf16ToUtf8(GetString16(pool, idx));
}
diff --git a/tools/split-select/Main.cpp b/tools/split-select/Main.cpp
index d3eb012..e6966db 100644
--- a/tools/split-select/Main.cpp
+++ b/tools/split-select/Main.cpp
@@ -182,14 +182,18 @@
if (type >= Res_value::TYPE_FIRST_INT && type <= Res_value::TYPE_LAST_INT) {
outInfo.minSdkVersion = xml.getAttributeData(idx);
} else if (type == Res_value::TYPE_STRING) {
- String8 minSdk8(xml.getStrings().string8ObjectAt(idx));
- char* endPtr;
- int minSdk = strtol(minSdk8.string(), &endPtr, 10);
- if (endPtr != minSdk8.string() + minSdk8.size()) {
- fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n",
- minSdk8.string());
+ auto minSdk8 = xml.getStrings().string8ObjectAt(idx);
+ if (!minSdk8.has_value()) {
+ fprintf(stderr, "warning: failed to retrieve android:minSdkVersion.\n");
} else {
- outInfo.minSdkVersion = minSdk;
+ char *endPtr;
+ int minSdk = strtol(minSdk8->string(), &endPtr, 10);
+ if (endPtr != minSdk8->string() + minSdk8->size()) {
+ fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n",
+ minSdk8->string());
+ } else {
+ outInfo.minSdkVersion = minSdk;
+ }
}
} else {
fprintf(stderr, "warning: unrecognized value for android:minSdkVersion.\n");
diff --git a/tools/split-select/OWNERS b/tools/split-select/OWNERS
new file mode 100644
index 0000000..6c50ede
--- /dev/null
+++ b/tools/split-select/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/res/OWNERS
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/.clang-format b/tools/stats_log_api_gen/.clang-format
deleted file mode 100644
index cead3a0..0000000
--- a/tools/stats_log_api_gen/.clang-format
+++ /dev/null
@@ -1,17 +0,0 @@
-BasedOnStyle: Google
-AllowShortIfStatementsOnASingleLine: true
-AllowShortFunctionsOnASingleLine: false
-AllowShortLoopsOnASingleLine: true
-BinPackArguments: true
-BinPackParameters: true
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-ContinuationIndentWidth: 8
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-AccessModifierOffset: -4
-IncludeCategories:
- - Regex: '^"Log\.h"'
- Priority: -1
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
deleted file mode 100644
index c0893f7..0000000
--- a/tools/stats_log_api_gen/Android.bp
+++ /dev/null
@@ -1,134 +0,0 @@
-//
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// ==========================================================
-// Build the host executable: stats-log-api-gen
-// ==========================================================
-cc_binary_host {
- name: "stats-log-api-gen",
- srcs: [
- "Collation.cpp",
- "java_writer.cpp",
- "java_writer_q.cpp",
- "main.cpp",
- "native_writer.cpp",
- "utils.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-
- shared_libs: [
- "libstats_proto_host",
- "libprotobuf-cpp-full",
- "libbase",
- ],
-
- proto: {
- type: "full",
- },
-}
-
-// ==========================================================
-// Build the host test executable: stats-log-api-gen
-// ==========================================================
-cc_test_host {
- name: "stats-log-api-gen-test",
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-g",
- "-DUNIT_TEST",
- ],
- srcs: [
- "Collation.cpp",
- "test_collation.cpp",
- "test.proto",
- ],
-
- static_libs: [
- "libgmock_host",
- ],
-
- shared_libs: [
- "libstats_proto_host",
- "libprotobuf-cpp-full",
- ],
-
- proto: {
- type: "full",
- include_dirs: [
- "external/protobuf/src",
- ],
- },
-}
-
-// ==========================================================
-// Native library
-// ==========================================================
-genrule {
- name: "statslog.h",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog.h",
- out: [
- "statslog.h",
- ],
-}
-
-genrule {
- name: "statslog.cpp",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog.cpp",
- out: [
- "statslog.cpp",
- ],
-}
-
-cc_library {
- name: "libstatslog",
- host_supported: true,
- generated_sources: [
- "statslog.cpp",
- ],
- generated_headers: [
- "statslog.h"
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- export_generated_headers: [
- "statslog.h"
- ],
- shared_libs: [
- "liblog",
- "libcutils",
- ],
- target: {
- android: {
- shared_libs: ["libstatssocket"],
- },
- host: {
- static_libs: ["libstatssocket"],
- },
- darwin: {
- enabled: false,
- },
- },
-}
-
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
deleted file mode 100644
index 2608097..0000000
--- a/tools/stats_log_api_gen/Collation.cpp
+++ /dev/null
@@ -1,576 +0,0 @@
-/*
- * Copyright (C) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Collation.h"
-
-#include <stdio.h>
-
-#include <map>
-
-#include "frameworks/proto_logging/stats/atoms.pb.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using google::protobuf::EnumDescriptor;
-using google::protobuf::FieldDescriptor;
-using google::protobuf::FileDescriptor;
-using google::protobuf::SourceLocation;
-using std::make_shared;
-using std::map;
-
-const bool dbg = false;
-
-//
-// AtomDecl class
-//
-
-AtomDecl::AtomDecl() : code(0), name() {
-}
-
-AtomDecl::AtomDecl(const AtomDecl& that)
- : code(that.code),
- name(that.name),
- message(that.message),
- fields(that.fields),
- fieldNumberToAnnotations(that.fieldNumberToAnnotations),
- primaryFields(that.primaryFields),
- exclusiveField(that.exclusiveField),
- defaultState(that.defaultState),
- triggerStateReset(that.triggerStateReset),
- nested(that.nested),
- uidField(that.uidField) {
-}
-
-AtomDecl::AtomDecl(int c, const string& n, const string& m) : code(c), name(n), message(m) {
-}
-
-AtomDecl::~AtomDecl() {
-}
-
-/**
- * Print an error message for a FieldDescriptor, including the file name and
- * line number.
- */
-static void print_error(const FieldDescriptor* field, const char* format, ...) {
- const Descriptor* message = field->containing_type();
- const FileDescriptor* file = message->file();
-
- SourceLocation loc;
- if (field->GetSourceLocation(&loc)) {
- // TODO: this will work if we can figure out how to pass
- // --include_source_info to protoc
- fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
- } else {
- fprintf(stderr, "%s: ", file->name().c_str());
- }
- va_list args;
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
-}
-
-/**
- * Convert a protobuf type into a java type.
- */
-static java_type_t java_type(const FieldDescriptor* field) {
- int protoType = field->type();
- switch (protoType) {
- case FieldDescriptor::TYPE_DOUBLE:
- return JAVA_TYPE_DOUBLE;
- case FieldDescriptor::TYPE_FLOAT:
- return JAVA_TYPE_FLOAT;
- case FieldDescriptor::TYPE_INT64:
- return JAVA_TYPE_LONG;
- case FieldDescriptor::TYPE_UINT64:
- return JAVA_TYPE_LONG;
- case FieldDescriptor::TYPE_INT32:
- return JAVA_TYPE_INT;
- case FieldDescriptor::TYPE_FIXED64:
- return JAVA_TYPE_LONG;
- case FieldDescriptor::TYPE_FIXED32:
- return JAVA_TYPE_INT;
- case FieldDescriptor::TYPE_BOOL:
- return JAVA_TYPE_BOOLEAN;
- case FieldDescriptor::TYPE_STRING:
- return JAVA_TYPE_STRING;
- case FieldDescriptor::TYPE_GROUP:
- return JAVA_TYPE_UNKNOWN;
- case FieldDescriptor::TYPE_MESSAGE:
- // TODO: not the final package name
- if (field->message_type()->full_name() == "android.os.statsd.AttributionNode") {
- return JAVA_TYPE_ATTRIBUTION_CHAIN;
- } else if (field->message_type()->full_name() == "android.os.statsd.KeyValuePair") {
- return JAVA_TYPE_KEY_VALUE_PAIR;
- } else if (field->options().GetExtension(os::statsd::log_mode) ==
- os::statsd::LogMode::MODE_BYTES) {
- return JAVA_TYPE_BYTE_ARRAY;
- } else {
- return JAVA_TYPE_OBJECT;
- }
- case FieldDescriptor::TYPE_BYTES:
- return JAVA_TYPE_BYTE_ARRAY;
- case FieldDescriptor::TYPE_UINT32:
- return JAVA_TYPE_INT;
- case FieldDescriptor::TYPE_ENUM:
- return JAVA_TYPE_ENUM;
- case FieldDescriptor::TYPE_SFIXED32:
- return JAVA_TYPE_INT;
- case FieldDescriptor::TYPE_SFIXED64:
- return JAVA_TYPE_LONG;
- case FieldDescriptor::TYPE_SINT32:
- return JAVA_TYPE_INT;
- case FieldDescriptor::TYPE_SINT64:
- return JAVA_TYPE_LONG;
- default:
- return JAVA_TYPE_UNKNOWN;
- }
-}
-
-/**
- * Gather the enums info.
- */
-void collate_enums(const EnumDescriptor& enumDescriptor, AtomField* atomField) {
- for (int i = 0; i < enumDescriptor.value_count(); i++) {
- atomField->enumValues[enumDescriptor.value(i)->number()] =
- enumDescriptor.value(i)->name().c_str();
- }
-}
-
-static void addAnnotationToAtomDecl(AtomDecl* atomDecl, const int fieldNumber,
- const AnnotationId annotationId,
- const AnnotationType annotationType,
- const AnnotationValue annotationValue) {
- if (dbg) {
- printf(" Adding annotation to %s: [%d] = {id: %d, type: %d}\n", atomDecl->name.c_str(),
- fieldNumber, annotationId, annotationType);
- }
- atomDecl->fieldNumberToAnnotations[fieldNumber].insert(
- make_shared<Annotation>(annotationId, atomDecl->code, annotationType, annotationValue));
-}
-
-static int collate_field_annotations(AtomDecl* atomDecl, const FieldDescriptor* field,
- const int fieldNumber, const java_type_t& javaType) {
- int errorCount = 0;
-
- if (field->options().HasExtension(os::statsd::state_field_option)) {
- const os::statsd::StateAtomFieldOption& stateFieldOption =
- field->options().GetExtension(os::statsd::state_field_option);
- const bool primaryField = stateFieldOption.primary_field();
- const bool exclusiveState = stateFieldOption.exclusive_state();
- const bool primaryFieldFirstUid = stateFieldOption.primary_field_first_uid();
-
- // Check the field is only one of primaryField, exclusiveState, or primaryFieldFirstUid.
- if (primaryField + primaryFieldFirstUid + exclusiveState > 1) {
- print_error(field,
- "Field can be max 1 of primary_field, exclusive_state, "
- "or primary_field_first_uid: '%s'\n",
- atomDecl->message.c_str());
- errorCount++;
- }
-
- if (primaryField) {
- if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
- javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
- print_error(field, "Invalid primary state field: '%s'\n",
- atomDecl->message.c_str());
- errorCount++;
- } else {
- atomDecl->primaryFields.push_back(fieldNumber);
- addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_PRIMARY_FIELD,
- ANNOTATION_TYPE_BOOL, AnnotationValue(true));
- }
- }
-
- if (primaryFieldFirstUid) {
- if (javaType != JAVA_TYPE_ATTRIBUTION_CHAIN) {
- print_error(field,
- "PRIMARY_FIELD_FIRST_UID annotation is only for AttributionChains: "
- "'%s'\n",
- atomDecl->message.c_str());
- errorCount++;
- } else {
- atomDecl->primaryFields.push_back(FIRST_UID_IN_CHAIN_ID);
- addAnnotationToAtomDecl(atomDecl, fieldNumber,
- ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, ANNOTATION_TYPE_BOOL,
- AnnotationValue(true));
- }
- }
-
- if (exclusiveState) {
- if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
- javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
- print_error(field, "Invalid exclusive state field: '%s'\n",
- atomDecl->message.c_str());
- errorCount++;
- }
-
- if (atomDecl->exclusiveField != 0) {
- print_error(field,
- "Cannot have more than one exclusive state field in an "
- "atom: '%s'\n",
- atomDecl->message.c_str());
- errorCount++;
- } else {
- atomDecl->exclusiveField = fieldNumber;
- addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_EXCLUSIVE_STATE,
- ANNOTATION_TYPE_BOOL, AnnotationValue(true));
- }
-
- if (stateFieldOption.has_default_state_value()) {
- const int defaultState = stateFieldOption.default_state_value();
- atomDecl->defaultState = defaultState;
-
- addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_DEFAULT_STATE,
- ANNOTATION_TYPE_INT, AnnotationValue(defaultState));
- }
-
- if (stateFieldOption.has_trigger_state_reset_value()) {
- const int triggerStateReset = stateFieldOption.trigger_state_reset_value();
-
- atomDecl->triggerStateReset = triggerStateReset;
- addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_TRIGGER_STATE_RESET,
- ANNOTATION_TYPE_INT, AnnotationValue(triggerStateReset));
- }
-
- if (stateFieldOption.has_nested()) {
- const bool nested = stateFieldOption.nested();
- atomDecl->nested = nested;
-
- addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_STATE_NESTED,
- ANNOTATION_TYPE_BOOL, AnnotationValue(nested));
- }
- }
- }
-
- if (field->options().GetExtension(os::statsd::is_uid) == true) {
- if (javaType != JAVA_TYPE_INT) {
- print_error(field, "is_uid annotation can only be applied to int32 fields: '%s'\n",
- atomDecl->message.c_str());
- errorCount++;
- }
-
- if (atomDecl->uidField == 0) {
- atomDecl->uidField = fieldNumber;
-
- addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_IS_UID,
- ANNOTATION_TYPE_BOOL, AnnotationValue(true));
- } else {
- print_error(field,
- "Cannot have more than one field in an atom with is_uid "
- "annotation: '%s'\n",
- atomDecl->message.c_str());
- errorCount++;
- }
- }
-
- return errorCount;
-}
-
-/**
- * Gather the info about an atom proto.
- */
-int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature) {
- int errorCount = 0;
-
- // Build a sorted list of the fields. Descriptor has them in source file
- // order.
- map<int, const FieldDescriptor*> fields;
- for (int j = 0; j < atom->field_count(); j++) {
- const FieldDescriptor* field = atom->field(j);
- fields[field->number()] = field;
- }
-
- // Check that the parameters start at 1 and go up sequentially.
- int expectedNumber = 1;
- for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
- it++) {
- const int number = it->first;
- const FieldDescriptor* field = it->second;
- if (number != expectedNumber) {
- print_error(field,
- "Fields must be numbered consecutively starting at 1:"
- " '%s' is %d but should be %d\n",
- field->name().c_str(), number, expectedNumber);
- errorCount++;
- expectedNumber = number;
- continue;
- }
- expectedNumber++;
- }
-
- // Check that only allowed types are present. Remove any invalid ones.
- for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
- it++) {
- const FieldDescriptor* field = it->second;
- bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
- os::statsd::LogMode::MODE_BYTES;
-
- java_type_t javaType = java_type(field);
-
- if (javaType == JAVA_TYPE_UNKNOWN) {
- print_error(field, "Unknown type for field: %s\n", field->name().c_str());
- errorCount++;
- continue;
- } else if (javaType == JAVA_TYPE_OBJECT && atomDecl->code < PULL_ATOM_START_ID) {
- // Allow attribution chain, but only at position 1.
- print_error(field, "Message type not allowed for field in pushed atoms: %s\n",
- field->name().c_str());
- errorCount++;
- continue;
- } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
- print_error(field, "Raw bytes type not allowed for field: %s\n", field->name().c_str());
- errorCount++;
- continue;
- }
-
- if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
- print_error(field, "Cannot mark field %s as bytes.\n", field->name().c_str());
- errorCount++;
- continue;
- }
-
- // Doubles are not supported yet.
- if (javaType == JAVA_TYPE_DOUBLE) {
- print_error(field,
- "Doubles are not supported in atoms. Please change field %s "
- "to float\n",
- field->name().c_str());
- errorCount++;
- continue;
- }
-
- if (field->is_repeated() &&
- !(javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_KEY_VALUE_PAIR)) {
- print_error(field,
- "Repeated fields are not supported in atoms. Please make "
- "field %s not "
- "repeated.\n",
- field->name().c_str());
- errorCount++;
- continue;
- }
- }
-
- // Check that if there's an attribution chain, it's at position 1.
- for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
- it++) {
- int number = it->first;
- if (number != 1) {
- const FieldDescriptor* field = it->second;
- java_type_t javaType = java_type(field);
- if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- print_error(field,
- "AttributionChain fields must have field id 1, in message: '%s'\n",
- atom->name().c_str());
- errorCount++;
- }
- }
- }
-
- // Build the type signature and the atom data.
- for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
- it++) {
- const FieldDescriptor* field = it->second;
- java_type_t javaType = java_type(field);
- bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
- os::statsd::LogMode::MODE_BYTES;
-
- AtomField atField(field->name(), javaType);
-
- if (javaType == JAVA_TYPE_ENUM) {
- // All enums are treated as ints when it comes to function signatures.
- collate_enums(*field->enum_type(), &atField);
- }
-
- // Generate signature for pushed atoms
- if (atomDecl->code < PULL_ATOM_START_ID) {
- if (javaType == JAVA_TYPE_ENUM) {
- // All enums are treated as ints when it comes to function signatures.
- signature->push_back(JAVA_TYPE_INT);
- } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
- signature->push_back(JAVA_TYPE_BYTE_ARRAY);
- } else {
- signature->push_back(javaType);
- }
- }
-
- atomDecl->fields.push_back(atField);
-
- errorCount += collate_field_annotations(atomDecl, field, it->first, javaType);
- }
-
- return errorCount;
-}
-
-// This function flattens the fields of the AttributionNode proto in an Atom
-// proto and generates the corresponding atom decl and signature.
-bool get_non_chained_node(const Descriptor* atom, AtomDecl* atomDecl,
- vector<java_type_t>* signature) {
- // Build a sorted list of the fields. Descriptor has them in source file
- // order.
- map<int, const FieldDescriptor*> fields;
- for (int j = 0; j < atom->field_count(); j++) {
- const FieldDescriptor* field = atom->field(j);
- fields[field->number()] = field;
- }
-
- AtomDecl attributionDecl;
- vector<java_type_t> attributionSignature;
- collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
- &attributionSignature);
-
- // Build the type signature and the atom data.
- bool has_attribution_node = false;
- for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
- it++) {
- const FieldDescriptor* field = it->second;
- java_type_t javaType = java_type(field);
- if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- atomDecl->fields.insert(atomDecl->fields.end(), attributionDecl.fields.begin(),
- attributionDecl.fields.end());
- signature->insert(signature->end(), attributionSignature.begin(),
- attributionSignature.end());
- has_attribution_node = true;
-
- } else {
- AtomField atField(field->name(), javaType);
- if (javaType == JAVA_TYPE_ENUM) {
- // All enums are treated as ints when it comes to function signatures.
- signature->push_back(JAVA_TYPE_INT);
- collate_enums(*field->enum_type(), &atField);
- } else {
- signature->push_back(javaType);
- }
- atomDecl->fields.push_back(atField);
- }
- }
- return has_attribution_node;
-}
-
-static void populateFieldNumberToAtomDeclSet(const shared_ptr<AtomDecl>& atomDecl,
- FieldNumberToAtomDeclSet* fieldNumberToAtomDeclSet) {
- for (FieldNumberToAnnotations::const_iterator it = atomDecl->fieldNumberToAnnotations.begin();
- it != atomDecl->fieldNumberToAnnotations.end(); it++) {
- const int fieldNumber = it->first;
- (*fieldNumberToAtomDeclSet)[fieldNumber].insert(atomDecl);
- }
-}
-
-/**
- * Gather the info about the atoms.
- */
-int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms) {
- int errorCount = 0;
-
- for (int i = 0; i < descriptor->field_count(); i++) {
- const FieldDescriptor* atomField = descriptor->field(i);
-
- if (moduleName != DEFAULT_MODULE_NAME) {
- const int moduleCount = atomField->options().ExtensionSize(os::statsd::module);
- int j;
- for (j = 0; j < moduleCount; ++j) {
- const string atomModuleName =
- atomField->options().GetExtension(os::statsd::module, j);
- if (atomModuleName == moduleName) {
- break;
- }
- }
-
- // This atom is not in the module we're interested in; skip it.
- if (moduleCount == j) {
- if (dbg) {
- printf(" Skipping %s (%d)\n", atomField->name().c_str(), atomField->number());
- }
- continue;
- }
- }
-
- if (dbg) {
- printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
- }
-
- // StatsEvent only has one oneof, which contains only messages. Don't allow
- // other types.
- if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
- print_error(atomField,
- "Bad type for atom. StatsEvent can only have message type "
- "fields: %s\n",
- atomField->name().c_str());
- errorCount++;
- continue;
- }
-
- const Descriptor* atom = atomField->message_type();
- shared_ptr<AtomDecl> atomDecl =
- make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name());
-
- if (atomDecl->code < PULL_ATOM_START_ID &&
- atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
- addAnnotationToAtomDecl(atomDecl.get(), ATOM_ID_FIELD_NUMBER,
- ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL,
- AnnotationValue(true));
- if (dbg) {
- printf("%s can have timestamp truncated\n", atomField->name().c_str());
- }
- }
-
- vector<java_type_t> signature;
- errorCount += collate_atom(atom, atomDecl.get(), &signature);
- if (atomDecl->primaryFields.size() != 0 && atomDecl->exclusiveField == 0) {
- print_error(atomField, "Cannot have a primary field without an exclusive field: %s\n",
- atomField->name().c_str());
- errorCount++;
- continue;
- }
-
- FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = atoms->signatureInfoMap[signature];
- populateFieldNumberToAtomDeclSet(atomDecl, &fieldNumberToAtomDeclSet);
-
- atoms->decls.insert(atomDecl);
-
- shared_ptr<AtomDecl> nonChainedAtomDecl =
- make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name());
- vector<java_type_t> nonChainedSignature;
- if (get_non_chained_node(atom, nonChainedAtomDecl.get(), &nonChainedSignature)) {
- FieldNumberToAtomDeclSet& nonChainedFieldNumberToAtomDeclSet =
- atoms->nonChainedSignatureInfoMap[nonChainedSignature];
- populateFieldNumberToAtomDeclSet(nonChainedAtomDecl,
- &nonChainedFieldNumberToAtomDeclSet);
-
- atoms->non_chained_decls.insert(nonChainedAtomDecl);
- }
- }
-
- if (dbg) {
- printf("signatures = [\n");
- for (SignatureInfoMap::const_iterator it = atoms->signatureInfoMap.begin();
- it != atoms->signatureInfoMap.end(); it++) {
- printf(" ");
- for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
- jt++) {
- printf(" %d", (int)*jt);
- }
- printf("\n");
- }
- printf("]\n");
- }
-
- return errorCount;
-}
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
deleted file mode 100644
index 3deb3ae..0000000
--- a/tools/stats_log_api_gen/Collation.h
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_STATS_LOG_API_GEN_COLLATION_H
-#define ANDROID_STATS_LOG_API_GEN_COLLATION_H
-
-#include <google/protobuf/descriptor.h>
-#include <stdint.h>
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "frameworks/proto_logging/stats/atom_field_options.pb.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using google::protobuf::Descriptor;
-using google::protobuf::FieldDescriptor;
-using std::map;
-using std::set;
-using std::shared_ptr;
-using std::string;
-using std::vector;
-
-const int PULL_ATOM_START_ID = 10000;
-
-const int FIRST_UID_IN_CHAIN_ID = 0;
-
-enum AnnotationId : uint8_t {
- ANNOTATION_ID_IS_UID = 1,
- ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2,
- ANNOTATION_ID_PRIMARY_FIELD = 3,
- ANNOTATION_ID_EXCLUSIVE_STATE = 4,
- ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5,
- ANNOTATION_ID_DEFAULT_STATE = 6,
- ANNOTATION_ID_TRIGGER_STATE_RESET = 7,
- ANNOTATION_ID_STATE_NESTED = 8,
-};
-
-const int ATOM_ID_FIELD_NUMBER = -1;
-
-const string DEFAULT_MODULE_NAME = "DEFAULT";
-
-/**
- * The types for atom parameters.
- */
-typedef enum {
- JAVA_TYPE_UNKNOWN = 0,
-
- JAVA_TYPE_ATTRIBUTION_CHAIN = 1,
- JAVA_TYPE_BOOLEAN = 2,
- JAVA_TYPE_INT = 3,
- JAVA_TYPE_LONG = 4,
- JAVA_TYPE_FLOAT = 5,
- JAVA_TYPE_DOUBLE = 6,
- JAVA_TYPE_STRING = 7,
- JAVA_TYPE_ENUM = 8,
- JAVA_TYPE_KEY_VALUE_PAIR = 9,
-
- JAVA_TYPE_OBJECT = -1,
- JAVA_TYPE_BYTE_ARRAY = -2,
-} java_type_t;
-
-enum AnnotationType {
- ANNOTATION_TYPE_UNKNOWN = 0,
- ANNOTATION_TYPE_INT = 1,
- ANNOTATION_TYPE_BOOL = 2,
-};
-
-union AnnotationValue {
- int intValue;
- bool boolValue;
-
- AnnotationValue(const int value) : intValue(value) {
- }
- AnnotationValue(const bool value) : boolValue(value) {
- }
-};
-
-struct Annotation {
- const AnnotationId annotationId;
- const int atomId;
- AnnotationType type;
- AnnotationValue value;
-
- inline Annotation(AnnotationId annotationId, int atomId, AnnotationType type,
- AnnotationValue value)
- : annotationId(annotationId), atomId(atomId), type(type), value(value) {
- }
- inline ~Annotation() {
- }
-
- inline bool operator<(const Annotation& that) const {
- return atomId == that.atomId ? annotationId < that.annotationId : atomId < that.atomId;
- }
-};
-
-struct SharedComparator {
- template <typename T>
- inline bool operator()(const shared_ptr<T>& lhs, const shared_ptr<T>& rhs) const {
- return (*lhs) < (*rhs);
- }
-};
-
-using AnnotationSet = set<shared_ptr<Annotation>, SharedComparator>;
-
-using FieldNumberToAnnotations = map<int, AnnotationSet>;
-
-/**
- * The name and type for an atom field.
- */
-struct AtomField {
- string name;
- java_type_t javaType;
-
- // If the field is of type enum, the following map contains the list of enum
- // values.
- map<int /* numeric value */, string /* value name */> enumValues;
-
- inline AtomField() : name(), javaType(JAVA_TYPE_UNKNOWN) {
- }
- inline AtomField(const AtomField& that)
- : name(that.name), javaType(that.javaType), enumValues(that.enumValues) {
- }
-
- inline AtomField(string n, java_type_t jt) : name(n), javaType(jt) {
- }
- inline ~AtomField() {
- }
-};
-
-/**
- * The name and code for an atom.
- */
-struct AtomDecl {
- int code;
- string name;
-
- string message;
- vector<AtomField> fields;
-
- FieldNumberToAnnotations fieldNumberToAnnotations;
-
- vector<int> primaryFields;
- int exclusiveField = 0;
- int defaultState = INT_MAX;
- int triggerStateReset = INT_MAX;
- bool nested = true;
-
- int uidField = 0;
-
- AtomDecl();
- AtomDecl(const AtomDecl& that);
- AtomDecl(int code, const string& name, const string& message);
- ~AtomDecl();
-
- inline bool operator<(const AtomDecl& that) const {
- return (code == that.code) ? (name < that.name) : (code < that.code);
- }
-};
-
-using AtomDeclSet = set<shared_ptr<AtomDecl>, SharedComparator>;
-
-// Maps a field number to a set of atoms that have annotation(s) for their field with that field
-// number.
-using FieldNumberToAtomDeclSet = map<int, AtomDeclSet>;
-
-using SignatureInfoMap = map<vector<java_type_t>, FieldNumberToAtomDeclSet>;
-
-struct Atoms {
- SignatureInfoMap signatureInfoMap;
- AtomDeclSet decls;
- AtomDeclSet non_chained_decls;
- SignatureInfoMap nonChainedSignatureInfoMap;
-};
-
-/**
- * Gather the information about the atoms. Returns the number of errors.
- */
-int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms);
-int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature);
-
-} // namespace stats_log_api_gen
-} // namespace android
-
-#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
diff --git a/tools/stats_log_api_gen/OWNERS b/tools/stats_log_api_gen/OWNERS
deleted file mode 100644
index 41a0c95..0000000
--- a/tools/stats_log_api_gen/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-yro@google.com
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
deleted file mode 100644
index f4c937c..0000000
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "java_writer.h"
-
-#include "java_writer_q.h"
-#include "utils.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-static int write_java_q_logger_class(FILE* out, const SignatureInfoMap& signatureInfoMap,
- const AtomDecl& attributionDecl) {
- fprintf(out, "\n");
- fprintf(out, " // Write logging helper methods for statsd in Q and earlier.\n");
- fprintf(out, " private static class QLogger {\n");
-
- write_java_q_logging_constants(out, " ");
-
- // Print Q write methods.
- fprintf(out, "\n");
- fprintf(out, " // Write methods.\n");
- write_java_methods_q_schema(out, signatureInfoMap, attributionDecl, " ");
-
- fprintf(out, " }\n");
- return 0;
-}
-
-static void write_java_annotation_constants(FILE* out) {
- fprintf(out, " // Annotation constants.\n");
-
- for (const auto& [id, name] : ANNOTATION_ID_CONSTANTS) {
- fprintf(out, " public static final byte %s = %hhu;\n", name.c_str(), id);
- }
- fprintf(out, "\n");
-}
-
-static void write_annotations(FILE* out, int argIndex,
- const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet) {
- FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
- fieldNumberToAtomDeclSet.find(argIndex);
- if (fieldNumberToAtomDeclSet.end() == fieldNumberToAtomDeclSetIt) {
- return;
- }
- const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second;
- for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
- const string atomConstant = make_constant_name(atomDecl->name);
- fprintf(out, " if (%s == code) {\n", atomConstant.c_str());
- const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex);
- int resetState = -1;
- int defaultState = -1;
- for (const shared_ptr<Annotation>& annotation : annotations) {
- const string& annotationConstant = ANNOTATION_ID_CONSTANTS.at(annotation->annotationId);
- switch (annotation->type) {
- case ANNOTATION_TYPE_INT:
- if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
- resetState = annotation->value.intValue;
- } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
- defaultState = annotation->value.intValue;
- } else {
- fprintf(out, " builder.addIntAnnotation(%s, %d);\n",
- annotationConstant.c_str(), annotation->value.intValue);
- }
- break;
- case ANNOTATION_TYPE_BOOL:
- fprintf(out, " builder.addBooleanAnnotation(%s, %s);\n",
- annotationConstant.c_str(),
- annotation->value.boolValue ? "true" : "false");
- break;
- default:
- break;
- }
- }
- if (defaultState != -1 && resetState != -1) {
- const string& annotationConstant =
- ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_TRIGGER_STATE_RESET);
- fprintf(out, " if (arg%d == %d) {\n", argIndex, resetState);
- fprintf(out, " builder.addIntAnnotation(%s, %d);\n",
- annotationConstant.c_str(), defaultState);
- fprintf(out, " }\n");
- }
- fprintf(out, " }\n");
- }
-}
-
-static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
- const AtomDecl& attributionDecl, const bool supportQ) {
- for (auto signatureInfoMapIt = signatureInfoMap.begin();
- signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
- // Print method signature.
- fprintf(out, " public static void write(int code");
- const vector<java_type_t>& signature = signatureInfoMapIt->first;
- const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
- chainField.name.c_str());
- }
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", android.util.SparseArray<Object> valueMap");
- } else {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
- }
- argIndex++;
- }
- fprintf(out, ") {\n");
-
- // Print method body.
- string indent("");
- if (supportQ) {
- fprintf(out, " if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {\n");
- indent = " ";
- }
-
- // Start StatsEvent.Builder.
- fprintf(out,
- "%s final StatsEvent.Builder builder = "
- "StatsEvent.newBuilder();\n",
- indent.c_str());
-
- // Write atom code.
- fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str());
- write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
-
- // Write the args.
- argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- switch (*arg) {
- case JAVA_TYPE_BOOLEAN:
- fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_INT:
- case JAVA_TYPE_ENUM:
- fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_FLOAT:
- fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_LONG:
- fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_STRING:
- fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_BYTE_ARRAY:
- fprintf(out,
- "%s builder.writeByteArray(null == arg%d ? new byte[0] : "
- "arg%d);\n",
- indent.c_str(), argIndex, argIndex);
- break;
- case JAVA_TYPE_ATTRIBUTION_CHAIN: {
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
-
- fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str());
- fprintf(out, "%s null == %s ? new int[0] : %s,\n",
- indent.c_str(), uidName, uidName);
- fprintf(out, "%s null == %s ? new String[0] : %s);\n",
- indent.c_str(), tagName, tagName);
- break;
- }
- case JAVA_TYPE_KEY_VALUE_PAIR:
- fprintf(out, "\n");
- fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str());
- fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str());
- fprintf(out, "%s android.util.SparseIntArray intMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseLongArray longMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n",
- indent.c_str());
- fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str());
- fprintf(out, "%s final int key = valueMap.keyAt(i);\n",
- indent.c_str());
- fprintf(out, "%s final Object value = valueMap.valueAt(i);\n",
- indent.c_str());
- fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str());
- fprintf(out, "%s if (null == intMap) {\n", indent.c_str());
- fprintf(out,
- "%s intMap = new "
- "android.util.SparseIntArray();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s intMap.put(key, (Integer) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof Long) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == longMap) {\n", indent.c_str());
- fprintf(out,
- "%s longMap = new "
- "android.util.SparseLongArray();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s longMap.put(key, (Long) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof String) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == stringMap) {\n", indent.c_str());
- fprintf(out,
- "%s stringMap = new "
- "android.util.SparseArray<>();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s stringMap.put(key, (String) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof Float) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == floatMap) {\n", indent.c_str());
- fprintf(out,
- "%s floatMap = new "
- "android.util.SparseArray<>();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s floatMap.put(key, (Float) value);\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out,
- "%s builder.writeKeyValuePairs("
- "intMap, longMap, stringMap, floatMap);\n",
- indent.c_str());
- break;
- default:
- // Unsupported types: OBJECT, DOUBLE.
- fprintf(stderr, "Encountered unsupported type.");
- return 1;
- }
- write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
- argIndex++;
- }
-
- fprintf(out, "\n");
- fprintf(out, "%s builder.usePooledBuffer();\n", indent.c_str());
- fprintf(out, "%s StatsLog.write(builder.build());\n", indent.c_str());
-
- // Add support for writing using Q schema if this is not the default module.
- if (supportQ) {
- fprintf(out, " } else {\n");
- fprintf(out, " QLogger.write(code");
- argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin();
- arg != signature.end(); arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
- fprintf(out, ", %s, %s", uidName, tagName);
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- // Module logging does not yet support key value pair.
- fprintf(stderr, "Module logging does not yet support key value pair.\n");
- return 1;
- } else {
- fprintf(out, ", arg%d", argIndex);
- }
- argIndex++;
- }
- fprintf(out, ");\n");
- fprintf(out, " }\n"); // if
- }
-
- fprintf(out, " }\n"); // method
- fprintf(out, "\n");
- }
- return 0;
-}
-
-int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
- const string& javaClass, const string& javaPackage, const bool supportQ,
- const bool supportWorkSource) {
- // Print prelude
- fprintf(out, "// This file is autogenerated\n");
- fprintf(out, "\n");
- fprintf(out, "package %s;\n", javaPackage.c_str());
- fprintf(out, "\n");
- fprintf(out, "\n");
- if (supportQ) {
- fprintf(out, "import android.os.Build;\n");
- fprintf(out, "import android.os.SystemClock;\n");
- }
-
- fprintf(out, "import android.util.StatsEvent;\n");
- fprintf(out, "import android.util.StatsLog;\n");
-
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "/**\n");
- fprintf(out, " * Utility class for logging statistics events.\n");
- fprintf(out, " */\n");
- fprintf(out, "public class %s {\n", javaClass.c_str());
-
- write_java_atom_codes(out, atoms);
- write_java_enum_values(out, atoms);
- write_java_annotation_constants(out);
-
- int errors = 0;
-
- // Print write methods.
- fprintf(out, " // Write methods\n");
- errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ);
- errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
- if (supportWorkSource) {
- errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
- }
-
- if (supportQ) {
- errors += write_java_q_logger_class(out, atoms.signatureInfoMap, attributionDecl);
- }
-
- fprintf(out, "}\n");
-
- return errors;
-}
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/java_writer.h b/tools/stats_log_api_gen/java_writer.h
deleted file mode 100644
index 8b3b505..0000000
--- a/tools/stats_log_api_gen/java_writer.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <string.h>
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "Collation.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using namespace std;
-
-int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
- const string& javaClass, const string& javaPackage, const bool supportQ,
- const bool supportWorkSource);
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp
deleted file mode 100644
index d21e270..0000000
--- a/tools/stats_log_api_gen/java_writer_q.cpp
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "java_writer_q.h"
-
-#include "utils.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-void write_java_q_logging_constants(FILE* out, const string& indent) {
- fprintf(out, "%s// Payload limits.\n", indent.c_str());
- fprintf(out, "%sprivate static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;\n", indent.c_str());
- fprintf(out,
- "%sprivate static final int MAX_EVENT_PAYLOAD = "
- "LOGGER_ENTRY_MAX_PAYLOAD - 4;\n",
- indent.c_str());
-
- // Value types. Must match with EventLog.java and log.h.
- fprintf(out, "\n");
- fprintf(out, "%s// Value types.\n", indent.c_str());
- fprintf(out, "%sprivate static final byte INT_TYPE = 0;\n", indent.c_str());
- fprintf(out, "%sprivate static final byte LONG_TYPE = 1;\n", indent.c_str());
- fprintf(out, "%sprivate static final byte STRING_TYPE = 2;\n", indent.c_str());
- fprintf(out, "%sprivate static final byte LIST_TYPE = 3;\n", indent.c_str());
- fprintf(out, "%sprivate static final byte FLOAT_TYPE = 4;\n", indent.c_str());
-
- // Size of each value type.
- // Booleans, ints, floats, and enums take 5 bytes, 1 for the type and 4 for
- // the value.
- fprintf(out, "\n");
- fprintf(out, "%s// Size of each value type.\n", indent.c_str());
- fprintf(out, "%sprivate static final int INT_TYPE_SIZE = 5;\n", indent.c_str());
- fprintf(out, "%sprivate static final int FLOAT_TYPE_SIZE = 5;\n", indent.c_str());
- // Longs take 9 bytes, 1 for the type and 8 for the value.
- fprintf(out, "%sprivate static final int LONG_TYPE_SIZE = 9;\n", indent.c_str());
- // Strings take 5 metadata bytes: 1 byte is for the type, 4 are for the
- // length.
- fprintf(out, "%sprivate static final int STRING_TYPE_OVERHEAD = 5;\n", indent.c_str());
- fprintf(out, "%sprivate static final int LIST_TYPE_OVERHEAD = 2;\n", indent.c_str());
-}
-
-int write_java_methods_q_schema(FILE* out, const SignatureInfoMap& signatureInfoMap,
- const AtomDecl& attributionDecl, const string& indent) {
- int requiredHelpers = 0;
- for (auto signatureInfoMapIt = signatureInfoMap.begin();
- signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
- // Print method signature.
- vector<java_type_t> signature = signatureInfoMapIt->first;
- fprintf(out, "%spublic static void write(int code", indent.c_str());
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
- chainField.name.c_str());
- }
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", android.util.SparseArray<Object> valueMap");
- } else {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
- }
- argIndex++;
- }
- fprintf(out, ") {\n");
-
- // Calculate the size of the buffer.
- fprintf(out, "%s // Initial overhead of the list, timestamp, and atom tag.\n",
- indent.c_str());
- fprintf(out,
- "%s int needed = LIST_TYPE_OVERHEAD + LONG_TYPE_SIZE + "
- "INT_TYPE_SIZE;\n",
- indent.c_str());
- argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- switch (*arg) {
- case JAVA_TYPE_BOOLEAN:
- case JAVA_TYPE_INT:
- case JAVA_TYPE_FLOAT:
- case JAVA_TYPE_ENUM:
- fprintf(out, "%s needed += INT_TYPE_SIZE;\n", indent.c_str());
- break;
- case JAVA_TYPE_LONG:
- // Longs take 9 bytes, 1 for the type and 8 for the value.
- fprintf(out, "%s needed += LONG_TYPE_SIZE;\n", indent.c_str());
- break;
- case JAVA_TYPE_STRING:
- // Strings take 5 metadata bytes + length of byte encoded string.
- fprintf(out, "%s if (arg%d == null) {\n", indent.c_str(), argIndex);
- fprintf(out, "%s arg%d = \"\";\n", indent.c_str(), argIndex);
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out,
- "%s byte[] arg%dBytes = "
- "arg%d.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
- indent.c_str(), argIndex, argIndex);
- fprintf(out, "%s needed += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
- indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_BYTE_ARRAY:
- // Byte arrays take 5 metadata bytes + length of byte array.
- fprintf(out, "%s if (arg%d == null) {\n", indent.c_str(), argIndex);
- fprintf(out, "%s arg%d = new byte[0];\n", indent.c_str(), argIndex);
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s needed += STRING_TYPE_OVERHEAD + arg%d.length;\n",
- indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_ATTRIBUTION_CHAIN: {
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
- // Null checks on the params.
- fprintf(out, "%s if (%s == null) {\n", indent.c_str(), uidName);
- fprintf(out, "%s %s = new %s[0];\n", indent.c_str(), uidName,
- java_type_name(attributionDecl.fields.front().javaType));
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s if (%s == null) {\n", indent.c_str(), tagName);
- fprintf(out, "%s %s = new %s[0];\n", indent.c_str(), tagName,
- java_type_name(attributionDecl.fields.back().javaType));
- fprintf(out, "%s }\n", indent.c_str());
-
- // First check that the lengths of the uid and tag arrays are the
- // same.
- fprintf(out, "%s if (%s.length != %s.length) {\n", indent.c_str(), uidName,
- tagName);
- fprintf(out, "%s return;\n", indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s int attrSize = LIST_TYPE_OVERHEAD;\n", indent.c_str());
- fprintf(out, "%s for (int i = 0; i < %s.length; i++) {\n", indent.c_str(),
- tagName);
- fprintf(out, "%s String str%d = (%s[i] == null) ? \"\" : %s[i];\n",
- indent.c_str(), argIndex, tagName, tagName);
- fprintf(out,
- "%s int str%dlen = "
- "str%d.getBytes(java.nio.charset.StandardCharsets.UTF_8)."
- "length;\n",
- indent.c_str(), argIndex, argIndex);
- fprintf(out,
- "%s attrSize += "
- "LIST_TYPE_OVERHEAD + INT_TYPE_SIZE + STRING_TYPE_OVERHEAD + "
- "str%dlen;\n",
- indent.c_str(), argIndex);
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s needed += attrSize;\n", indent.c_str());
- break;
- }
- case JAVA_TYPE_KEY_VALUE_PAIR: {
- fprintf(out, "%s // Calculate bytes needed by Key Value Pairs.\n",
- indent.c_str());
- fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str());
- fprintf(out, "%s android.util.SparseIntArray intMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseLongArray longMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n",
- indent.c_str());
- fprintf(out, "%s int keyValuePairSize = LIST_TYPE_OVERHEAD;\n",
- indent.c_str());
- fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str());
- fprintf(out, "%s final int key = valueMap.keyAt(i);\n", indent.c_str());
- fprintf(out, "%s final Object value = valueMap.valueAt(i);\n",
- indent.c_str());
- fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str());
- fprintf(out, "%s keyValuePairSize += LIST_TYPE_OVERHEAD\n",
- indent.c_str());
- fprintf(out, "%s + INT_TYPE_SIZE + INT_TYPE_SIZE;\n",
- indent.c_str());
- fprintf(out, "%s if (null == intMap) {\n", indent.c_str());
- fprintf(out, "%s intMap = new android.util.SparseIntArray();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s intMap.put(key, (Integer) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof Long) {\n", indent.c_str());
- fprintf(out, "%s keyValuePairSize += LIST_TYPE_OVERHEAD\n",
- indent.c_str());
- fprintf(out, "%s + INT_TYPE_SIZE + LONG_TYPE_SIZE;\n",
- indent.c_str());
- fprintf(out, "%s if (null == longMap) {\n", indent.c_str());
- fprintf(out,
- "%s longMap = new "
- "android.util.SparseLongArray();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s longMap.put(key, (Long) value);\n", indent.c_str());
- fprintf(out, "%s } else if (value instanceof String) {\n",
- indent.c_str());
- fprintf(out,
- "%s final String str = (value == null) ? \"\" : "
- "(String) value;\n",
- indent.c_str());
- fprintf(out,
- "%s final int len = "
- "str.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n",
- indent.c_str());
- fprintf(out,
- "%s keyValuePairSize += LIST_TYPE_OVERHEAD + "
- "INT_TYPE_SIZE\n",
- indent.c_str());
- fprintf(out, "%s + STRING_TYPE_OVERHEAD + len;\n",
- indent.c_str());
- fprintf(out, "%s if (null == stringMap) {\n", indent.c_str());
- fprintf(out,
- "%s stringMap = new "
- "android.util.SparseArray<>();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s stringMap.put(key, str);\n", indent.c_str());
- fprintf(out, "%s } else if (value instanceof Float) {\n",
- indent.c_str());
- fprintf(out, "%s keyValuePairSize += LIST_TYPE_OVERHEAD\n",
- indent.c_str());
- fprintf(out, "%s + INT_TYPE_SIZE + FLOAT_TYPE_SIZE;\n",
- indent.c_str());
- fprintf(out, "%s if (null == floatMap) {\n", indent.c_str());
- fprintf(out,
- "%s floatMap = new "
- "android.util.SparseArray<>();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s floatMap.put(key, (Float) value);\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s needed += keyValuePairSize;\n", indent.c_str());
- break;
- }
- default:
- // Unsupported types: OBJECT, DOUBLE.
- fprintf(stderr, "Module logging does not yet support Object and Double.\n");
- return 1;
- }
- argIndex++;
- }
-
- // Now we have the size that is needed. Check for overflow and return if
- // needed.
- fprintf(out, "%s if (needed > MAX_EVENT_PAYLOAD) {\n", indent.c_str());
- fprintf(out, "%s return;\n", indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
-
- // Create new buffer, and associated data types.
- fprintf(out, "%s byte[] buff = new byte[needed];\n", indent.c_str());
- fprintf(out, "%s int pos = 0;\n", indent.c_str());
-
- // Initialize the buffer with list data type.
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = %zu;\n", indent.c_str(), signature.size() + 2);
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-
- // Write timestamp.
- fprintf(out, "%s long elapsedRealtime = SystemClock.elapsedRealtimeNanos();\n",
- indent.c_str());
- fprintf(out, "%s buff[pos] = LONG_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyLong(buff, pos + 1, elapsedRealtime);\n", indent.c_str());
- fprintf(out, "%s pos += LONG_TYPE_SIZE;\n", indent.c_str());
-
- // Write atom code.
- fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, code);\n", indent.c_str());
- fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str());
-
- // Write the args.
- argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- switch (*arg) {
- case JAVA_TYPE_BOOLEAN:
- fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, arg%d? 1 : 0);\n", indent.c_str(),
- argIndex);
- fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str());
- break;
- case JAVA_TYPE_INT:
- case JAVA_TYPE_ENUM:
- fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, arg%d);\n", indent.c_str(),
- argIndex);
- fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str());
- break;
- case JAVA_TYPE_FLOAT:
- requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
- fprintf(out, "%s buff[pos] = FLOAT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyFloat(buff, pos + 1, arg%d);\n", indent.c_str(),
- argIndex);
- fprintf(out, "%s pos += FLOAT_TYPE_SIZE;\n", indent.c_str());
- break;
- case JAVA_TYPE_LONG:
- fprintf(out, "%s buff[pos] = LONG_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyLong(buff, pos + 1, arg%d);\n", indent.c_str(),
- argIndex);
- fprintf(out, "%s pos += LONG_TYPE_SIZE;\n", indent.c_str());
- break;
- case JAVA_TYPE_STRING:
- fprintf(out, "%s buff[pos] = STRING_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, arg%dBytes.length);\n",
- indent.c_str(), argIndex);
- fprintf(out,
- "%s System.arraycopy("
- "arg%dBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, "
- "arg%dBytes.length);\n",
- indent.c_str(), argIndex, argIndex);
- fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
- indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_BYTE_ARRAY:
- fprintf(out, "%s buff[pos] = STRING_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, arg%d.length);\n", indent.c_str(),
- argIndex);
- fprintf(out,
- "%s System.arraycopy("
- "arg%d, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%d.length);\n",
- indent.c_str(), argIndex, argIndex);
- fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + arg%d.length;\n",
- indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_ATTRIBUTION_CHAIN: {
- requiredHelpers |= JAVA_MODULE_REQUIRES_ATTRIBUTION;
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
-
- fprintf(out, "%s writeAttributionChain(buff, pos, %s, %s);\n",
- indent.c_str(), uidName, tagName);
- fprintf(out, "%s pos += attrSize;\n", indent.c_str());
- break;
- }
- case JAVA_TYPE_KEY_VALUE_PAIR:
- requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
- requiredHelpers |= JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS;
- fprintf(out,
- "%s writeKeyValuePairs(buff, pos, (byte) count, intMap, "
- "longMap, "
- "stringMap, floatMap);\n",
- indent.c_str());
- fprintf(out, "%s pos += keyValuePairSize;\n", indent.c_str());
- break;
- default:
- // Unsupported types: OBJECT, DOUBLE.
- fprintf(stderr, "Object and Double are not supported in module logging");
- return 1;
- }
- argIndex++;
- }
-
- fprintf(out, "%s StatsLog.writeRaw(buff, pos);\n", indent.c_str());
- fprintf(out, "%s}\n", indent.c_str());
- fprintf(out, "\n");
- }
-
- write_java_helpers_for_q_schema_methods(out, attributionDecl, requiredHelpers, indent);
-
- return 0;
-}
-
-void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attributionDecl,
- const int requiredHelpers, const string& indent) {
- fprintf(out, "\n");
- fprintf(out, "%s// Helper methods for copying primitives\n", indent.c_str());
- fprintf(out, "%sprivate static void copyInt(byte[] buff, int pos, int val) {\n",
- indent.c_str());
- fprintf(out, "%s buff[pos] = (byte) (val);\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) (val >> 8);\n", indent.c_str());
- fprintf(out, "%s buff[pos + 2] = (byte) (val >> 16);\n", indent.c_str());
- fprintf(out, "%s buff[pos + 3] = (byte) (val >> 24);\n", indent.c_str());
- fprintf(out, "%s return;\n", indent.c_str());
- fprintf(out, "%s}\n", indent.c_str());
- fprintf(out, "\n");
-
- fprintf(out, "%sprivate static void copyLong(byte[] buff, int pos, long val) {\n",
- indent.c_str());
- fprintf(out, "%s buff[pos] = (byte) (val);\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) (val >> 8);\n", indent.c_str());
- fprintf(out, "%s buff[pos + 2] = (byte) (val >> 16);\n", indent.c_str());
- fprintf(out, "%s buff[pos + 3] = (byte) (val >> 24);\n", indent.c_str());
- fprintf(out, "%s buff[pos + 4] = (byte) (val >> 32);\n", indent.c_str());
- fprintf(out, "%s buff[pos + 5] = (byte) (val >> 40);\n", indent.c_str());
- fprintf(out, "%s buff[pos + 6] = (byte) (val >> 48);\n", indent.c_str());
- fprintf(out, "%s buff[pos + 7] = (byte) (val >> 56);\n", indent.c_str());
- fprintf(out, "%s return;\n", indent.c_str());
- fprintf(out, "%s}\n", indent.c_str());
- fprintf(out, "\n");
-
- if (requiredHelpers & JAVA_MODULE_REQUIRES_FLOAT) {
- fprintf(out, "%sprivate static void copyFloat(byte[] buff, int pos, float val) {\n",
- indent.c_str());
- fprintf(out, "%s copyInt(buff, pos, Float.floatToIntBits(val));\n", indent.c_str());
- fprintf(out, "%s return;\n", indent.c_str());
- fprintf(out, "%s}\n", indent.c_str());
- fprintf(out, "\n");
- }
-
- if (requiredHelpers & JAVA_MODULE_REQUIRES_ATTRIBUTION) {
- fprintf(out, "%sprivate static void writeAttributionChain(byte[] buff, int pos",
- indent.c_str());
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), chainField.name.c_str());
- }
- fprintf(out, ") {\n");
-
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
-
- // Write the first list begin.
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) (%s.length);\n", indent.c_str(), tagName);
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-
- // Iterate through the attribution chain and write the nodes.
- fprintf(out, "%s for (int i = 0; i < %s.length; i++) {\n", indent.c_str(), tagName);
- // Write the list begin.
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = %lu;\n", indent.c_str(),
- attributionDecl.fields.size());
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-
- // Write the uid.
- fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, %s[i]);\n", indent.c_str(), uidName);
- fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str());
-
- // Write the tag.
- fprintf(out, "%s String %sStr = (%s[i] == null) ? \"\" : %s[i];\n", indent.c_str(),
- tagName, tagName, tagName);
- fprintf(out,
- "%s byte[] %sByte = "
- "%sStr.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
- indent.c_str(), tagName, tagName);
- fprintf(out, "%s buff[pos] = STRING_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, %sByte.length);\n", indent.c_str(), tagName);
- fprintf(out,
- "%s System.arraycopy("
- "%sByte, 0, buff, pos + STRING_TYPE_OVERHEAD, %sByte.length);\n",
- indent.c_str(), tagName, tagName);
- fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + %sByte.length;\n", indent.c_str(),
- tagName);
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s}\n", indent.c_str());
- fprintf(out, "\n");
- }
-
- if (requiredHelpers & JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS) {
- fprintf(out,
- "%sprivate static void writeKeyValuePairs(byte[] buff, int pos, "
- "byte numPairs,\n",
- indent.c_str());
- fprintf(out, "%s final android.util.SparseIntArray intMap,\n", indent.c_str());
- fprintf(out, "%s final android.util.SparseLongArray longMap,\n", indent.c_str());
- fprintf(out, "%s final android.util.SparseArray<String> stringMap,\n",
- indent.c_str());
- fprintf(out, "%s final android.util.SparseArray<Float> floatMap) {\n",
- indent.c_str());
-
- // Start list of lists.
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) numPairs;\n", indent.c_str());
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
-
- // Write integers.
- fprintf(out, "%s final int intMapSize = null == intMap ? 0 : intMap.size();\n",
- indent.c_str());
- fprintf(out, "%s for (int i = 0; i < intMapSize; i++) {\n", indent.c_str());
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str());
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
- fprintf(out, "%s final int key = intMap.keyAt(i);\n", indent.c_str());
- fprintf(out, "%s final int value = intMap.valueAt(i);\n", indent.c_str());
- fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, key);\n", indent.c_str());
- fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str());
- fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, value);\n", indent.c_str());
- fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
-
- // Write longs.
- fprintf(out, "%s final int longMapSize = null == longMap ? 0 : longMap.size();\n",
- indent.c_str());
- fprintf(out, "%s for (int i = 0; i < longMapSize; i++) {\n", indent.c_str());
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str());
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
- fprintf(out, "%s final int key = longMap.keyAt(i);\n", indent.c_str());
- fprintf(out, "%s final long value = longMap.valueAt(i);\n", indent.c_str());
- fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, key);\n", indent.c_str());
- fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str());
- fprintf(out, "%s buff[pos] = LONG_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyLong(buff, pos + 1, value);\n", indent.c_str());
- fprintf(out, "%s pos += LONG_TYPE_SIZE;\n", indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
-
- // Write Strings.
- fprintf(out,
- "%s final int stringMapSize = null == stringMap ? 0 : "
- "stringMap.size();\n",
- indent.c_str());
- fprintf(out, "%s for (int i = 0; i < stringMapSize; i++) {\n", indent.c_str());
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str());
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
- fprintf(out, "%s final int key = stringMap.keyAt(i);\n", indent.c_str());
- fprintf(out, "%s final String value = stringMap.valueAt(i);\n", indent.c_str());
- fprintf(out,
- "%s final byte[] valueBytes = "
- "value.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
- indent.c_str());
- fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, key);\n", indent.c_str());
- fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str());
- fprintf(out, "%s buff[pos] = STRING_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, valueBytes.length);\n", indent.c_str());
- fprintf(out,
- "%s System.arraycopy("
- "valueBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, "
- "valueBytes.length);\n",
- indent.c_str());
- fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + valueBytes.length;\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
-
- // Write floats.
- fprintf(out,
- "%s final int floatMapSize = null == floatMap ? 0 : "
- "floatMap.size();\n",
- indent.c_str());
- fprintf(out, "%s for (int i = 0; i < floatMapSize; i++) {\n", indent.c_str());
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str());
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
- fprintf(out, "%s final int key = floatMap.keyAt(i);\n", indent.c_str());
- fprintf(out, "%s final float value = floatMap.valueAt(i);\n", indent.c_str());
- fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyInt(buff, pos + 1, key);\n", indent.c_str());
- fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str());
- fprintf(out, "%s buff[pos] = FLOAT_TYPE;\n", indent.c_str());
- fprintf(out, "%s copyFloat(buff, pos + 1, value);\n", indent.c_str());
- fprintf(out, "%s pos += FLOAT_TYPE_SIZE;\n", indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s}\n", indent.c_str());
- fprintf(out, "\n");
- }
-}
-
-// This method is called in main.cpp to generate StatsLog for modules that's
-// compatible with Q at compile-time.
-int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
- const AtomDecl& attributionDecl, const string& javaClass,
- const string& javaPackage, const bool supportWorkSource) {
- // Print prelude
- fprintf(out, "// This file is autogenerated\n");
- fprintf(out, "\n");
- fprintf(out, "package %s;\n", javaPackage.c_str());
- fprintf(out, "\n");
- fprintf(out, "import static java.nio.charset.StandardCharsets.UTF_8;\n");
- fprintf(out, "\n");
- fprintf(out, "import android.util.StatsLog;\n");
- fprintf(out, "import android.os.SystemClock;\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "/**\n");
- fprintf(out, " * Utility class for logging statistics events.\n");
- fprintf(out, " */\n");
- fprintf(out, "public class %s {\n", javaClass.c_str());
-
- write_java_q_logging_constants(out, " ");
-
- write_java_atom_codes(out, atoms);
-
- write_java_enum_values(out, atoms);
-
- int errors = 0;
- // Print write methods
- fprintf(out, " // Write methods\n");
- errors += write_java_methods_q_schema(out, atoms.signatureInfoMap, attributionDecl, " ");
- errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
- if (supportWorkSource) {
- errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
- }
-
- fprintf(out, "}\n");
-
- return errors;
-}
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/java_writer_q.h b/tools/stats_log_api_gen/java_writer_q.h
deleted file mode 100644
index c511a84..0000000
--- a/tools/stats_log_api_gen/java_writer_q.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <string.h>
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "Collation.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using namespace std;
-
-void write_java_q_logging_constants(FILE* out, const string& indent);
-
-int write_java_methods_q_schema(FILE* out, const SignatureInfoMap& signatureInfoMap,
- const AtomDecl& attributionDecl, const string& indent);
-
-void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attributionDecl,
- const int requiredHelpers, const string& indent);
-
-int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
- const AtomDecl& attributionDecl, const string& javaClass,
- const string& javaPackage, const bool supportWorkSource);
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
deleted file mode 100644
index 416dfdd..0000000
--- a/tools/stats_log_api_gen/main.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "Collation.h"
-#include "frameworks/proto_logging/stats/atoms.pb.h"
-#include "java_writer.h"
-#include "java_writer_q.h"
-#include "native_writer.h"
-#include "utils.h"
-
-using namespace google::protobuf;
-using namespace std;
-
-namespace android {
-namespace stats_log_api_gen {
-
-using android::os::statsd::Atom;
-
-static void print_usage() {
- fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "OPTIONS\n");
- fprintf(stderr, " --cpp FILENAME the header file to output for write helpers\n");
- fprintf(stderr, " --header FILENAME the cpp file to output for write helpers\n");
- fprintf(stderr, " --help this message\n");
- fprintf(stderr, " --java FILENAME the java file to output\n");
- fprintf(stderr, " --module NAME optional, module name to generate outputs for\n");
- fprintf(stderr,
- " --namespace COMMA,SEP,NAMESPACE required for cpp/header with "
- "module\n");
- fprintf(stderr,
- " comma separated namespace of "
- "the files\n");
- fprintf(stderr,
- " --importHeader NAME required for cpp/jni to say which header to "
- "import "
- "for write helpers\n");
- fprintf(stderr, " --javaPackage PACKAGE the package for the java file.\n");
- fprintf(stderr, " required for java with module\n");
- fprintf(stderr, " --javaClass CLASS the class name of the java class.\n");
- fprintf(stderr, " Optional for Java with module.\n");
- fprintf(stderr, " Default is \"StatsLogInternal\"\n");
- fprintf(stderr, " --supportQ Include runtime support for Android Q.\n");
- fprintf(stderr,
- " --worksource Include support for logging WorkSource "
- "objects.\n");
- fprintf(stderr,
- " --compileQ Include compile-time support for Android Q "
- "(Java only).\n");
-}
-
-/**
- * Do the argument parsing and execute the tasks.
- */
-static int run(int argc, char const* const* argv) {
- string cppFilename;
- string headerFilename;
- string javaFilename;
- string javaPackage;
- string javaClass;
-
- string moduleName = DEFAULT_MODULE_NAME;
- string cppNamespace = DEFAULT_CPP_NAMESPACE;
- string cppHeaderImport = DEFAULT_CPP_HEADER_IMPORT;
- bool supportQ = false;
- bool supportWorkSource = false;
- bool compileQ = false;
-
- int index = 1;
- while (index < argc) {
- if (0 == strcmp("--help", argv[index])) {
- print_usage();
- return 0;
- } else if (0 == strcmp("--cpp", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- cppFilename = argv[index];
- } else if (0 == strcmp("--header", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- headerFilename = argv[index];
- } else if (0 == strcmp("--java", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- javaFilename = argv[index];
- } else if (0 == strcmp("--module", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- moduleName = argv[index];
- } else if (0 == strcmp("--namespace", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- cppNamespace = argv[index];
- } else if (0 == strcmp("--importHeader", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- cppHeaderImport = argv[index];
- } else if (0 == strcmp("--javaPackage", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- javaPackage = argv[index];
- } else if (0 == strcmp("--javaClass", argv[index])) {
- index++;
- if (index >= argc) {
- print_usage();
- return 1;
- }
- javaClass = argv[index];
- } else if (0 == strcmp("--supportQ", argv[index])) {
- supportQ = true;
- } else if (0 == strcmp("--worksource", argv[index])) {
- supportWorkSource = true;
- } else if (0 == strcmp("--compileQ", argv[index])) {
- compileQ = true;
- }
-
- index++;
- }
-
- if (cppFilename.size() == 0 && headerFilename.size() == 0 && javaFilename.size() == 0) {
- print_usage();
- return 1;
- }
-
- if (DEFAULT_MODULE_NAME == moduleName && (supportQ || compileQ)) {
- // Support for Q schema is not needed for default module.
- fprintf(stderr, "%s cannot support Q schema\n", moduleName.c_str());
- return 1;
- }
-
- if (supportQ && compileQ) {
- // Runtime Q support is redundant if compile-time Q support is required.
- fprintf(stderr, "Cannot specify compileQ and supportQ simultaneously.\n");
- return 1;
- }
-
- // Collate the parameters
- Atoms atoms;
- int errorCount = collate_atoms(Atom::descriptor(), moduleName, &atoms);
- if (errorCount != 0) {
- return 1;
- }
-
- AtomDecl attributionDecl;
- vector<java_type_t> attributionSignature;
- collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
- &attributionSignature);
-
- // Write the .cpp file
- if (cppFilename.size() != 0) {
- FILE* out = fopen(cppFilename.c_str(), "w");
- if (out == NULL) {
- fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
- return 1;
- }
- // If this is for a specific module, the namespace must also be provided.
- if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
- fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
- return 1;
- }
- // If this is for a specific module, the header file to import must also be
- // provided.
- if (moduleName != DEFAULT_MODULE_NAME && cppHeaderImport == DEFAULT_CPP_HEADER_IMPORT) {
- fprintf(stderr, "Must supply --headerImport if supplying a specific module\n");
- return 1;
- }
- errorCount = android::stats_log_api_gen::write_stats_log_cpp(
- out, atoms, attributionDecl, cppNamespace, cppHeaderImport, supportQ);
- fclose(out);
- }
-
- // Write the .h file
- if (headerFilename.size() != 0) {
- FILE* out = fopen(headerFilename.c_str(), "w");
- if (out == NULL) {
- fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
- return 1;
- }
- // If this is for a specific module, the namespace must also be provided.
- if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
- fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
- }
- errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms, attributionDecl,
- cppNamespace);
- fclose(out);
- }
-
- // Write the .java file
- if (javaFilename.size() != 0) {
- if (javaClass.size() == 0) {
- fprintf(stderr, "Must supply --javaClass if supplying a Java filename");
- return 1;
- }
-
- if (javaPackage.size() == 0) {
- fprintf(stderr, "Must supply --javaPackage if supplying a Java filename");
- return 1;
- }
-
- if (moduleName.size() == 0) {
- fprintf(stderr, "Must supply --module if supplying a Java filename");
- return 1;
- }
-
- FILE* out = fopen(javaFilename.c_str(), "w");
- if (out == NULL) {
- fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
- return 1;
- }
-
- if (compileQ) {
- errorCount = android::stats_log_api_gen::write_stats_log_java_q_for_module(
- out, atoms, attributionDecl, javaClass, javaPackage, supportWorkSource);
- } else {
- errorCount = android::stats_log_api_gen::write_stats_log_java(
- out, atoms, attributionDecl, javaClass, javaPackage, supportQ,
- supportWorkSource);
- }
-
- fclose(out);
- }
-
- return errorCount;
-}
-
-} // namespace stats_log_api_gen
-} // namespace android
-
-/**
- * Main.
- */
-int main(int argc, char const* const* argv) {
- GOOGLE_PROTOBUF_VERIFY_VERSION;
-
- return android::stats_log_api_gen::run(argc, argv);
-}
diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp
deleted file mode 100644
index 0c6c009..0000000
--- a/tools/stats_log_api_gen/native_writer.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "native_writer.h"
-
-#include "utils.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-static void write_native_annotation_constants(FILE* out) {
- fprintf(out, "// Annotation constants.\n");
-
- for (const auto& [id, name] : ANNOTATION_ID_CONSTANTS) {
- fprintf(out, "const uint8_t %s = %hhu;\n", name.c_str(), id);
- }
- fprintf(out, "\n");
-}
-
-static void write_annotations(FILE* out, int argIndex,
- const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
- const string& methodPrefix, const string& methodSuffix) {
- FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
- fieldNumberToAtomDeclSet.find(argIndex);
- if (fieldNumberToAtomDeclSet.end() == fieldNumberToAtomDeclSetIt) {
- return;
- }
- const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second;
- for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
- const string atomConstant = make_constant_name(atomDecl->name);
- fprintf(out, " if (%s == code) {\n", atomConstant.c_str());
- const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex);
- int resetState = -1;
- int defaultState = -1;
- for (const shared_ptr<Annotation>& annotation : annotations) {
- const string& annotationConstant = ANNOTATION_ID_CONSTANTS.at(annotation->annotationId);
- switch (annotation->type) {
- case ANNOTATION_TYPE_INT:
- if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
- resetState = annotation->value.intValue;
- } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
- defaultState = annotation->value.intValue;
- } else {
- fprintf(out, " %saddInt32Annotation(%s%s, %d);\n",
- methodPrefix.c_str(), methodSuffix.c_str(),
- annotationConstant.c_str(), annotation->value.intValue);
- }
- break;
- case ANNOTATION_TYPE_BOOL:
- // TODO(b/151786433): Write annotation constant name instead of
- // annotation id literal.
- fprintf(out, " %saddBoolAnnotation(%s%s, %s);\n", methodPrefix.c_str(),
- methodSuffix.c_str(), annotationConstant.c_str(),
- annotation->value.boolValue ? "true" : "false");
- break;
- default:
- break;
- }
- }
- if (defaultState != -1 && resetState != -1) {
- const string& annotationConstant =
- ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_TRIGGER_STATE_RESET);
- fprintf(out, " if (arg%d == %d) {\n", argIndex, resetState);
- fprintf(out, " %saddInt32Annotation(%s%s, %d);\n", methodPrefix.c_str(),
- methodSuffix.c_str(), annotationConstant.c_str(), defaultState);
- fprintf(out, " }\n");
- }
- fprintf(out, " }\n");
- }
-}
-
-static int write_native_stats_write_methods(FILE* out, const Atoms& atoms,
- const AtomDecl& attributionDecl, const bool supportQ) {
- fprintf(out, "\n");
- for (auto signatureInfoMapIt = atoms.signatureInfoMap.begin();
- signatureInfoMapIt != atoms.signatureInfoMap.end(); signatureInfoMapIt++) {
- vector<java_type_t> signature = signatureInfoMapIt->first;
- const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
- // Key value pairs not supported in native.
- if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
- continue;
- }
- write_native_method_signature(out, "int stats_write", signature, attributionDecl, " {");
-
- int argIndex = 1;
- if (supportQ) {
- fprintf(out, " StatsEventCompat event;\n");
- fprintf(out, " event.setAtomId(code);\n");
- write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "event.", "");
- for (vector<java_type_t>::const_iterator arg = signature.begin();
- arg != signature.end(); arg++) {
- switch (*arg) {
- case JAVA_TYPE_ATTRIBUTION_CHAIN: {
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
- fprintf(out, " event.writeAttributionChain(%s, %s_length, %s);\n",
- uidName, uidName, tagName);
- break;
- }
- case JAVA_TYPE_BYTE_ARRAY:
- fprintf(out, " event.writeByteArray(arg%d.arg, arg%d.arg_length);\n",
- argIndex, argIndex);
- break;
- case JAVA_TYPE_BOOLEAN:
- fprintf(out, " event.writeBool(arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_INT: // Fall through.
- case JAVA_TYPE_ENUM:
- fprintf(out, " event.writeInt32(arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_FLOAT:
- fprintf(out, " event.writeFloat(arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_LONG:
- fprintf(out, " event.writeInt64(arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_STRING:
- fprintf(out, " event.writeString(arg%d);\n", argIndex);
- break;
- default:
- // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS.
- fprintf(stderr, "Encountered unsupported type.");
- return 1;
- }
- write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "event.", "");
- argIndex++;
- }
- fprintf(out, " return event.writeToSocket();\n");
- } else {
- fprintf(out, " AStatsEvent* event = AStatsEvent_obtain();\n");
- fprintf(out, " AStatsEvent_setAtomId(event, code);\n");
- write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "AStatsEvent_",
- "event, ");
- for (vector<java_type_t>::const_iterator arg = signature.begin();
- arg != signature.end(); arg++) {
- switch (*arg) {
- case JAVA_TYPE_ATTRIBUTION_CHAIN: {
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
- fprintf(out,
- " AStatsEvent_writeAttributionChain(event, "
- "reinterpret_cast<const uint32_t*>(%s), %s.data(), "
- "static_cast<uint8_t>(%s_length));\n",
- uidName, tagName, uidName);
- break;
- }
- case JAVA_TYPE_BYTE_ARRAY:
- fprintf(out,
- " AStatsEvent_writeByteArray(event, "
- "reinterpret_cast<const uint8_t*>(arg%d.arg), "
- "arg%d.arg_length);\n",
- argIndex, argIndex);
- break;
- case JAVA_TYPE_BOOLEAN:
- fprintf(out, " AStatsEvent_writeBool(event, arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_INT: // Fall through.
- case JAVA_TYPE_ENUM:
- fprintf(out, " AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_FLOAT:
- fprintf(out, " AStatsEvent_writeFloat(event, arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_LONG:
- fprintf(out, " AStatsEvent_writeInt64(event, arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_STRING:
- fprintf(out, " AStatsEvent_writeString(event, arg%d);\n", argIndex);
- break;
- default:
- // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS
- fprintf(stderr, "Encountered unsupported type.");
- return 1;
- }
- write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "AStatsEvent_",
- "event, ");
- argIndex++;
- }
- fprintf(out, " const int ret = AStatsEvent_write(event);\n");
- fprintf(out, " AStatsEvent_release(event);\n");
- fprintf(out, " return ret;\n");
- }
- fprintf(out, "}\n\n");
- }
- return 0;
-}
-
-static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms& atoms,
- const AtomDecl& attributionDecl) {
- fprintf(out, "\n");
- for (auto signature_it = atoms.nonChainedSignatureInfoMap.begin();
- signature_it != atoms.nonChainedSignatureInfoMap.end(); signature_it++) {
- vector<java_type_t> signature = signature_it->first;
- // Key value pairs not supported in native.
- if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
- continue;
- }
-
- write_native_method_signature(out, "int stats_write_non_chained", signature,
- attributionDecl, " {");
-
- vector<java_type_t> newSignature;
-
- // First two args form the attribution node so size goes down by 1.
- newSignature.reserve(signature.size() - 1);
-
- // First arg is Attribution Chain.
- newSignature.push_back(JAVA_TYPE_ATTRIBUTION_CHAIN);
-
- // Followed by the originial signature except the first 2 args.
- newSignature.insert(newSignature.end(), signature.begin() + 2, signature.end());
-
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
- fprintf(out, " const int32_t* %s = &arg1;\n", uidName);
- fprintf(out, " const size_t %s_length = 1;\n", uidName);
- fprintf(out, " const std::vector<char const*> %s(1, arg2);\n", tagName);
- fprintf(out, " return ");
- write_native_method_call(out, "stats_write", newSignature, attributionDecl, 2);
-
- fprintf(out, "}\n\n");
- }
-}
-
-static void write_native_method_header(FILE* out, const string& methodName,
- const SignatureInfoMap& signatureInfoMap,
- const AtomDecl& attributionDecl) {
- for (auto signatureInfoMapIt = signatureInfoMap.begin();
- signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
- vector<java_type_t> signature = signatureInfoMapIt->first;
-
- // Key value pairs not supported in native.
- if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
- continue;
- }
- write_native_method_signature(out, methodName, signature, attributionDecl, ";");
- }
-}
-
-int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
- const string& cppNamespace, const string& importHeader,
- const bool supportQ) {
- // Print prelude
- fprintf(out, "// This file is autogenerated\n");
- fprintf(out, "\n");
-
- fprintf(out, "#include <%s>\n", importHeader.c_str());
- if (supportQ) {
- fprintf(out, "#include <StatsEventCompat.h>\n");
- } else {
- fprintf(out, "#include <stats_event.h>\n");
- }
-
- fprintf(out, "\n");
- write_namespace(out, cppNamespace);
-
- write_native_stats_write_methods(out, atoms, attributionDecl, supportQ);
- write_native_stats_write_non_chained_methods(out, atoms, attributionDecl);
-
- // Print footer
- fprintf(out, "\n");
- write_closing_namespace(out, cppNamespace);
-
- return 0;
-}
-
-int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
- const string& cppNamespace) {
- // Print prelude
- fprintf(out, "// This file is autogenerated\n");
- fprintf(out, "\n");
- fprintf(out, "#pragma once\n");
- fprintf(out, "\n");
- fprintf(out, "#include <stdint.h>\n");
- fprintf(out, "#include <vector>\n");
- fprintf(out, "#include <map>\n");
- fprintf(out, "#include <set>\n");
- fprintf(out, "\n");
-
- write_namespace(out, cppNamespace);
- fprintf(out, "\n");
- fprintf(out, "/*\n");
- fprintf(out, " * API For logging statistics events.\n");
- fprintf(out, " */\n");
- fprintf(out, "\n");
-
- write_native_atom_constants(out, atoms, attributionDecl);
-
- // Print constants for the enum values.
- fprintf(out, "//\n");
- fprintf(out, "// Constants for enum values\n");
- fprintf(out, "//\n\n");
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
- field != (*atomIt)->fields.end(); field++) {
- if (field->javaType == JAVA_TYPE_ENUM) {
- fprintf(out, "// Values for %s.%s\n", (*atomIt)->message.c_str(),
- field->name.c_str());
- for (map<int, string>::const_iterator value = field->enumValues.begin();
- value != field->enumValues.end(); value++) {
- fprintf(out, "const int32_t %s__%s__%s = %d;\n",
- make_constant_name((*atomIt)->message).c_str(),
- make_constant_name(field->name).c_str(),
- make_constant_name(value->second).c_str(), value->first);
- }
- fprintf(out, "\n");
- }
- }
- }
-
- write_native_annotation_constants(out);
-
- fprintf(out, "struct BytesField {\n");
- fprintf(out,
- " BytesField(char const* array, size_t len) : arg(array), "
- "arg_length(len) {}\n");
- fprintf(out, " char const* arg;\n");
- fprintf(out, " size_t arg_length;\n");
- fprintf(out, "};\n");
- fprintf(out, "\n");
-
- // Print write methods
- fprintf(out, "//\n");
- fprintf(out, "// Write methods\n");
- fprintf(out, "//\n");
- write_native_method_header(out, "int stats_write", atoms.signatureInfoMap, attributionDecl);
-
- fprintf(out, "//\n");
- fprintf(out, "// Write flattened methods\n");
- fprintf(out, "//\n");
- write_native_method_header(out, "int stats_write_non_chained", atoms.nonChainedSignatureInfoMap,
- attributionDecl);
-
- fprintf(out, "\n");
- write_closing_namespace(out, cppNamespace);
-
- return 0;
-}
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/native_writer.h b/tools/stats_log_api_gen/native_writer.h
deleted file mode 100644
index 264d4db..0000000
--- a/tools/stats_log_api_gen/native_writer.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <string.h>
-
-#include "Collation.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using namespace std;
-
-int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
- const string& cppNamespace, const string& importHeader,
- const bool supportQ);
-
-int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
- const string& cppNamespace);
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
deleted file mode 100644
index a3ea785..0000000
--- a/tools/stats_log_api_gen/test.proto
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-import "frameworks/proto_logging/stats/atoms.proto";
-import "frameworks/proto_logging/stats/atom_field_options.proto";
-
-package android.stats_log_api_gen;
-
-message IntAtom {
- optional int32 field1 = 1;
-}
-
-message AnotherIntAtom {
- optional int32 field1 = 1;
-}
-
-message OutOfOrderAtom {
- optional int32 field2 = 2;
- optional int32 field1 = 1;
-}
-
-enum AnEnum {
- VALUE0 = 0;
- VALUE1 = 1;
-}
-
-message AllTypesAtom {
- repeated android.os.statsd.AttributionNode attribution_chain = 1;
- optional float float_field = 2;
- optional int64 int64_field = 3;
- optional uint64 uint64_field = 4;
- optional int32 int32_field = 5;
- optional fixed64 fixed64_field = 6;
- optional fixed32 fixed32_field = 7;
- optional bool bool_field = 8;
- optional string string_field = 9;
- optional uint32 uint32_field = 10;
- optional AnEnum enum_field = 11;
- optional sfixed32 sfixed32_field = 12;
- optional sfixed64 sfixed64_field = 13;
- optional sint32 sint32_field = 14;
- optional sint64 sint64_field = 15;
-}
-
-message Event {
- oneof event {
- OutOfOrderAtom out_of_order_atom = 2;
- IntAtom int_atom = 1;
- AnotherIntAtom another_int_atom = 3;
- AllTypesAtom all_types_atom = 4;
- }
-}
-
-message BadTypesAtom {
- optional IntAtom bad_int_atom = 1;
- optional bytes bad_bytes = 2;
- repeated int32 repeated_field = 3;
- optional double double_field = 4;
-}
-
-message BadTypesEvent {
- oneof event {
- BadTypesAtom bad_types_atom = 1;
- }
-}
-
-message BadSkippedFieldSingleAtom {
- optional int32 field2 = 2;
-}
-
-message BadSkippedFieldSingle {
- oneof event {
- BadSkippedFieldSingleAtom bad = 1;
- }
-}
-
-message BadSkippedFieldMultipleAtom {
- optional int32 field1 = 1;
- optional int32 field3 = 3;
- optional int32 field5 = 5;
-}
-
-message BadSkippedFieldMultiple {
- oneof event {
- BadSkippedFieldMultipleAtom bad = 1;
- }
-}
-
-message BadAttributionNodePositionAtom {
- optional int32 field1 = 1;
- repeated android.os.statsd.AttributionNode attribution = 2;
-}
-
-message BadAttributionNodePosition {
- oneof event { BadAttributionNodePositionAtom bad = 1; }
-}
-
-message GoodEventWithBinaryFieldAtom {
- oneof event { GoodBinaryFieldAtom field1 = 1; }
-}
-
-message ComplexField {
- optional string str = 1;
-}
-
-message GoodBinaryFieldAtom {
- optional int32 field1 = 1;
- optional ComplexField bf = 2 [(android.os.statsd.log_mode) = MODE_BYTES];
-}
-
-message BadEventWithBinaryFieldAtom {
- oneof event { BadBinaryFieldAtom field1 = 1; }
-}
-
-message BadBinaryFieldAtom {
- optional int32 field1 = 1;
- optional ComplexField bf = 2;
-}
-
-message BadStateAtoms {
- oneof event {
- BadStateAtom1 bad1 = 1;
- BadStateAtom2 bad2 = 2;
- BadStateAtom3 bad3 = 3;
- }
-}
-
-message GoodStateAtoms {
- oneof event {
- GoodStateAtom1 good1 = 1;
- GoodStateAtom2 good2 = 2;
- }
-}
-
-// The atom has only primary field but no exclusive state field.
-message BadStateAtom1 {
- optional int32 uid = 1 [(android.os.statsd.state_field_option).primary_field = true];
-}
-
-// Only primative types can be annotated.
-message BadStateAtom2 {
- repeated android.os.statsd.AttributionNode attribution = 1
- [(android.os.statsd.state_field_option).primary_field = true];
- optional int32 state = 2 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-// Having 2 exclusive state field in the atom means the atom is badly designed.
-// E.g., putting bluetooth state and wifi state in the same atom.
-message BadStateAtom3 {
- optional int32 uid = 1 [(android.os.statsd.state_field_option).primary_field = true];
- optional int32 state = 2 [(android.os.statsd.state_field_option).exclusive_state = true];
- optional int32 state2 = 3 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-message GoodStateAtom1 {
- optional int32 uid = 1 [(android.os.statsd.state_field_option).primary_field = true];
- optional int32 state = 2 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-// Atoms can have exclusive state field, but no primary field. That means
-// the state is globally exclusive (e.g., DisplayState).
-message GoodStateAtom2 {
- optional int32 uid = 1;
- optional int32 state = 2 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-// We can have more than one primary fields. That means their combination is a
-// primary key.
-message GoodStateAtom3 {
- optional int32 uid = 1 [(android.os.statsd.state_field_option).primary_field = true];
- optional int32 tid = 2 [(android.os.statsd.state_field_option).primary_field = true];
- optional int32 state = 3 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-message ModuleOneAtom {
- optional int32 field = 1 [(android.os.statsd.is_uid) = true];
-}
-
-message ModuleTwoAtom {
- optional int32 field = 1;
-}
-
-message ModuleOneAndTwoAtom {
- optional int32 field = 1 [(android.os.statsd.state_field_option).exclusive_state = true];
-}
-
-message NoModuleAtom {
- optional string field = 1;
-}
-
-message ModuleAtoms {
- oneof event {
- ModuleOneAtom module_one_atom = 1 [(android.os.statsd.module) = "module1"];
- ModuleTwoAtom module_two_atom = 2 [(android.os.statsd.module) = "module2"];
- ModuleOneAndTwoAtom module_one_and_two_atom = 3 [
- (android.os.statsd.module) = "module1", (android.os.statsd.module) = "module2"
- ];
- NoModuleAtom no_module_atom = 4;
- }
-}
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
deleted file mode 100644
index dbae588..0000000
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Copyright (C) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#include "Collation.h"
-#include "frameworks/base/tools/stats_log_api_gen/test.pb.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using std::map;
-using std::set;
-using std::vector;
-
-/**
- * Return whether the map contains a vector of the elements provided.
- */
-static bool map_contains_vector(const SignatureInfoMap& s, int count, ...) {
- va_list args;
- vector<java_type_t> v;
-
- va_start(args, count);
- for (int i = 0; i < count; i++) {
- v.push_back((java_type_t)va_arg(args, int));
- }
- va_end(args);
-
- return s.find(v) != s.end();
-}
-
-/**
- * Expect that the provided map contains the elements provided.
- */
-#define EXPECT_MAP_CONTAINS_SIGNATURE(s, ...) \
- do { \
- int count = sizeof((int[]){__VA_ARGS__}) / sizeof(int); \
- EXPECT_TRUE(map_contains_vector(s, count, __VA_ARGS__)); \
- } while (0)
-
-/** Expects that the provided atom has no enum values for any field. */
-#define EXPECT_NO_ENUM_FIELD(atom) \
- do { \
- for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
- field != atom->fields.end(); field++) { \
- EXPECT_TRUE(field->enumValues.empty()); \
- } \
- } while (0)
-
-/** Expects that exactly one specific field has expected enum values. */
-#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values) \
- do { \
- for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
- field != atom->fields.end(); field++) { \
- if (field->name == field_name) { \
- EXPECT_EQ(field->enumValues, values); \
- } else { \
- EXPECT_TRUE(field->enumValues.empty()); \
- } \
- } \
- } while (0)
-
-/**
- * Test a correct collation, with all the types.
- */
-TEST(CollationTest, CollateStats) {
- Atoms atoms;
- int errorCount = collate_atoms(Event::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
- EXPECT_EQ(0, errorCount);
- EXPECT_EQ(3ul, atoms.signatureInfoMap.size());
-
- // IntAtom, AnotherIntAtom
- EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT);
-
- // OutOfOrderAtom
- EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT, JAVA_TYPE_INT);
-
- // AllTypesAtom
- EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap,
- JAVA_TYPE_ATTRIBUTION_CHAIN, // AttributionChain
- JAVA_TYPE_FLOAT, // float
- JAVA_TYPE_LONG, // int64
- JAVA_TYPE_LONG, // uint64
- JAVA_TYPE_INT, // int32
- JAVA_TYPE_LONG, // fixed64
- JAVA_TYPE_INT, // fixed32
- JAVA_TYPE_BOOLEAN, // bool
- JAVA_TYPE_STRING, // string
- JAVA_TYPE_INT, // uint32
- JAVA_TYPE_INT, // AnEnum
- JAVA_TYPE_INT, // sfixed32
- JAVA_TYPE_LONG, // sfixed64
- JAVA_TYPE_INT, // sint32
- JAVA_TYPE_LONG // sint64
- );
-
- EXPECT_EQ(4ul, atoms.decls.size());
-
- AtomDeclSet::const_iterator atomIt = atoms.decls.begin();
- EXPECT_EQ(1, (*atomIt)->code);
- EXPECT_EQ("int_atom", (*atomIt)->name);
- EXPECT_EQ("IntAtom", (*atomIt)->message);
- EXPECT_NO_ENUM_FIELD((*atomIt));
- atomIt++;
-
- EXPECT_EQ(2, (*atomIt)->code);
- EXPECT_EQ("out_of_order_atom", (*atomIt)->name);
- EXPECT_EQ("OutOfOrderAtom", (*atomIt)->message);
- EXPECT_NO_ENUM_FIELD((*atomIt));
- atomIt++;
-
- EXPECT_EQ(3, (*atomIt)->code);
- EXPECT_EQ("another_int_atom", (*atomIt)->name);
- EXPECT_EQ("AnotherIntAtom", (*atomIt)->message);
- EXPECT_NO_ENUM_FIELD((*atomIt));
- atomIt++;
-
- EXPECT_EQ(4, (*atomIt)->code);
- EXPECT_EQ("all_types_atom", (*atomIt)->name);
- EXPECT_EQ("AllTypesAtom", (*atomIt)->message);
- map<int, string> enumValues;
- enumValues[0] = "VALUE0";
- enumValues[1] = "VALUE1";
- EXPECT_HAS_ENUM_FIELD((*atomIt), "enum_field", enumValues);
- atomIt++;
-
- EXPECT_EQ(atoms.decls.end(), atomIt);
-}
-
-/**
- * Test that event class that contains stuff other than the atoms is rejected.
- */
-TEST(CollationTest, NonMessageTypeFails) {
- Atoms atoms;
- int errorCount = collate_atoms(IntAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
- EXPECT_EQ(1, errorCount);
-}
-
-/**
- * Test that atoms that have non-primitive types or repeated fields are
- * rejected.
- */
-TEST(CollationTest, FailOnBadTypes) {
- Atoms atoms;
- int errorCount = collate_atoms(BadTypesEvent::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
- EXPECT_EQ(4, errorCount);
-}
-
-/**
- * Test that atoms that skip field numbers (in the first position) are rejected.
- */
-TEST(CollationTest, FailOnSkippedFieldsSingle) {
- Atoms atoms;
- int errorCount =
- collate_atoms(BadSkippedFieldSingle::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
- EXPECT_EQ(1, errorCount);
-}
-
-/**
- * Test that atoms that skip field numbers (not in the first position, and
- * multiple times) are rejected.
- */
-TEST(CollationTest, FailOnSkippedFieldsMultiple) {
- Atoms atoms;
- int errorCount =
- collate_atoms(BadSkippedFieldMultiple::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
- EXPECT_EQ(2, errorCount);
-}
-
-/**
- * Test that atoms that have an attribution chain not in the first position are
- * rejected.
- */
-TEST(CollationTest, FailBadAttributionNodePosition) {
- Atoms atoms;
- int errorCount =
- collate_atoms(BadAttributionNodePosition::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
- EXPECT_EQ(1, errorCount);
-}
-
-TEST(CollationTest, FailOnBadStateAtomOptions) {
- Atoms atoms;
- int errorCount = collate_atoms(BadStateAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
-
- EXPECT_EQ(3, errorCount);
-}
-
-TEST(CollationTest, PassOnGoodStateAtomOptions) {
- Atoms atoms;
- int errorCount = collate_atoms(GoodStateAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
- EXPECT_EQ(0, errorCount);
-}
-
-TEST(CollationTest, PassOnGoodBinaryFieldAtom) {
- Atoms atoms;
- int errorCount =
- collate_atoms(GoodEventWithBinaryFieldAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms);
- EXPECT_EQ(0, errorCount);
-}
-
-TEST(CollationTest, FailOnBadBinaryFieldAtom) {
- Atoms atoms;
- int errorCount =
- collate_atoms(BadEventWithBinaryFieldAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms);
- EXPECT_TRUE(errorCount > 0);
-}
-
-TEST(CollationTest, PassOnLogFromModuleAtom) {
- Atoms atoms;
- int errorCount = collate_atoms(ModuleAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
- EXPECT_EQ(errorCount, 0);
- EXPECT_EQ(atoms.decls.size(), 4ul);
-}
-
-TEST(CollationTest, RecognizeModuleAtom) {
- Atoms atoms;
- int errorCount = collate_atoms(ModuleAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
- EXPECT_EQ(errorCount, 0);
- EXPECT_EQ(atoms.decls.size(), 4ul);
- EXPECT_EQ(atoms.signatureInfoMap.size(), 2u);
- EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT);
- EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_STRING);
-
- SignatureInfoMap::const_iterator signatureInfoMapIt;
- const vector<java_type_t>* signature;
- const FieldNumberToAtomDeclSet* fieldNumberToAtomDeclSet;
- FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt;
- const AtomDeclSet* atomDeclSet;
- AtomDeclSet::const_iterator atomDeclSetIt;
- AtomDecl* atomDecl;
- FieldNumberToAnnotations* fieldNumberToAnnotations;
- FieldNumberToAnnotations::const_iterator fieldNumberToAnnotationsIt;
- const AnnotationSet* annotationSet;
- AnnotationSet::const_iterator annotationSetIt;
- Annotation* annotation;
-
- signatureInfoMapIt = atoms.signatureInfoMap.begin();
- signature = &(signatureInfoMapIt->first);
- fieldNumberToAtomDeclSet = &signatureInfoMapIt->second;
- EXPECT_EQ(1ul, signature->size());
- EXPECT_EQ(JAVA_TYPE_INT, signature->at(0));
- EXPECT_EQ(1ul, fieldNumberToAtomDeclSet->size());
- fieldNumberToAtomDeclSetIt = fieldNumberToAtomDeclSet->begin();
- EXPECT_EQ(1, fieldNumberToAtomDeclSetIt->first);
- atomDeclSet = &fieldNumberToAtomDeclSetIt->second;
- EXPECT_EQ(2ul, atomDeclSet->size());
- atomDeclSetIt = atomDeclSet->begin();
- atomDecl = atomDeclSetIt->get();
- EXPECT_EQ(1, atomDecl->code);
- fieldNumberToAnnotations = &atomDecl->fieldNumberToAnnotations;
- fieldNumberToAnnotationsIt = fieldNumberToAnnotations->find(1);
- EXPECT_NE(fieldNumberToAnnotations->end(), fieldNumberToAnnotationsIt);
- annotationSet = &fieldNumberToAnnotationsIt->second;
- EXPECT_EQ(1ul, annotationSet->size());
- annotationSetIt = annotationSet->begin();
- annotation = annotationSetIt->get();
- EXPECT_EQ(ANNOTATION_ID_IS_UID, annotation->annotationId);
- EXPECT_EQ(1, annotation->atomId);
- EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
- EXPECT_TRUE(annotation->value.boolValue);
-
- atomDeclSetIt++;
- atomDecl = atomDeclSetIt->get();
- EXPECT_EQ(3, atomDecl->code);
- fieldNumberToAnnotations = &atomDecl->fieldNumberToAnnotations;
- fieldNumberToAnnotationsIt = fieldNumberToAnnotations->find(1);
- EXPECT_NE(fieldNumberToAnnotations->end(), fieldNumberToAnnotationsIt);
- annotationSet = &fieldNumberToAnnotationsIt->second;
- EXPECT_EQ(1ul, annotationSet->size());
- annotationSetIt = annotationSet->begin();
- annotation = annotationSetIt->get();
- EXPECT_EQ(ANNOTATION_ID_EXCLUSIVE_STATE, annotation->annotationId);
- EXPECT_EQ(3, annotation->atomId);
- EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
- EXPECT_TRUE(annotation->value.boolValue);
-
- signatureInfoMapIt++;
- signature = &signatureInfoMapIt->first;
- fieldNumberToAtomDeclSet = &signatureInfoMapIt->second;
- EXPECT_EQ(1ul, signature->size());
- EXPECT_EQ(JAVA_TYPE_STRING, signature->at(0));
- EXPECT_EQ(0ul, fieldNumberToAtomDeclSet->size());
-}
-
-TEST(CollationTest, RecognizeModule1Atom) {
- Atoms atoms;
- const string moduleName = "module1";
- int errorCount = collate_atoms(ModuleAtoms::descriptor(), moduleName, &atoms);
- EXPECT_EQ(errorCount, 0);
- EXPECT_EQ(atoms.decls.size(), 2ul);
- EXPECT_EQ(atoms.signatureInfoMap.size(), 1u);
- EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT);
-
- SignatureInfoMap::const_iterator signatureInfoMapIt;
- const vector<java_type_t>* signature;
- const FieldNumberToAtomDeclSet* fieldNumberToAtomDeclSet;
- FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt;
- const AtomDeclSet* atomDeclSet;
- AtomDeclSet::const_iterator atomDeclSetIt;
- AtomDecl* atomDecl;
- FieldNumberToAnnotations* fieldNumberToAnnotations;
- FieldNumberToAnnotations::const_iterator fieldNumberToAnnotationsIt;
- const AnnotationSet* annotationSet;
- AnnotationSet::const_iterator annotationSetIt;
- Annotation* annotation;
-
- signatureInfoMapIt = atoms.signatureInfoMap.begin();
- signature = &(signatureInfoMapIt->first);
- fieldNumberToAtomDeclSet = &signatureInfoMapIt->second;
- EXPECT_EQ(1ul, signature->size());
- EXPECT_EQ(JAVA_TYPE_INT, signature->at(0));
- EXPECT_EQ(1ul, fieldNumberToAtomDeclSet->size());
- fieldNumberToAtomDeclSetIt = fieldNumberToAtomDeclSet->begin();
- EXPECT_EQ(1, fieldNumberToAtomDeclSetIt->first);
- atomDeclSet = &fieldNumberToAtomDeclSetIt->second;
- EXPECT_EQ(2ul, atomDeclSet->size());
- atomDeclSetIt = atomDeclSet->begin();
- atomDecl = atomDeclSetIt->get();
- EXPECT_EQ(1, atomDecl->code);
- fieldNumberToAnnotations = &atomDecl->fieldNumberToAnnotations;
- fieldNumberToAnnotationsIt = fieldNumberToAnnotations->find(1);
- EXPECT_NE(fieldNumberToAnnotations->end(), fieldNumberToAnnotationsIt);
- annotationSet = &fieldNumberToAnnotationsIt->second;
- EXPECT_EQ(1ul, annotationSet->size());
- annotationSetIt = annotationSet->begin();
- annotation = annotationSetIt->get();
- EXPECT_EQ(ANNOTATION_ID_IS_UID, annotation->annotationId);
- EXPECT_EQ(1, annotation->atomId);
- EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
- EXPECT_TRUE(annotation->value.boolValue);
-
- atomDeclSetIt++;
- atomDecl = atomDeclSetIt->get();
- EXPECT_EQ(3, atomDecl->code);
- fieldNumberToAnnotations = &atomDecl->fieldNumberToAnnotations;
- fieldNumberToAnnotationsIt = fieldNumberToAnnotations->find(1);
- EXPECT_NE(fieldNumberToAnnotations->end(), fieldNumberToAnnotationsIt);
- annotationSet = &fieldNumberToAnnotationsIt->second;
- EXPECT_EQ(1ul, annotationSet->size());
- annotationSetIt = annotationSet->begin();
- annotation = annotationSetIt->get();
- EXPECT_EQ(ANNOTATION_ID_EXCLUSIVE_STATE, annotation->annotationId);
- EXPECT_EQ(3, annotation->atomId);
- EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
- EXPECT_TRUE(annotation->value.boolValue);
-}
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/utils.cpp b/tools/stats_log_api_gen/utils.cpp
deleted file mode 100644
index abb8913..0000000
--- a/tools/stats_log_api_gen/utils.cpp
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "utils.h"
-
-#include "android-base/strings.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-static void build_non_chained_decl_map(const Atoms& atoms,
- std::map<int, AtomDeclSet::const_iterator>* decl_map) {
- for (AtomDeclSet::const_iterator atomIt = atoms.non_chained_decls.begin();
- atomIt != atoms.non_chained_decls.end(); atomIt++) {
- decl_map->insert(std::make_pair((*atomIt)->code, atomIt));
- }
-}
-
-/**
- * Turn lower and camel case into upper case with underscores.
- */
-string make_constant_name(const string& str) {
- string result;
- const int N = str.size();
- bool underscore_next = false;
- for (int i = 0; i < N; i++) {
- char c = str[i];
- if (c >= 'A' && c <= 'Z') {
- if (underscore_next) {
- result += '_';
- underscore_next = false;
- }
- } else if (c >= 'a' && c <= 'z') {
- c = 'A' + c - 'a';
- underscore_next = true;
- } else if (c == '_') {
- underscore_next = false;
- }
- result += c;
- }
- return result;
-}
-
-const char* cpp_type_name(java_type_t type) {
- switch (type) {
- case JAVA_TYPE_BOOLEAN:
- return "bool";
- case JAVA_TYPE_INT:
- case JAVA_TYPE_ENUM:
- return "int32_t";
- case JAVA_TYPE_LONG:
- return "int64_t";
- case JAVA_TYPE_FLOAT:
- return "float";
- case JAVA_TYPE_DOUBLE:
- return "double";
- case JAVA_TYPE_STRING:
- return "char const*";
- case JAVA_TYPE_BYTE_ARRAY:
- return "const BytesField&";
- default:
- return "UNKNOWN";
- }
-}
-
-const char* java_type_name(java_type_t type) {
- switch (type) {
- case JAVA_TYPE_BOOLEAN:
- return "boolean";
- case JAVA_TYPE_INT:
- case JAVA_TYPE_ENUM:
- return "int";
- case JAVA_TYPE_LONG:
- return "long";
- case JAVA_TYPE_FLOAT:
- return "float";
- case JAVA_TYPE_DOUBLE:
- return "double";
- case JAVA_TYPE_STRING:
- return "java.lang.String";
- case JAVA_TYPE_BYTE_ARRAY:
- return "byte[]";
- default:
- return "UNKNOWN";
- }
-}
-
-// Native
-// Writes namespaces for the cpp and header files, returning the number of
-// namespaces written.
-void write_namespace(FILE* out, const string& cppNamespaces) {
- vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
- for (string cppNamespace : cppNamespaceVec) {
- fprintf(out, "namespace %s {\n", cppNamespace.c_str());
- }
-}
-
-// Writes namespace closing brackets for cpp and header files.
-void write_closing_namespace(FILE* out, const string& cppNamespaces) {
- vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
- for (auto it = cppNamespaceVec.rbegin(); it != cppNamespaceVec.rend(); ++it) {
- fprintf(out, "} // namespace %s\n", it->c_str());
- }
-}
-
-static void write_cpp_usage(FILE* out, const string& method_name, const string& atom_code_name,
- const shared_ptr<AtomDecl> atom, const AtomDecl& attributionDecl) {
- fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
-
- for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- if (chainField.javaType == JAVA_TYPE_STRING) {
- fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
- chainField.name.c_str());
- } else {
- fprintf(out, ", const %s* %s, size_t %s_length",
- cpp_type_name(chainField.javaType), chainField.name.c_str(),
- chainField.name.c_str());
- }
- }
- } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out,
- ", const std::map<int, int32_t>& %s_int"
- ", const std::map<int, int64_t>& %s_long"
- ", const std::map<int, char const*>& %s_str"
- ", const std::map<int, float>& %s_float",
- field->name.c_str(), field->name.c_str(), field->name.c_str(),
- field->name.c_str());
- } else {
- fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
- }
- }
- fprintf(out, ");\n");
-}
-
-void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl) {
- fprintf(out, "/**\n");
- fprintf(out, " * Constants for atom codes.\n");
- fprintf(out, " */\n");
- fprintf(out, "enum {\n");
-
- std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
- build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
-
- size_t i = 0;
- // Print atom constants
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- string constant = make_constant_name((*atomIt)->name);
- fprintf(out, "\n");
- fprintf(out, " /**\n");
- fprintf(out, " * %s %s\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
- write_cpp_usage(out, "stats_write", constant, *atomIt, attributionDecl);
-
- auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
- if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
- write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
- attributionDecl);
- }
- fprintf(out, " */\n");
- char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
- fprintf(out, " %s = %d%s\n", constant.c_str(), (*atomIt)->code, comma);
- i++;
- }
- fprintf(out, "\n");
- fprintf(out, "};\n");
- fprintf(out, "\n");
-}
-
-void write_native_method_signature(FILE* out, const string& methodName,
- const vector<java_type_t>& signature,
- const AtomDecl& attributionDecl, const string& closer) {
- fprintf(out, "%s(int32_t code", methodName.c_str());
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- if (chainField.javaType == JAVA_TYPE_STRING) {
- fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
- chainField.name.c_str());
- } else {
- fprintf(out, ", const %s* %s, size_t %s_length",
- cpp_type_name(chainField.javaType), chainField.name.c_str(),
- chainField.name.c_str());
- }
- }
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out,
- ", const std::map<int, int32_t>& arg%d_1, "
- "const std::map<int, int64_t>& arg%d_2, "
- "const std::map<int, char const*>& arg%d_3, "
- "const std::map<int, float>& arg%d_4",
- argIndex, argIndex, argIndex, argIndex);
- } else {
- fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
- }
- argIndex++;
- }
- fprintf(out, ")%s\n", closer.c_str());
-}
-
-void write_native_method_call(FILE* out, const string& methodName,
- const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
- int argIndex) {
- fprintf(out, "%s(code", methodName.c_str());
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- if (chainField.javaType == JAVA_TYPE_STRING) {
- fprintf(out, ", %s", chainField.name.c_str());
- } else {
- fprintf(out, ", %s, %s_length", chainField.name.c_str(),
- chainField.name.c_str());
- }
- }
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex, argIndex, argIndex,
- argIndex);
- } else {
- fprintf(out, ", arg%d", argIndex);
- }
- argIndex++;
- }
- fprintf(out, ");\n");
-}
-
-// Java
-void write_java_atom_codes(FILE* out, const Atoms& atoms) {
- fprintf(out, " // Constants for atom codes.\n");
-
- std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
- build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
-
- // Print constants for the atom codes.
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- string constant = make_constant_name((*atomIt)->name);
- fprintf(out, "\n");
- fprintf(out, " /**\n");
- fprintf(out, " * %s %s<br>\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
- write_java_usage(out, "write", constant, **atomIt);
- auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
- if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
- write_java_usage(out, "write_non_chained", constant, **(non_chained_decl->second));
- }
- fprintf(out, " */\n");
- fprintf(out, " public static final int %s = %d;\n", constant.c_str(), (*atomIt)->code);
- }
- fprintf(out, "\n");
-}
-
-void write_java_enum_values(FILE* out, const Atoms& atoms) {
- fprintf(out, " // Constants for enum values.\n\n");
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
- field != (*atomIt)->fields.end(); field++) {
- if (field->javaType == JAVA_TYPE_ENUM) {
- fprintf(out, " // Values for %s.%s\n", (*atomIt)->message.c_str(),
- field->name.c_str());
- for (map<int, string>::const_iterator value = field->enumValues.begin();
- value != field->enumValues.end(); value++) {
- fprintf(out, " public static final int %s__%s__%s = %d;\n",
- make_constant_name((*atomIt)->message).c_str(),
- make_constant_name(field->name).c_str(),
- make_constant_name(value->second).c_str(), value->first);
- }
- fprintf(out, "\n");
- }
- }
- }
-}
-
-void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
- const AtomDecl& atom) {
- fprintf(out, " * Usage: StatsLog.%s(StatsLog.%s", method_name.c_str(),
- atom_code_name.c_str());
- for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
- field++) {
- if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- fprintf(out, ", android.os.WorkSource workSource");
- } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", android.util.SparseArray<Object> value_map");
- } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
- fprintf(out, ", byte[] %s", field->name.c_str());
- } else {
- fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
- }
- }
- fprintf(out, ");<br>\n");
-}
-
-int write_java_non_chained_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
- for (auto signatureInfoMapIt = signatureInfoMap.begin();
- signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
- // Print method signature.
- fprintf(out, " public static void write_non_chained(int code");
- vector<java_type_t> signature = signatureInfoMapIt->first;
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- fprintf(stderr, "Non chained signatures should not have attribution chains.\n");
- return 1;
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(stderr, "Module logging does not yet support key value pair.\n");
- return 1;
- } else {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
- }
- argIndex++;
- }
- fprintf(out, ") {\n");
-
- fprintf(out, " write(code");
- argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- // First two args are uid and tag of attribution chain.
- if (argIndex == 1) {
- fprintf(out, ", new int[] {arg%d}", argIndex);
- } else if (argIndex == 2) {
- fprintf(out, ", new java.lang.String[] {arg%d}", argIndex);
- } else {
- fprintf(out, ", arg%d", argIndex);
- }
- argIndex++;
- }
- fprintf(out, ");\n");
- fprintf(out, " }\n");
- fprintf(out, "\n");
- }
- return 0;
-}
-
-int write_java_work_source_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
- fprintf(out, " // WorkSource methods.\n");
- for (auto signatureInfoMapIt = signatureInfoMap.begin();
- signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
- vector<java_type_t> signature = signatureInfoMapIt->first;
- // Determine if there is Attribution in this signature.
- int attributionArg = -1;
- int argIndexMax = 0;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- argIndexMax++;
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- if (attributionArg > -1) {
- fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
- fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
- fprintf(out,
- "\n// Invalid for WorkSource: more than one attribution "
- "chain.\n");
- return 1;
- }
- attributionArg = argIndexMax;
- }
- }
- if (attributionArg < 0) {
- continue;
- }
-
- fprintf(out, "\n");
- // Method header (signature)
- fprintf(out, " public static void write(int code");
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- fprintf(out, ", android.os.WorkSource ws");
- } else {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
- }
- argIndex++;
- }
- fprintf(out, ") {\n");
-
- // write_non_chained() component. TODO: Remove when flat uids are no longer
- // needed.
- fprintf(out, " for (int i = 0; i < ws.size(); ++i) {\n");
- fprintf(out, " write_non_chained(code");
- for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
- if (argIndex == attributionArg) {
- fprintf(out, ", ws.getUid(i), ws.getPackageName(i)");
- } else {
- fprintf(out, ", arg%d", argIndex);
- }
- }
- fprintf(out, ");\n");
- fprintf(out, " }\n"); // close for-loop
-
- // write() component.
- fprintf(out,
- " java.util.List<android.os.WorkSource.WorkChain> workChains = "
- "ws.getWorkChains();\n");
- fprintf(out, " if (workChains != null) {\n");
- fprintf(out,
- " for (android.os.WorkSource.WorkChain wc : workChains) "
- "{\n");
- fprintf(out, " write(code");
- for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
- if (argIndex == attributionArg) {
- fprintf(out, ", wc.getUids(), wc.getTags()");
- } else {
- fprintf(out, ", arg%d", argIndex);
- }
- }
- fprintf(out, ");\n");
- fprintf(out, " }\n"); // close for-loop
- fprintf(out, " }\n"); // close if
- fprintf(out, " }\n"); // close method
- }
- return 0;
-}
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h
deleted file mode 100644
index 73e0cb8..0000000
--- a/tools/stats_log_api_gen/utils.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <string.h>
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "Collation.h"
-
-namespace android {
-namespace stats_log_api_gen {
-
-using namespace std;
-
-const string DEFAULT_CPP_NAMESPACE = "android,util";
-const string DEFAULT_CPP_HEADER_IMPORT = "statslog.h";
-
-const int JAVA_MODULE_REQUIRES_FLOAT = 0x01;
-const int JAVA_MODULE_REQUIRES_ATTRIBUTION = 0x02;
-const int JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS = 0x04;
-
-const map<AnnotationId, string> ANNOTATION_ID_CONSTANTS = {
- {ANNOTATION_ID_IS_UID, "ANNOTATION_ID_IS_UID"},
- {ANNOTATION_ID_TRUNCATE_TIMESTAMP, "ANNOTATION_ID_TRUNCATE_TIMESTAMP"},
- {ANNOTATION_ID_PRIMARY_FIELD, "ANNOTATION_ID_PRIMARY_FIELD"},
- {ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, "ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID"},
- {ANNOTATION_ID_EXCLUSIVE_STATE, "ANNOTATION_ID_EXCLUSIVE_STATE"},
- {ANNOTATION_ID_TRIGGER_STATE_RESET, "ANNOTATION_ID_TRIGGER_STATE_RESET"},
- {ANNOTATION_ID_STATE_NESTED, "ANNOTATION_ID_STATE_NESTED"}};
-
-string make_constant_name(const string& str);
-
-const char* cpp_type_name(java_type_t type);
-
-const char* java_type_name(java_type_t type);
-
-// Common Native helpers
-void write_namespace(FILE* out, const string& cppNamespaces);
-
-void write_closing_namespace(FILE* out, const string& cppNamespaces);
-
-void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl);
-
-void write_native_method_signature(FILE* out, const string& methodName,
- const vector<java_type_t>& signature,
- const AtomDecl& attributionDecl, const string& closer);
-
-void write_native_method_call(FILE* out, const string& methodName,
- const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
- int argIndex = 1);
-
-// Common Java helpers.
-void write_java_atom_codes(FILE* out, const Atoms& atoms);
-
-void write_java_enum_values(FILE* out, const Atoms& atoms);
-
-void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
- const AtomDecl& atom);
-
-int write_java_non_chained_methods(FILE* out, const SignatureInfoMap& signatureInfoMap);
-
-int write_java_work_source_methods(FILE* out, const SignatureInfoMap& signatureInfoMap);
-
-} // namespace stats_log_api_gen
-} // namespace android
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 8b89959..e3aafd6 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -31,9 +31,6 @@
"java/**/*.java",
"java/**/*.aidl",
],
- exclude_srcs: [
- ":framework-wifi-non-updatable-sources"
- ],
path: "java",
visibility: ["//visibility:private"],
}
@@ -47,20 +44,6 @@
}
filegroup {
- name: "framework-wifi-non-updatable-sources",
- srcs: [
- // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
- // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
- // to a separate package.
- "java/android/net/wifi/SoftApConfToXmlMigrationUtil.java",
- "java/android/net/wifi/WifiNetworkScoreCache.java",
- "java/android/net/wifi/WifiMigration.java",
- "java/android/net/wifi/nl80211/*.java",
- ":libwificond_ipc_aidl",
- ],
-}
-
-filegroup {
name: "framework-wifi-annotations",
srcs: ["java/android/net/wifi/WifiAnnotations.java"],
}
@@ -68,6 +51,7 @@
// list of tests that are allowed to access @hide APIs from framework-wifi
test_access_hidden_api_whitelist = [
"//frameworks/base/wifi/tests",
+ "//frameworks/base/wifi/non-updatable/tests",
"//frameworks/opt/net/wifi/tests/wifitests:__subpackages__",
"//external/robolectric-shadows:__subpackages__",
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index 75b5e72..7b84d57 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -21,6 +21,7 @@
rule android.net.ResolverParamsParcel* com.android.wifi.x.@0
rule android.net.RouteInfoParcel* com.android.wifi.x.@0
rule android.net.ScanResultInfoParcelable* com.android.wifi.x.@0
+rule android.net.TcpKeepalivePacketDataParcelable* com.android.wifi.x.@0
rule android.net.TetherConfigParcel* com.android.wifi.x.@0
rule android.net.TetherOffloadRuleParcel* com.android.wifi.x.@0
rule android.net.TetherStatsParcel* com.android.wifi.x.@0
@@ -43,7 +44,6 @@
rule android.net.InterfaceConfiguration* com.android.wifi.x.@0
rule android.net.IpMemoryStore* com.android.wifi.x.@0
rule android.net.NetworkMonitorManager* com.android.wifi.x.@0
-rule android.net.TcpKeepalivePacketData* com.android.wifi.x.@0
rule android.net.NetworkFactory* com.android.wifi.x.@0
rule android.net.ip.IpClientCallbacks* com.android.wifi.x.@0
rule android.net.ip.IpClientManager* com.android.wifi.x.@0
@@ -112,8 +112,6 @@
## used by both framework-wifi and service-wifi ##
rule android.content.pm.BaseParceledListSlice* com.android.wifi.x.@0
rule android.content.pm.ParceledListSlice* com.android.wifi.x.@0
-rule android.net.util.MacAddressUtils* com.android.wifi.x.@0
-rule android.net.util.nsd.DnsSdTxtRecord* com.android.wifi.x.@0
rule android.os.HandlerExecutor* com.android.wifi.x.@0
rule android.telephony.Annotation* com.android.wifi.x.@0
rule com.android.internal.util.AsyncChannel* com.android.wifi.x.@0
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 8c32d18..93c6358 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -29,7 +29,6 @@
import android.net.ProxyInfo;
import android.net.StaticIpConfiguration;
import android.net.Uri;
-import android.net.util.MacAddressUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,6 +40,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.MacAddressUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
index dad431c1..e2f40cf 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
@@ -17,10 +17,11 @@
package android.net.wifi.p2p.nsd;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.nsd.DnsSdTxtRecord;
import android.os.Build;
import android.text.TextUtils;
+import com.android.net.module.util.DnsSdTxtRecord;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
diff --git a/wifi/non-updatable/java/Android.bp b/wifi/non-updatable/java/Android.bp
new file mode 100644
index 0000000..b35b4be
--- /dev/null
+++ b/wifi/non-updatable/java/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This directory contains framework Wifi APIs that are not part of the Wifi module (i.e. not
+// updatable), and are generally only called by the Wifi module.
+
+// necessary since we only want the `path` property to only refer to these files
+filegroup {
+ name: "framework-wifi-non-updatable-sources-internal",
+ srcs: ["src/**/*.java"],
+ path: "src",
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "framework-wifi-non-updatable-sources",
+ srcs: [
+ // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
+ // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
+ // to a separate package.
+ ":framework-wifi-non-updatable-sources-internal",
+ ":libwificond_ipc_aidl",
+ ],
+}
diff --git a/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/non-updatable/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java
similarity index 100%
rename from wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java
rename to wifi/non-updatable/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java
diff --git a/wifi/java/android/net/wifi/WifiMigration.java b/wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java
similarity index 98%
rename from wifi/java/android/net/wifi/WifiMigration.java
rename to wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java
index 5792d27..4fabc0b 100755
--- a/wifi/java/android/net/wifi/WifiMigration.java
+++ b/wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java
@@ -139,8 +139,8 @@
/**
* Load data from legacy shared wifi config store file.
* <p>
- * Expected AOSP format is available in the sample files under {@code /frameworks/base/wifi/
- * java/android/net/wifi/migration_samples}.
+ * Expected AOSP format is available in the sample files under {@code
+ * frameworks/base/wifi/non-updatable/migration_samples/}.
* </p>
* <p>
* Note:
@@ -218,8 +218,8 @@
/**
* Load data from legacy user wifi config store file.
* <p>
- * Expected AOSP format is available in the sample files under {@code /frameworks/base/wifi/
- * java/android/net/wifi/migration_samples}.
+ * Expected AOSP format is available in the sample files under {@code
+ * frameworks/base/wifi/non-updatable/migration_samples/}.
* </p>
* <p>
* Note:
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java
similarity index 98%
rename from wifi/java/android/net/wifi/WifiNetworkScoreCache.java
rename to wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java
index 378549d..3903658 100755
--- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
+++ b/wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java
@@ -89,7 +89,7 @@
@Override public final void updateScores(List<ScoredNetwork> networks) {
if (networks == null || networks.isEmpty()) {
- return;
+ return;
}
if (DBG) {
Log.d(TAG, "updateScores list size=" + networks.size());
@@ -97,7 +97,7 @@
boolean changed = false;
- synchronized(mLock) {
+ synchronized (mLock) {
for (ScoredNetwork network : networks) {
String networkKey = buildNetworkKey(network);
if (networkKey == null) {
@@ -189,7 +189,7 @@
String key = buildNetworkKey(result);
if (key == null) return null;
- synchronized(mLock) {
+ synchronized (mLock) {
ScoredNetwork network = mCache.get(key);
return network;
}
diff --git a/wifi/java/android/net/wifi/nl80211/ChannelSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/ChannelSettings.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/ChannelSettings.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/ChannelSettings.java
diff --git a/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
diff --git a/wifi/java/android/net/wifi/nl80211/HiddenNetwork.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/HiddenNetwork.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/HiddenNetwork.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/HiddenNetwork.java
diff --git a/wifi/java/android/net/wifi/nl80211/NativeScanResult.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeScanResult.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/NativeScanResult.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeScanResult.java
diff --git a/wifi/java/android/net/wifi/nl80211/NativeWifiClient.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeWifiClient.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/NativeWifiClient.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeWifiClient.java
diff --git a/wifi/java/android/net/wifi/nl80211/PnoNetwork.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoNetwork.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/PnoNetwork.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoNetwork.java
diff --git a/wifi/java/android/net/wifi/nl80211/PnoSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoSettings.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/PnoSettings.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoSettings.java
diff --git a/wifi/java/android/net/wifi/nl80211/RadioChainInfo.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/RadioChainInfo.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/RadioChainInfo.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/RadioChainInfo.java
diff --git a/wifi/java/android/net/wifi/nl80211/SingleScanSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/SingleScanSettings.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/SingleScanSettings.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/SingleScanSettings.java
diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
diff --git a/wifi/java/android/net/wifi/migration_samples/README.txt b/wifi/non-updatable/migration_samples/README.txt
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/README.txt
rename to wifi/non-updatable/migration_samples/README.txt
diff --git a/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml b/wifi/non-updatable/migration_samples/Shared_WifiConfigStore.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml
rename to wifi/non-updatable/migration_samples/Shared_WifiConfigStore.xml
diff --git a/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml b/wifi/non-updatable/migration_samples/Shared_WifiConfigStoreSoftAp.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml
rename to wifi/non-updatable/migration_samples/Shared_WifiConfigStoreSoftAp.xml
diff --git a/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml b/wifi/non-updatable/migration_samples/User_WifiConfigStore.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml
rename to wifi/non-updatable/migration_samples/User_WifiConfigStore.xml
diff --git a/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml b/wifi/non-updatable/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml
rename to wifi/non-updatable/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml
diff --git a/wifi/non-updatable/tests/Android.bp b/wifi/non-updatable/tests/Android.bp
new file mode 100644
index 0000000..3f5cacf
--- /dev/null
+++ b/wifi/non-updatable/tests/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "FrameworksWifiNonUpdatableApiTests",
+
+ defaults: ["framework-wifi-test-defaults"],
+
+ srcs: ["src/**/*.java"],
+
+ jacoco: {
+ include_filter: ["android.net.wifi.*"],
+ // TODO(b/147521214) need to exclude test classes
+ exclude_filter: [],
+ },
+
+ static_libs: [
+ "androidx.test.rules",
+ "frameworks-base-testutils",
+ "guava",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/wifi/non-updatable/tests/AndroidManifest.xml b/wifi/non-updatable/tests/AndroidManifest.xml
new file mode 100644
index 0000000..b4b6b2d
--- /dev/null
+++ b/wifi/non-updatable/tests/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.net.wifi.test">
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:label="WifiTestDummyLabel"
+ android:name="WifiTestDummyName"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.net.wifi.test"
+ android:label="Frameworks Wifi Non-updatable API Tests">
+ </instrumentation>
+
+</manifest>
diff --git a/wifi/non-updatable/tests/AndroidTest.xml b/wifi/non-updatable/tests/AndroidTest.xml
new file mode 100644
index 0000000..5f3fdd4
--- /dev/null
+++ b/wifi/non-updatable/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks Wifi Non-updatable API Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="FrameworksWifiNonUpdatableApiTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="FrameworksWifiNonUpdatableApiTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.net.wifi.test" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/wifi/non-updatable/tests/README.md b/wifi/non-updatable/tests/README.md
new file mode 100644
index 0000000..ad535f4
--- /dev/null
+++ b/wifi/non-updatable/tests/README.md
@@ -0,0 +1,32 @@
+# Wifi Non-Updatable Framework Unit Tests
+This package contains unit tests for the non-updatable part (i.e. outside the Wifi module) of the
+Android Wifi framework APIs based on the
+[Android Testing Support Library](http://developer.android.com/tools/testing-support-library/index.html).
+The test cases are built using the [JUnit](http://junit.org/) and [Mockito](http://mockito.org/)
+libraries.
+
+## Running Tests
+The easiest way to run tests is simply run
+
+```
+atest android.net.wifi
+```
+
+To pick up changes in framework/base, you will need to:
+1. rebuild the framework library 'make -j32'
+2. sync over the updated library to the device 'adb sync'
+3. restart framework on the device 'adb shell stop' then 'adb shell start'
+
+To enable syncing data to the device for first time after clean reflash:
+1. adb disable-verity
+2. adb reboot
+3. adb remount
+
+## Adding Tests
+Tests can be added by adding classes to the src directory. JUnit4 style test cases can
+be written by simply annotating test methods with `org.junit.Test`.
+
+## Debugging Tests
+If you are trying to debug why tests are not doing what you expected, you can add android log
+statements and use logcat to view them. The beginning and end of every tests is automatically logged
+with the tag `TestRunner`.
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java b/wifi/non-updatable/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
similarity index 99%
rename from wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index fdd11a3..c4967eb 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.net.wifi;
diff --git a/wifi/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp
index b710a14..0e58740 100644
--- a/wifi/tests/Android.bp
+++ b/wifi/tests/Android.bp
@@ -36,6 +36,7 @@
"mockito-target-minus-junit4",
"modules-utils-build",
"net-tests-utils",
+ "net-utils-framework-common",
"truth-prebuilt",
],
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index a7b6765..890d3d3 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -32,13 +32,14 @@
import static org.junit.Assert.assertTrue;
import android.net.MacAddress;
-import android.net.util.MacAddressUtils;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
+import com.android.net.module.util.MacAddressUtils;
+
import org.junit.Before;
import org.junit.Test;