Merge changes from topic "decorate_custom_notifs"
* changes:
Improve RemoteViews to simplify notification view hierarchy.
Fully custom view notifications now receive minimal decoration when targeting S.
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 3b4823e..a7396fa 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -85,6 +85,8 @@
static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time";
static const char CLOCK_FONT_ASSET[] = "images/clock_font.png";
static const char CLOCK_FONT_ZIP_NAME[] = "clock_font.png";
+static const char PROGRESS_FONT_ASSET[] = "images/progress_font.png";
+static const char PROGRESS_FONT_ZIP_NAME[] = "progress_font.png";
static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change";
static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
@@ -100,6 +102,7 @@
static const int TEXT_CENTER_VALUE = INT_MAX;
static const int TEXT_MISSING_VALUE = INT_MIN;
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
+static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress";
static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
static constexpr size_t TEXT_POS_LEN_MAX = 16;
@@ -914,6 +917,18 @@
drawText(out, font, false, &x, &y);
}
+void BootAnimation::drawProgress(int percent, const Font& font, const int xPos, const int yPos) {
+ static constexpr int PERCENT_LENGTH = 5;
+
+ char percentBuff[PERCENT_LENGTH];
+ // ';' has the ascii code just after ':', and the font resource contains '%'
+ // for that ascii code.
+ sprintf(percentBuff, "%d;", percent);
+ int x = xPos;
+ int y = yPos;
+ drawText(percentBuff, font, false, &x, &y);
+}
+
bool BootAnimation::parseAnimationDesc(Animation& animation) {
String8 desString;
@@ -933,6 +948,7 @@
int height = 0;
int count = 0;
int pause = 0;
+ int progress = 0;
int framesToFadeCount = 0;
char path[ANIM_ENTRY_NAME_MAX];
char color[7] = "000000"; // default to black if unspecified
@@ -942,11 +958,17 @@
int nextReadPos;
- if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
- // SLOGD("> w=%d, h=%d, fps=%d", width, height, fps);
+ int topLineNumbers = sscanf(l, "%d %d %d %d", &width, &height, &fps, &progress);
+ if (topLineNumbers == 3 || topLineNumbers == 4) {
+ // SLOGD("> w=%d, h=%d, fps=%d, progress=%d", width, height, fps, progress);
animation.width = width;
animation.height = height;
animation.fps = fps;
+ if (topLineNumbers == 4) {
+ animation.progressEnabled = (progress != 0);
+ } else {
+ animation.progressEnabled = false;
+ }
} else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
&pathType, &count, &pause, path, &nextReadPos) >= 4) {
if (pathType == 'f') {
@@ -1023,6 +1045,14 @@
continue;
}
+ if (entryName == PROGRESS_FONT_ZIP_NAME) {
+ FileMap* map = zip->createEntryFileMap(entry);
+ if (map) {
+ animation.progressFont.map = map;
+ }
+ continue;
+ }
+
for (size_t j = 0; j < pcount; j++) {
if (path == animation.parts[j].path) {
uint16_t method;
@@ -1154,6 +1184,8 @@
mClockEnabled = clockFontInitialized;
}
+ initFont(&mAnimation->progressFont, PROGRESS_FONT_ASSET);
+
if (mClockEnabled && !updateIsTimeAccurate()) {
mTimeCheckThread = new TimeCheckThread(this);
mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
@@ -1189,6 +1221,7 @@
elapsedRealtime());
int fadedFramesCount = 0;
+ int lastDisplayedProgress = 0;
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
@@ -1214,6 +1247,12 @@
part.backgroundColor[2],
1.0f);
+ // For the last animation, if we have progress indicator from
+ // the system, display it.
+ int currentProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
+ bool displayProgress = animation.progressEnabled &&
+ (i == (pcount -1)) && currentProgress != 0;
+
for (size_t j=0 ; j<fcount ; j++) {
if (shouldStopPlayingPart(part, fadedFramesCount)) break;
@@ -1271,6 +1310,23 @@
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
}
+ if (displayProgress) {
+ int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
+ // In case the new progress jumped suddenly, still show an
+ // increment of 1.
+ if (lastDisplayedProgress != 100) {
+ // Artificially sleep 1/10th a second to slow down the animation.
+ usleep(100000);
+ if (lastDisplayedProgress < newProgress) {
+ lastDisplayedProgress++;
+ }
+ }
+ // Put the progress percentage right below the animation.
+ int posY = animation.height / 3;
+ int posX = TEXT_CENTER_VALUE;
+ drawProgress(lastDisplayedProgress, animation.progressFont, posX, posY);
+ }
+
handleViewport(frameDuration);
eglSwapBuffers(mDisplay, mSurface);
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 4699cfe..07432a2 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -98,11 +98,13 @@
int fps;
int width;
int height;
+ bool progressEnabled;
Vector<Part> parts;
String8 audioConf;
String8 fileName;
ZipFileRO* zip;
Font clockFont;
+ Font progressFont;
};
// All callbacks will be called from this class's internal thread.
@@ -168,6 +170,7 @@
bool movie();
void drawText(const char* str, const Font& font, bool bold, int* x, int* y);
void drawClock(const Font& font, const int xPos, const int yPos);
+ void drawProgress(int percent, const Font& font, const int xPos, const int yPos);
void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight,
const Animation::Part& part, int fadedFramesCount);
bool validClock(const Animation::Part& part);
diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md
index f9b83c9..1678053 100644
--- a/cmds/bootanimation/FORMAT.md
+++ b/cmds/bootanimation/FORMAT.md
@@ -22,11 +22,14 @@
The first line defines the general parameters of the animation:
- WIDTH HEIGHT FPS
+ WIDTH HEIGHT FPS [PROGRESS]
* **WIDTH:** animation width (pixels)
* **HEIGHT:** animation height (pixels)
* **FPS:** frames per second, e.g. 60
+ * **PROGRESS:** whether to show a progress percentage on the last part
+ + The percentage will be displayed with an x-coordinate of 'c', and a
+ y-coordinate set to 1/3 of the animation height.
It is followed by a number of rows of the form:
@@ -77,6 +80,11 @@
* Each row is divided in half: regular weight glyphs on the top half, bold glyphs on the bottom
* For a NxM image each character glyph will be N/16 pixels wide and M/(12*2) pixels high
+## progress_font.png
+
+The file used to draw the boot progress in percentage on top of the boot animation. The font format
+follows the same specification as the one described for clock_font.png.
+
## loading and playing frames
Each part is scanned and loaded directly from the zip archive. Within a part directory, every file
diff --git a/cmds/idmap2/idmap2/CommandUtils.cpp b/cmds/idmap2/idmap2/CommandUtils.cpp
index 8f5845b..09867f3 100644
--- a/cmds/idmap2/idmap2/CommandUtils.cpp
+++ b/cmds/idmap2/idmap2/CommandUtils.cpp
@@ -29,8 +29,8 @@
using android::idmap2::Unit;
Result<Unit> Verify(const std::string& idmap_path, const std::string& target_path,
- const std::string& overlay_path, PolicyBitmask fulfilled_policies,
- bool enforce_overlayable) {
+ const std::string& overlay_path, const std::string& overlay_name,
+ PolicyBitmask fulfilled_policies, bool enforce_overlayable) {
SYSTRACE << "Verify " << idmap_path;
std::ifstream fin(idmap_path);
const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
@@ -39,7 +39,7 @@
return Error("failed to parse idmap header");
}
- const auto header_ok = header->IsUpToDate(target_path.c_str(), overlay_path.c_str(),
+ const auto header_ok = header->IsUpToDate(target_path, overlay_path, overlay_name,
fulfilled_policies, enforce_overlayable);
if (!header_ok) {
return Error(header_ok.GetError(), "idmap not up to date");
diff --git a/cmds/idmap2/idmap2/CommandUtils.h b/cmds/idmap2/idmap2/CommandUtils.h
index e717e04..e068967 100644
--- a/cmds/idmap2/idmap2/CommandUtils.h
+++ b/cmds/idmap2/idmap2/CommandUtils.h
@@ -20,10 +20,8 @@
#include "idmap2/PolicyUtils.h"
#include "idmap2/Result.h"
-android::idmap2::Result<android::idmap2::Unit> Verify(const std::string& idmap_path,
- const std::string& target_path,
- const std::string& overlay_path,
- PolicyBitmask fulfilled_policies,
- bool enforce_overlayable);
+android::idmap2::Result<android::idmap2::Unit> Verify(
+ const std::string& idmap_path, const std::string& target_path, const std::string& overlay_path,
+ const std::string& overlay_name, PolicyBitmask fulfilled_policies, bool enforce_overlayable);
#endif // IDMAP2_IDMAP2_COMMAND_UTILS_H_
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index 648b78e..c93c717 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -50,6 +50,7 @@
std::string target_apk_path;
std::string overlay_apk_path;
std::string idmap_path;
+ std::string overlay_name;
std::vector<std::string> policies;
bool ignore_overlayable = false;
@@ -62,9 +63,11 @@
"input: path to apk which contains the new resource values",
&overlay_apk_path)
.MandatoryOption("--idmap-path", "output: path to where to write idmap file", &idmap_path)
+ .OptionalOption("--overlay-name", "input: the value of android:name of the overlay",
+ &overlay_name)
.OptionalOption("--policy",
"input: an overlayable policy this overlay fulfills "
- "(if none or supplied, the overlay policy will default to \"public\")",
+ "(if none are supplied, the overlay policy will default to \"public\")",
&policies)
.OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks",
&ignore_overlayable);
@@ -100,8 +103,8 @@
return Error("failed to load apk %s", overlay_apk_path.c_str());
}
- const auto idmap =
- Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, overlay_name,
+ fulfilled_policies, !ignore_overlayable);
if (!idmap) {
return Error(idmap.GetError(), "failed to create idmap");
}
diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp
index 19622c4..5db391c 100644
--- a/cmds/idmap2/idmap2/CreateMultiple.cpp
+++ b/cmds/idmap2/idmap2/CreateMultiple.cpp
@@ -105,7 +105,8 @@
continue;
}
- if (!Verify(idmap_path, target_apk_path, overlay_apk_path, fulfilled_policies,
+ // TODO(b/175014391): Support multiple overlay tags in OverlayConfig
+ if (!Verify(idmap_path, target_apk_path, overlay_apk_path, "", fulfilled_policies,
!ignore_overlayable)) {
const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
if (!overlay_apk) {
@@ -113,8 +114,8 @@
continue;
}
- const auto idmap =
- Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", fulfilled_policies,
+ !ignore_overlayable);
if (!idmap) {
LOG(WARNING) << "failed to create idmap";
continue;
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 437180d..43a1951 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -188,29 +188,27 @@
}
if (i == 0) {
- target_path = idmap_header->GetTargetPath().to_string();
+ target_path = idmap_header->GetTargetPath();
auto target_apk = ApkAssets::Load(target_path);
if (!target_apk) {
return Error("failed to read target apk from %s", target_path.c_str());
}
apk_assets.push_back(std::move(target_apk));
- auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath().to_string(),
- true /* assert_overlay */);
+ auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath(),
+ idmap_header->GetOverlayName());
if (!manifest_info) {
return manifest_info.GetError();
}
- target_package_name = (*manifest_info).target_package;
+ target_package_name = manifest_info->target_package;
} else if (target_path != idmap_header->GetTargetPath()) {
return Error("different target APKs (expected target APK %s but %s has target APK %s)",
- target_path.c_str(), idmap_path.c_str(),
- idmap_header->GetTargetPath().to_string().c_str());
+ target_path.c_str(), idmap_path.c_str(), idmap_header->GetTargetPath().c_str());
}
auto overlay_apk = ApkAssets::LoadOverlay(idmap_path);
if (!overlay_apk) {
- return Error("failed to read overlay apk from %s",
- idmap_header->GetOverlayPath().to_string().c_str());
+ return Error("failed to read overlay apk from %s", idmap_header->GetOverlayPath().c_str());
}
apk_assets.push_back(std::move(overlay_apk));
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 15e22a3..93537d3 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -155,8 +155,9 @@
return overlay_crc_status;
}
+ // TODO(162841629): Support passing overlay name to idmap2d verify
auto up_to_date =
- header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), target_crc, overlay_crc,
+ header->IsUpToDate(target_apk_path, overlay_apk_path, "", target_crc, overlay_crc,
ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable);
*_aidl_return = static_cast<bool>(up_to_date);
@@ -190,8 +191,9 @@
return error("failed to load apk " + overlay_apk_path);
}
+ // TODO(162841629): Support passing overlay name to idmap2d create
const auto idmap =
- Idmap::FromApkAssets(*target_apk, *overlay_apk, policy_bitmask, enforce_overlayable);
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, "", policy_bitmask, enforce_overlayable);
if (!idmap) {
return error(idmap.GetErrorMessage());
}
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
index bf31cbf..5e189f2 100644
--- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -39,7 +39,6 @@
void Write8(uint8_t value);
void Write16(uint16_t value);
void Write32(uint32_t value);
- void WriteString256(const StringPiece& value);
void WriteString(const StringPiece& value);
std::ostream& stream_;
};
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index a35fad9..1b815c1 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -19,7 +19,8 @@
*
* idmap := header data*
* header := magic version target_crc overlay_crc fulfilled_policies
- * enforce_overlayable target_path overlay_path debug_info
+ * enforce_overlayable target_path overlay_path overlay_name
+ * debug_info
* data := data_header target_entry* target_inline_entry* overlay_entry*
* string_pool
* data_header := target_package_id overlay_package_id padding(2) target_entry_count
@@ -37,13 +38,13 @@
* overlay_entry_count := <uint32_t>
* overlay_id := <uint32_t>
* overlay_package_id := <uint8_t>
- * overlay_path := string256
+ * overlay_name := string
+ * overlay_path := string
* padding(n) := <uint8_t>[n]
* Res_value::size := <uint16_t>
* Res_value::type := <uint8_t>
* Res_value::value := <uint32_t>
* string := <uint32_t> <uint8_t>+ padding(n)
- * string256 := <uint8_t>[256]
* string_pool := string
* string_pool_index := <uint32_t>
* string_pool_length := <uint32_t>
@@ -52,7 +53,7 @@
* target_inline_entry_count := <uint32_t>
* target_id := <uint32_t>
* target_package_id := <uint8_t>
- * target_path := string256
+ * target_path := string
* value_type := <uint8_t>
* value_data := <uint32_t>
* version := <uint32_t>
@@ -78,19 +79,12 @@
class Idmap;
class Visitor;
-static constexpr const ResourceId kPadding = 0xffffffffu;
-static constexpr const EntryId kNoEntry = 0xffffu;
-
// magic number: all idmap files start with this
static constexpr const uint32_t kIdmapMagic = android::kIdmapMagic;
// current version of the idmap binary format; must be incremented when the format is changed
static constexpr const uint32_t kIdmapCurrentVersion = android::kIdmapCurrentVersion;
-// strings in the idmap are encoded char arrays of length 'kIdmapStringLength' (including mandatory
-// terminating null)
-static constexpr const size_t kIdmapStringLength = 256;
-
// Retrieves a crc generated using all of the files within the zip that can affect idmap generation.
Result<uint32_t> GetPackageCrc(const ZipFile& zip_info);
@@ -122,32 +116,38 @@
return enforce_overlayable_;
}
- inline StringPiece GetTargetPath() const {
- return StringPiece(target_path_);
+ const std::string& GetTargetPath() const {
+ return target_path_;
}
- inline StringPiece GetOverlayPath() const {
- return StringPiece(overlay_path_);
+ const std::string& GetOverlayPath() const {
+ return overlay_path_;
}
- inline const std::string& GetDebugInfo() const {
+ const std::string& GetOverlayName() const {
+ return overlay_name_;
+ }
+
+ const std::string& GetDebugInfo() const {
return debug_info_;
}
// Invariant: anytime the idmap data encoding is changed, the idmap version
// field *must* be incremented. Because of this, we know that if the idmap
// header is up-to-date the entire file is up-to-date.
- Result<Unit> IsUpToDate(const char* target_path, const char* overlay_path,
- PolicyBitmask fulfilled_policies, bool enforce_overlayable) const;
- Result<Unit> IsUpToDate(const char* target_path, const char* overlay_path, uint32_t target_crc,
+ Result<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path,
+ const std::string& overlay_name, PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) const;
+
+ Result<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path,
+ const std::string& overlay_name, uint32_t target_crc,
uint32_t overlay_crc, PolicyBitmask fulfilled_policies,
bool enforce_overlayable) const;
void accept(Visitor* v) const;
private:
- IdmapHeader() {
- }
+ IdmapHeader() = default;
uint32_t magic_;
uint32_t version_;
@@ -155,8 +155,9 @@
uint32_t overlay_crc_;
uint32_t fulfilled_policies_;
bool enforce_overlayable_;
- char target_path_[kIdmapStringLength];
- char overlay_path_[kIdmapStringLength];
+ std::string target_path_;
+ std::string overlay_path_;
+ std::string overlay_name_;
std::string debug_info_;
friend Idmap;
@@ -251,8 +252,7 @@
void accept(Visitor* v) const;
private:
- IdmapData() {
- }
+ IdmapData() = default;
std::unique_ptr<const Header> header_;
std::vector<TargetEntry> target_entries_;
@@ -277,22 +277,22 @@
// the target and overlay package names
static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
+ const std::string& overlay_name,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable);
- inline const std::unique_ptr<const IdmapHeader>& GetHeader() const {
+ const std::unique_ptr<const IdmapHeader>& GetHeader() const {
return header_;
}
- inline const std::vector<std::unique_ptr<const IdmapData>>& GetData() const {
+ const std::vector<std::unique_ptr<const IdmapData>>& GetData() const {
return data_;
}
void accept(Visitor* v) const;
private:
- Idmap() {
- }
+ Idmap() = default;
std::unique_ptr<const IdmapHeader> header_;
std::vector<std::unique_ptr<const IdmapData>> data_;
@@ -302,8 +302,7 @@
class Visitor {
public:
- virtual ~Visitor() {
- }
+ virtual ~Visitor() = default;
virtual void visit(const Idmap& idmap) = 0;
virtual void visit(const IdmapHeader& header) = 0;
virtual void visit(const IdmapData& data) = 0;
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
index 58edc99..4583516 100644
--- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -44,7 +44,9 @@
void print(uint8_t value, const char* fmt, ...);
void print(uint16_t value, const char* fmt, ...);
void print(uint32_t value, const char* fmt, ...);
- void print(const std::string& value, size_t encoded_size, const char* fmt, ...);
+ void print(const std::string& value, bool print_value, const char* fmt, ...);
+ void align();
+ void pad(size_t padding);
std::ostream& stream_;
std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index c643b0e..cd14d3e 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -44,17 +44,14 @@
StringPiece DataTypeToString(uint8_t data_type);
struct OverlayManifestInfo {
- std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string requiredSystemPropertyName; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string requiredSystemPropertyValue; // NOLINT(misc-non-private-member-variables-in-classes)
- uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
- bool is_static; // NOLINT(misc-non-private-member-variables-in-classes)
- int priority = -1; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string name; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
+ uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
};
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
- bool assert_overlay = true);
+ const std::string& name);
Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h
index 972a6d7..1c74ab3 100644
--- a/cmds/idmap2/include/idmap2/XmlParser.h
+++ b/cmds/idmap2/include/idmap2/XmlParser.h
@@ -22,6 +22,7 @@
#include <memory>
#include <string>
+#include "ResourceUtils.h"
#include "Result.h"
#include "android-base/macros.h"
#include "androidfw/ResourceTypes.h"
@@ -39,8 +40,11 @@
Event event() const;
std::string name() const;
- Result<std::string> GetAttributeStringValue(const std::string& name) const;
Result<Res_value> GetAttributeValue(const std::string& name) const;
+ Result<Res_value> GetAttributeValue(ResourceId attr, const std::string& label) const;
+
+ Result<std::string> GetAttributeStringValue(const std::string& name) const;
+ Result<std::string> GetAttributeStringValue(ResourceId attr, const std::string& label) const;
bool operator==(const Node& rhs) const;
bool operator!=(const Node& rhs) const;
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index 726f6c5..c163107 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -38,13 +38,6 @@
stream_.write(reinterpret_cast<char*>(&x), sizeof(uint32_t));
}
-void BinaryStreamVisitor::WriteString256(const StringPiece& value) {
- char buf[kIdmapStringLength];
- memset(buf, 0, sizeof(buf));
- memcpy(buf, value.data(), std::min(value.size(), sizeof(buf)));
- stream_.write(buf, sizeof(buf));
-}
-
void BinaryStreamVisitor::WriteString(const StringPiece& value) {
// pad with null to nearest word boundary;
size_t padding_size = CalculatePadding(value.size());
@@ -64,8 +57,9 @@
Write32(header.GetOverlayCrc());
Write32(header.GetFulfilledPolicies());
Write32(static_cast<uint8_t>(header.GetEnforceOverlayable()));
- WriteString256(header.GetTargetPath());
- WriteString256(header.GetOverlayPath());
+ WriteString(header.GetTargetPath());
+ WriteString(header.GetOverlayPath());
+ WriteString(header.GetOverlayName());
WriteString(header.GetDebugInfo());
}
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 1129413..5af84b0 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -69,38 +69,26 @@
return false;
}
-// a string is encoded as a kIdmapStringLength char array; the array is always null-terminated
-bool WARN_UNUSED ReadString256(std::istream& stream, char out[kIdmapStringLength]) {
- char buf[kIdmapStringLength];
- memset(buf, 0, sizeof(buf));
- if (!stream.read(buf, sizeof(buf))) {
- return false;
- }
- if (buf[sizeof(buf) - 1] != '\0') {
- return false;
- }
- memcpy(out, buf, sizeof(buf));
- return true;
-}
-
-Result<std::string> ReadString(std::istream& stream) {
+bool WARN_UNUSED ReadString(std::istream& stream, std::string* out) {
uint32_t size;
if (!Read32(stream, &size)) {
- return Error("failed to read string size");
+ return false;
}
if (size == 0) {
- return std::string("");
+ *out = "";
+ return true;
}
std::string buf(size, '\0');
if (!stream.read(buf.data(), size)) {
- return Error("failed to read string of size %u", size);
+ return false;
}
uint32_t padding_size = CalculatePadding(size);
std::string padding(padding_size, '\0');
if (!stream.read(padding.data(), padding_size)) {
- return Error("failed to read string padding of size %u", padding_size);
+ return false;
}
- return buf;
+ *out = buf;
+ return true;
}
} // namespace
@@ -119,28 +107,25 @@
if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) ||
!Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) ||
!Read32(stream, &idmap_header->fulfilled_policies_) ||
- !Read32(stream, &enforce_overlayable) || !ReadString256(stream, idmap_header->target_path_) ||
- !ReadString256(stream, idmap_header->overlay_path_)) {
+ !Read32(stream, &enforce_overlayable) || !ReadString(stream, &idmap_header->target_path_) ||
+ !ReadString(stream, &idmap_header->overlay_path_) ||
+ !ReadString(stream, &idmap_header->overlay_name_) ||
+ !ReadString(stream, &idmap_header->debug_info_)) {
return nullptr;
}
idmap_header->enforce_overlayable_ = enforce_overlayable != 0U;
-
- auto debug_str = ReadString(stream);
- if (!debug_str) {
- return nullptr;
- }
- idmap_header->debug_info_ = std::move(*debug_str);
-
return std::move(idmap_header);
}
-Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overlay_path,
+Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path,
+ const std::string& overlay_path,
+ const std::string& overlay_name,
PolicyBitmask fulfilled_policies,
bool enforce_overlayable) const {
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path);
if (!target_zip) {
- return Error("failed to open target %s", target_path);
+ return Error("failed to open target %s", target_path.c_str());
}
const Result<uint32_t> target_crc = GetPackageCrc(*target_zip);
@@ -150,7 +135,7 @@
const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path);
if (!overlay_zip) {
- return Error("failed to overlay target %s", overlay_path);
+ return Error("failed to overlay target %s", overlay_path.c_str());
}
const Result<uint32_t> overlay_crc = GetPackageCrc(*overlay_zip);
@@ -158,13 +143,14 @@
return Error("failed to get overlay crc");
}
- return IsUpToDate(target_path, overlay_path, *target_crc, *overlay_crc, fulfilled_policies,
- enforce_overlayable);
+ return IsUpToDate(target_path, overlay_path, overlay_name, *target_crc, *overlay_crc,
+ fulfilled_policies, enforce_overlayable);
}
-Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overlay_path,
- uint32_t target_crc, uint32_t overlay_crc,
- PolicyBitmask fulfilled_policies,
+Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path,
+ const std::string& overlay_path,
+ const std::string& overlay_name, uint32_t target_crc,
+ uint32_t overlay_crc, PolicyBitmask fulfilled_policies,
bool enforce_overlayable) const {
if (magic_ != kIdmapMagic) {
return Error("bad magic: actual 0x%08x, expected 0x%08x", magic_, kIdmapMagic);
@@ -194,14 +180,19 @@
enforce_overlayable ? "true" : "false", enforce_overlayable_ ? "true" : "false");
}
- if (strcmp(target_path, target_path_) != 0) {
- return Error("bad target path: idmap version %s, file system version %s", target_path,
- target_path_);
+ if (target_path != target_path_) {
+ return Error("bad target path: idmap version %s, file system version %s", target_path.c_str(),
+ target_path_.c_str());
}
- if (strcmp(overlay_path, overlay_path_) != 0) {
- return Error("bad overlay path: idmap version %s, file system version %s", overlay_path,
- overlay_path_);
+ if (overlay_path != overlay_path_) {
+ return Error("bad overlay path: idmap version %s, file system version %s", overlay_path.c_str(),
+ overlay_path_.c_str());
+ }
+
+ if (overlay_name != overlay_name_) {
+ return Error("bad overlay name: idmap version %s, file system version %s", overlay_name.c_str(),
+ overlay_name_.c_str());
}
return Unit{};
@@ -262,12 +253,9 @@
}
// Read raw string pool bytes.
- auto string_pool_data = ReadString(stream);
- if (!string_pool_data) {
+ if (!ReadString(stream, &data->string_pool_data_)) {
return nullptr;
}
- data->string_pool_data_ = std::move(*string_pool_data);
-
return std::move(data);
}
@@ -337,6 +325,7 @@
Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
+ const std::string& overlay_name,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
SYSTRACE << "Idmap::FromApkAssets";
@@ -368,32 +357,20 @@
return Error(crc.GetError(), "failed to get zip CRC for overlay");
}
header->overlay_crc_ = *crc;
-
header->fulfilled_policies_ = fulfilled_policies;
header->enforce_overlayable_ = enforce_overlayable;
+ header->target_path_ = target_apk_path;
+ header->overlay_path_ = overlay_apk_path;
+ header->overlay_name_ = overlay_name;
- if (target_apk_path.size() > sizeof(header->target_path_)) {
- return Error("target apk path \"%s\" longer than maximum size %zu", target_apk_path.c_str(),
- sizeof(header->target_path_));
- }
- memset(header->target_path_, 0, sizeof(header->target_path_));
- memcpy(header->target_path_, target_apk_path.data(), target_apk_path.size());
-
- if (overlay_apk_path.size() > sizeof(header->overlay_path_)) {
- return Error("overlay apk path \"%s\" longer than maximum size %zu", overlay_apk_path.c_str(),
- sizeof(header->target_path_));
- }
- memset(header->overlay_path_, 0, sizeof(header->overlay_path_));
- memcpy(header->overlay_path_, overlay_apk_path.data(), overlay_apk_path.size());
-
- auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
- if (!overlay_info) {
- return overlay_info.GetError();
+ auto info = utils::ExtractOverlayManifestInfo(overlay_apk_path, overlay_name);
+ if (!info) {
+ return info.GetError();
}
LogInfo log_info;
auto resource_mapping =
- ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *overlay_info,
+ ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *info,
fulfilled_policies, enforce_overlayable, log_info);
if (!resource_mapping) {
return resource_mapping.GetError();
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index 3037a79..7e090a9 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -39,6 +39,10 @@
<< TAB "target apk path : " << header.GetTargetPath() << std::endl
<< TAB "overlay apk path : " << header.GetOverlayPath() << std::endl;
+ if (!header.GetOverlayName().empty()) {
+ stream_ << "Overlay name: " << header.GetOverlayName() << std::endl;
+ }
+
const std::string& debug = header.GetDebugInfo();
if (!debug.empty()) {
std::istringstream debug_stream(debug);
@@ -49,12 +53,12 @@
}
}
- if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string())) {
+ if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath())) {
target_am_.SetApkAssets({target_apk_.get()});
apk_assets_.push_back(std::move(target_apk_));
}
- if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath().to_string())) {
+ if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath())) {
overlay_am_.SetApkAssets({overlay_apk.get()});
apk_assets_.push_back(std::move(overlay_apk));
}
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 82f5d26..b517aa3 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -43,20 +43,18 @@
print(header.GetFulfilledPolicies(), "fulfilled policies: %s",
PoliciesToDebugString(header.GetFulfilledPolicies()).c_str());
print(static_cast<uint32_t>(header.GetEnforceOverlayable()), "enforce overlayable");
- print(header.GetTargetPath().to_string(), kIdmapStringLength, "target path");
- print(header.GetOverlayPath().to_string(), kIdmapStringLength, "overlay path");
+ print(header.GetTargetPath(), true /* print_value */, "target path");
+ print(header.GetOverlayPath(), true /* print_value */, "overlay path");
+ print(header.GetOverlayName(), true /* print_value */, "overlay name");
+ print(header.GetDebugInfo(), false /* print_value */, "debug info");
- uint32_t debug_info_size = header.GetDebugInfo().size();
- print(debug_info_size, "debug info size");
- print("...", debug_info_size + CalculatePadding(debug_info_size), "debug info");
-
- auto target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
+ auto target_apk_ = ApkAssets::Load(header.GetTargetPath());
if (target_apk_) {
target_am_.SetApkAssets({target_apk_.get()});
apk_assets_.push_back(std::move(target_apk_));
}
- auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string());
+ auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath());
if (overlay_apk_) {
overlay_am_.SetApkAssets({overlay_apk_.get()});
apk_assets_.push_back(std::move(overlay_apk_));
@@ -100,7 +98,7 @@
print(target_entry.target_id, "target id");
}
- print("...", sizeof(Res_value::size) + sizeof(Res_value::res0), "padding");
+ pad(sizeof(Res_value::size) + sizeof(Res_value::res0));
print(target_entry.value.data_type, "type: %s",
utils::DataTypeToString(target_entry.value.data_type).data());
@@ -143,15 +141,13 @@
}
}
- uint32_t string_pool_size = data.GetStringPoolData().size();
- print(string_pool_size, "string pool size");
- print("...", string_pool_size + CalculatePadding(string_pool_size), "string pool");
+ print(data.GetStringPoolData(), false /* print_value */, "string pool");
}
void RawPrintVisitor::visit(const IdmapData::Header& header) {
print(header.GetTargetPackageId(), "target package id");
print(header.GetOverlayPackageId(), "overlay package id");
- print("...", sizeof(Idmap_data_header::p0), "padding");
+ align();
print(header.GetTargetEntryCount(), "target entry count");
print(header.GetTargetInlineEntryCount(), "target inline entry count");
print(header.GetOverlayEntryCount(), "overlay entry count");
@@ -168,7 +164,6 @@
stream_ << base::StringPrintf("%08zx: %02x", offset_, value) << " " << comment
<< std::endl;
-
offset_ += sizeof(uint8_t);
}
@@ -181,7 +176,6 @@
va_end(ap);
stream_ << base::StringPrintf("%08zx: %04x", offset_, value) << " " << comment << std::endl;
-
offset_ += sizeof(uint16_t);
}
@@ -194,22 +188,35 @@
va_end(ap);
stream_ << base::StringPrintf("%08zx: %08x", offset_, value) << " " << comment << std::endl;
-
offset_ += sizeof(uint32_t);
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
-void RawPrintVisitor::print(const std::string& value, size_t encoded_size, const char* fmt, ...) {
+void RawPrintVisitor::print(const std::string& value, bool print_value, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
std::string comment;
base::StringAppendV(&comment, fmt, ap);
va_end(ap);
- stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << ": " << value
- << std::endl;
+ stream_ << base::StringPrintf("%08zx: %08x", offset_, (uint32_t)value.size()) << " " << comment
+ << " size" << std::endl;
+ offset_ += sizeof(uint32_t);
- offset_ += encoded_size;
+ stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment;
+ offset_ += value.size() + CalculatePadding(value.size());
+
+ if (print_value) {
+ stream_ << ": " << value;
+ }
+ stream_ << std::endl;
}
+void RawPrintVisitor::align() {
+ offset_ += CalculatePadding(offset_);
+}
+
+void RawPrintVisitor::pad(size_t padding) {
+ offset_ += padding;
+}
} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index d777cbf..9abb9e4 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -230,8 +230,8 @@
base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
auto target_resource_result = target_am->GetResourceId(full_name);
if (!target_resource_result.has_value()) {
- log_info.Warning(LogMessage() << "failed to find resource \"" << full_name
- << "\" in target resources");
+ log_info.Warning(LogMessage()
+ << "failed to find resource \"" << full_name << "\" in target resources");
continue;
}
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index e817140..4e85e57 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -32,6 +32,12 @@
using android::util::Utf16ToUtf8;
namespace android::idmap2::utils {
+namespace {
+constexpr ResourceId kAttrName = 0x01010003;
+constexpr ResourceId kAttrResourcesMap = 0x01010609;
+constexpr ResourceId kAttrTargetName = 0x0101044d;
+constexpr ResourceId kAttrTargetPackage = 0x01010021;
+} // namespace
bool IsReference(uint8_t data_type) {
return data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE;
@@ -92,7 +98,7 @@
}
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
- bool assert_overlay) {
+ const std::string& name) {
std::unique_ptr<const ZipFile> zip = ZipFile::Open(path);
if (!zip) {
return Error("failed to open %s as a zip file", path.c_str());
@@ -113,65 +119,49 @@
return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str());
}
- auto overlay_it = std::find_if(manifest_it.begin(), manifest_it.end(), [](const auto& it) {
- return it.event() == XmlParser::Event::START_TAG && it.name() == "overlay";
- });
-
- OverlayManifestInfo info{};
- if (overlay_it == manifest_it.end()) {
- if (!assert_overlay) {
- return info;
+ for (auto&& it : manifest_it) {
+ if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") {
+ continue;
}
- return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
- }
- if (auto result_str = overlay_it->GetAttributeStringValue("targetPackage")) {
- info.target_package = *result_str;
- } else {
- return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(),
- result_str.GetErrorMessage().c_str());
- }
+ OverlayManifestInfo info{};
+ if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) {
+ if (*result_str != name) {
+ // A value for android:name was found, but either a the name does not match the requested
+ // name, or an <overlay> tag with no name was requested.
+ continue;
+ }
+ info.name = *result_str;
+ } else if (!name.empty()) {
+ // This tag does not have a value for android:name, but an <overlay> tag with a specific name
+ // has been requested.
+ continue;
+ }
- if (auto result_str = overlay_it->GetAttributeStringValue("targetName")) {
- info.target_name = *result_str;
- }
-
- if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) {
- if (IsReference((*result_value).dataType)) {
- info.resource_mapping = (*result_value).data;
+ if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) {
+ info.target_package = *result_str;
} else {
- return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
- path.c_str());
+ return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(),
+ result_str.GetErrorMessage().c_str());
}
- }
- if (auto result_value = overlay_it->GetAttributeValue("isStatic")) {
- if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
- (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
- info.is_static = (*result_value).data != 0U;
- } else {
- return Error("android:isStatic is not a boolean in AndroidManifest.xml of %s", path.c_str());
+ if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) {
+ info.target_name = *result_str;
}
- }
- if (auto result_value = overlay_it->GetAttributeValue("priority")) {
- if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
- (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
- info.priority = (*result_value).data;
- } else {
- return Error("android:priority is not an integer in AndroidManifest.xml of %s", path.c_str());
+ if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) {
+ if (IsReference((*result_value).dataType)) {
+ info.resource_mapping = (*result_value).data;
+ } else {
+ return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
+ path.c_str());
+ }
}
+ return info;
}
- if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyName")) {
- info.requiredSystemPropertyName = *result_str;
- }
-
- if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyValue")) {
- info.requiredSystemPropertyValue = *result_str;
- }
-
- return info;
+ return Error("<overlay> with android:name \"%s\" missing from AndroidManifest.xml of %s",
+ name.c_str(), path.c_str());
}
} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
index 4030b83..00baea4 100644
--- a/cmds/idmap2/libidmap2/XmlParser.cpp
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -90,15 +90,27 @@
return String8(key16).c_str();
}
-Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const {
- auto value = GetAttributeValue(name);
- if (!value) {
- return value.GetError();
+template <typename Func>
+Result<Res_value> FindAttribute(const ResXMLParser& parser, const std::string& label,
+ Func&& predicate) {
+ for (size_t i = 0; i < parser.getAttributeCount(); i++) {
+ if (!predicate(i)) {
+ continue;
+ }
+ Res_value res_value{};
+ if (parser.getAttributeValue(i, &res_value) == BAD_TYPE) {
+ return Error(R"(Bad value for attribute "%s")", label.c_str());
+ }
+ return res_value;
}
+ return Error(R"(Failed to find attribute "%s")", label.c_str());
+}
- switch ((*value).dataType) {
+Result<std::string> GetStringValue(const ResXMLParser& parser, const Res_value& value,
+ const std::string& label) {
+ switch (value.dataType) {
case Res_value::TYPE_STRING: {
- if (auto str = parser_.getStrings().string8ObjectAt((*value).data); str.ok()) {
+ if (auto str = parser.getStrings().string8ObjectAt(value.data); str.ok()) {
return std::string(str->string());
}
break;
@@ -106,31 +118,37 @@
case Res_value::TYPE_INT_DEC:
case Res_value::TYPE_INT_HEX:
case Res_value::TYPE_INT_BOOLEAN: {
- return std::to_string((*value).data);
+ return std::to_string(value.data);
}
}
+ return Error(R"(Failed to convert attribute "%s" value to a string)", label.c_str());
+}
- return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str());
+Result<Res_value> XmlParser::Node::GetAttributeValue(ResourceId attr,
+ const std::string& label) const {
+ return FindAttribute(parser_, label, [&](size_t index) -> bool {
+ return parser_.getAttributeNameResID(index) == attr;
+ });
}
Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
- size_t len;
- for (size_t i = 0; i < parser_.getAttributeCount(); i++) {
- const String16 key16(parser_.getAttributeName(i, &len));
+ return FindAttribute(parser_, name, [&](size_t index) -> bool {
+ size_t len;
+ const String16 key16(parser_.getAttributeName(index, &len));
std::string key = String8(key16).c_str();
- if (key != name) {
- continue;
- }
+ return key == name;
+ });
+}
- Res_value res_value{};
- if (parser_.getAttributeValue(i, &res_value) == BAD_TYPE) {
- return Error(R"(Bad value for attribute "%s")", name.c_str());
- }
+Result<std::string> XmlParser::Node::GetAttributeStringValue(ResourceId attr,
+ const std::string& label) const {
+ auto value = GetAttributeValue(attr, label);
+ return value ? GetStringValue(parser_, *value, label) : value.GetError();
+}
- return res_value;
- }
-
- return Error(R"(Failed to find attribute "%s")", name.c_str());
+Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const {
+ auto value = GetAttributeValue(name);
+ return value ? GetStringValue(parser_, *value, name) : value.GetError();
}
Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size,
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index c3a3e0b..524aabc 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -33,7 +33,7 @@
namespace android::idmap2 {
TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
auto result1 = Idmap::FromBinaryStream(raw_stream);
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index e7e9e4c..a55b41b 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -35,6 +35,7 @@
#include <vector>
#include "R.h"
+#include "TestConstants.h"
#include "TestHelpers.h"
#include "androidfw/PosixUtils.h"
#include "gmock/gmock.h"
@@ -43,6 +44,7 @@
#include "idmap2/Idmap.h"
#include "private/android_filesystem_config.h"
+using ::android::base::StringPrintf;
using ::android::util::ExecuteBinary;
using ::testing::NotNull;
@@ -90,6 +92,7 @@
"create",
"--target-apk-path", GetTargetApkPath(),
"--overlay-apk-path", GetOverlayApkPath(),
+ "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
"--idmap-path", GetIdmapPath()});
// clang-format on
ASSERT_THAT(result, NotNull());
@@ -116,6 +119,7 @@
"create",
"--target-apk-path", GetTargetApkPath(),
"--overlay-apk-path", GetOverlayApkPath(),
+ "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
"--idmap-path", GetIdmapPath()});
// clang-format on
ASSERT_THAT(result, NotNull());
@@ -128,14 +132,23 @@
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000"),
- std::string::npos);
- ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000"),
- std::string::npos);
- ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001"),
- std::string::npos);
- ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002"),
- std::string::npos);
+
+ ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::integer::int1,
+ R::overlay::integer::int1)),
+ std::string::npos)
+ << result->stdout;
+ ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str1,
+ R::overlay::string::str1)),
+ std::string::npos)
+ << result->stdout;
+ ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str3,
+ R::overlay::string::str3)),
+ std::string::npos)
+ << result->stdout;
+ ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str4,
+ R::overlay::string::str4)),
+ std::string::npos)
+ << result->stdout;
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -167,6 +180,7 @@
"create",
"--target-apk-path", GetTargetApkPath(),
"--overlay-apk-path", GetOverlayApkPath(),
+ "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
"--idmap-path", GetIdmapPath()});
// clang-format on
ASSERT_THAT(result, NotNull());
@@ -177,7 +191,7 @@
"lookup",
"--idmap-path", GetIdmapPath(),
"--config", "",
- "--resid", R::target::string::literal::str1});
+ "--resid", StringPrintf("0x%08x", R::target::string::str1)});
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -229,6 +243,7 @@
"create",
"--target-apk-path", GetTargetApkPath(),
"--overlay-apk-path", GetOverlayApkPath(),
+ "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
"--idmap-path"});
// clang-format on
ASSERT_THAT(result, NotNull());
@@ -240,6 +255,7 @@
"create",
"--target-apk-path", invalid_target_apk_path,
"--overlay-apk-path", GetOverlayApkPath(),
+ "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
"--idmap-path", GetIdmapPath()});
// clang-format on
ASSERT_THAT(result, NotNull());
@@ -251,6 +267,7 @@
"create",
"--target-apk-path", GetTargetApkPath(),
"--overlay-apk-path", GetOverlayApkPath(),
+ "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
"--idmap-path", GetIdmapPath(),
"--policy", "this-does-not-exist"});
// clang-format on
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 9b42a27..c13b049 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -27,6 +27,7 @@
#include "TestHelpers.h"
#include "android-base/macros.h"
#include "androidfw/ApkAssets.h"
+#include "androidfw/ResourceUtils.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/BinaryStreamVisitor.h"
@@ -35,7 +36,6 @@
#include "idmap2/LogInfo.h"
using android::Res_value;
-using ::testing::IsNull;
using ::testing::NotNull;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
@@ -61,36 +61,25 @@
}
TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::istringstream stream(raw);
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
- ASSERT_EQ(header->GetVersion(), 0x05U);
+ ASSERT_EQ(header->GetVersion(), 0x07U);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
ASSERT_EQ(header->GetEnforceOverlayable(), true);
- ASSERT_EQ(header->GetTargetPath().to_string(), "targetX.apk");
- ASSERT_EQ(header->GetOverlayPath().to_string(), "overlayX.apk");
+ ASSERT_EQ(header->GetTargetPath(), "targetX.apk");
+ ASSERT_EQ(header->GetOverlayPath(), "overlayX.apk");
ASSERT_EQ(header->GetDebugInfo(), "debug");
}
-TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
- // overwrite the target path string, including the terminating null, with '.'
- for (size_t i = 0x18; i < 0x118; i++) {
- raw[i] = '.';
- }
- std::istringstream stream(raw);
- std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
- ASSERT_THAT(header, IsNull());
-}
-
TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
- const size_t offset = 0x224;
+ const size_t offset = kIdmapRawDataOffset;
std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
- idmap_raw_data_len - offset);
+ kIdmapRawDataLen - offset);
std::istringstream stream(raw);
std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
@@ -100,9 +89,9 @@
}
TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
- const size_t offset = 0x224;
+ const size_t offset = kIdmapRawDataOffset;
std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
- idmap_raw_data_len - offset);
+ kIdmapRawDataLen - offset);
std::istringstream stream(raw);
std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
@@ -127,7 +116,7 @@
}
TEST(IdmapTests, CreateIdmapFromBinaryStream) {
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::istringstream stream(raw);
auto result = Idmap::FromBinaryStream(stream);
@@ -136,13 +125,14 @@
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x05U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
- ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), 0x11);
+ ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies);
ASSERT_EQ(idmap->GetHeader()->GetEnforceOverlayable(), true);
- ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "targetX.apk");
- ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlayX.apk");
+ ASSERT_EQ(idmap->GetHeader()->GetTargetPath(), kIdmapRawTargetPath);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), kIdmapRawOverlayPath);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayName(), kIdmapRawOverlayName);
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
@@ -187,48 +177,23 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ auto idmap_result = Idmap::FromApkAssets(
+ *target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x05U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
ASSERT_EQ(idmap->GetHeader()->GetEnforceOverlayable(), true);
- ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetPath(), target_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
-}
-
-Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
- const android::StringPiece& local_target_apk_path,
- const android::StringPiece& local_overlay_apk_path, const OverlayManifestInfo& overlay_info,
- const PolicyBitmask& fulfilled_policies, bool enforce_overlayable) {
- const std::string target_apk_path(GetTestDataPath() + local_target_apk_path.data());
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- if (!target_apk) {
- return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
- }
-
- const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path.data());
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- if (!overlay_apk) {
- return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
- }
-
- LogInfo log_info;
- auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info,
- fulfilled_policies, enforce_overlayable, log_info);
-
- if (!mapping) {
- return mapping.GetError();
- }
-
- return IdmapData::FromResourceMapping(*mapping);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayName(), TestConstants::OVERLAY_NAME_ALL_POLICIES);
}
TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
@@ -241,7 +206,8 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk,
+ TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
@@ -271,6 +237,29 @@
ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay::string::str4, R::target::string::str4);
}
+TEST(IdmapTests, FailCreateIdmapInvalidName) {
+ std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
+ std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
+
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ {
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_FALSE(idmap_result);
+ }
+ {
+ auto idmap_result =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, "unknown", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_FALSE(idmap_result);
+ }
+}
+
TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.apk";
@@ -281,7 +270,8 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk,
+ TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
@@ -296,34 +286,67 @@
const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 4U);
ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
- R::overlay_shared::integer::int1);
- ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, R::overlay_shared::string::str1);
- ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, R::overlay_shared::string::str3);
- ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, R::overlay_shared::string::str4);
+ fix_package_id(R::overlay::integer::int1, 0));
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1,
+ fix_package_id(R::overlay::string::str1, 0));
+ ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3,
+ fix_package_id(R::overlay::string::str3, 0));
+ ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4,
+ fix_package_id(R::overlay::string::str4, 0));
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 0U);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 4U);
- ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay_shared::integer::int1,
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], fix_package_id(R::overlay::integer::int1, 0),
R::target::integer::int1);
- ASSERT_OVERLAY_ENTRY(overlay_entries[1], R::overlay_shared::string::str1,
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], fix_package_id(R::overlay::string::str1, 0),
R::target::string::str1);
- ASSERT_OVERLAY_ENTRY(overlay_entries[2], R::overlay_shared::string::str3,
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], fix_package_id(R::overlay::string::str3, 0),
R::target::string::str3);
- ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay_shared::string::str4,
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], fix_package_id(R::overlay::string::str4, 0),
R::target::string::str4);
}
+Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
+ const std::string& local_target_apk_path, const std::string& local_overlay_apk_path,
+ const std::string& overlay_name, const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ auto overlay_info =
+ utils::ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name);
+ if (!overlay_info) {
+ return overlay_info.GetError();
+ }
+
+ const std::string target_apk_path(GetTestDataPath() + local_target_apk_path);
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
+ }
+
+ const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path);
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
+ }
+
+ LogInfo log_info;
+ auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info,
+ fulfilled_policies, enforce_overlayable, log_info);
+ if (!mapping) {
+ return mapping.GetError();
+ }
+
+ return IdmapData::FromResourceMapping(*mapping);
+}
+
TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
- OverlayManifestInfo info{};
- info.target_package = "test.target";
- info.target_name = "TestResources";
- info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
- auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::PUBLIC,
- /* enforce_overlayable */ false);
+ auto idmap_data =
+ TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", "DifferentPackages",
+
+ PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
auto& data = *idmap_data;
@@ -343,12 +366,8 @@
}
TEST(IdmapTests, CreateIdmapDataInlineResources) {
- OverlayManifestInfo info{};
- info.target_package = "test.target";
- info.target_name = "TestResources";
- info.resource_mapping = 0x7f030002; // xml/overlays_inline
- auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::PUBLIC,
+ auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk",
+ "Inline", PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
@@ -357,7 +376,7 @@
const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 0U);
- constexpr size_t overlay_string_pool_size = 8U;
+ constexpr size_t overlay_string_pool_size = 10U;
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 2U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
@@ -370,38 +389,20 @@
ASSERT_EQ(overlay_entries.size(), 0U);
}
-TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
- std::string target_apk_path(GetTestDataPath());
- for (int i = 0; i < 32; i++) {
- target_apk_path += "/target/../";
- }
- target_apk_path += "/target/target.apk";
- ASSERT_GT(target_apk_path.size(), kIdmapStringLength);
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
-
- const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
-
- const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
- ASSERT_FALSE(result);
-}
-
TEST(IdmapTests, IdmapHeaderIsUpToDate) {
fclose(stderr); // silence expected warnings from libandroidfw
- const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
+ const std::string target_apk_path = kIdmapRawTargetPath;
+ const std::string overlay_apk_path = kIdmapRawOverlayPath;
+ const std::string overlay_name = kIdmapRawOverlayName;
+ const PolicyBitmask policies = kIdmapRawDataPolicies;
+ const uint32_t target_crc = kIdmapRawDataTargetCrc;
+ const uint32_t overlay_crc = kIdmapRawOverlayCrc;
- const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
+ std::istringstream raw_stream(raw);
- auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ auto result = Idmap::FromBinaryStream(raw_stream);
ASSERT_TRUE(result);
const auto idmap = std::move(*result);
@@ -411,8 +412,9 @@
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
- ASSERT_TRUE(header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+ ASSERT_TRUE(header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
+ kIdmapRawDataTargetCrc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
// magic: bytes (0x0, 0x03)
std::string bad_magic_string(stream.str());
@@ -425,8 +427,9 @@
IdmapHeader::FromBinaryStream(bad_magic_stream);
ASSERT_THAT(bad_magic_header, NotNull());
ASSERT_NE(header->GetMagic(), bad_magic_header->GetMagic());
- ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
// version: bytes (0x4, 0x07)
std::string bad_version_string(stream.str());
@@ -439,8 +442,9 @@
IdmapHeader::FromBinaryStream(bad_version_stream);
ASSERT_THAT(bad_version_header, NotNull());
ASSERT_NE(header->GetVersion(), bad_version_header->GetVersion());
- ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
// target crc: bytes (0x8, 0xb)
std::string bad_target_crc_string(stream.str());
@@ -453,8 +457,9 @@
IdmapHeader::FromBinaryStream(bad_target_crc_stream);
ASSERT_THAT(bad_target_crc_header, NotNull());
ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc());
- ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
// overlay crc: bytes (0xc, 0xf)
std::string bad_overlay_crc_string(stream.str());
@@ -467,8 +472,9 @@
IdmapHeader::FromBinaryStream(bad_overlay_crc_stream);
ASSERT_THAT(bad_overlay_crc_header, NotNull());
ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc());
- ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
// fulfilled policy: bytes (0x10, 0x13)
std::string bad_policy_string(stream.str());
@@ -481,8 +487,9 @@
IdmapHeader::FromBinaryStream(bad_policy_stream);
ASSERT_THAT(bad_policy_header, NotNull());
ASSERT_NE(header->GetFulfilledPolicies(), bad_policy_header->GetFulfilledPolicies());
- ASSERT_FALSE(bad_policy_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+ ASSERT_FALSE(bad_policy_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
// enforce overlayable: bytes (0x14)
std::string bad_enforce_string(stream.str());
@@ -492,30 +499,47 @@
IdmapHeader::FromBinaryStream(bad_enforce_stream);
ASSERT_THAT(bad_enforce_header, NotNull());
ASSERT_NE(header->GetEnforceOverlayable(), bad_enforce_header->GetEnforceOverlayable());
- ASSERT_FALSE(bad_enforce_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+ ASSERT_FALSE(bad_enforce_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
- // target path: bytes (0x18, 0x117)
+ // target path: bytes (0x1c, 0x27)
std::string bad_target_path_string(stream.str());
- bad_target_path_string[0x18] = '\0';
+ bad_target_path_string[0x1c] = '\0';
std::stringstream bad_target_path_stream(bad_target_path_string);
std::unique_ptr<const IdmapHeader> bad_target_path_header =
IdmapHeader::FromBinaryStream(bad_target_path_stream);
ASSERT_THAT(bad_target_path_header, NotNull());
ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath());
- ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
- // overlay path: bytes (0x118, 0x217)
+ // overlay path: bytes (0x2c, 0x37)
std::string bad_overlay_path_string(stream.str());
- bad_overlay_path_string[0x118] = '\0';
+ bad_overlay_path_string[0x33] = '\0';
std::stringstream bad_overlay_path_stream(bad_overlay_path_string);
std::unique_ptr<const IdmapHeader> bad_overlay_path_header =
IdmapHeader::FromBinaryStream(bad_overlay_path_stream);
ASSERT_THAT(bad_overlay_path_header, NotNull());
ASSERT_NE(header->GetOverlayPath(), bad_overlay_path_header->GetOverlayPath());
- ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+ ASSERT_FALSE(bad_overlay_path_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
+
+ // overlay path: bytes (0x3c, 0x47)
+ std::string bad_overlay_name_string(stream.str());
+ bad_overlay_name_string[0x3c] = '\0';
+ std::stringstream bad_overlay_name_stream(bad_overlay_name_string);
+ std::unique_ptr<const IdmapHeader> bad_overlay_name_header =
+ IdmapHeader::FromBinaryStream(bad_overlay_name_stream);
+ ASSERT_THAT(bad_overlay_name_header, NotNull());
+ ASSERT_NE(header->GetOverlayName(), bad_overlay_name_header->GetOverlayName());
+ ASSERT_FALSE(bad_overlay_name_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
+
+ // overlay name: bytes (0x2c, 0x37)
}
class TestVisitor : public Visitor {
@@ -544,7 +568,7 @@
};
TEST(IdmapTests, TestVisitor) {
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::istringstream stream(raw);
const auto idmap = Idmap::FromBinaryStream(stream);
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index d30fbfc..87ce0f1 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -15,22 +15,21 @@
*/
#include <memory>
-#include <sstream>
#include <string>
#include "R.h"
+#include "TestConstants.h"
#include "TestHelpers.h"
#include "androidfw/ApkAssets.h"
-#include "androidfw/Idmap.h"
#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/Idmap.h"
#include "idmap2/PrettyPrintVisitor.h"
-using ::testing::NotNull;
-
using android::ApkAssets;
+using android::base::StringPrintf;
+using ::testing::NotNull;
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
@@ -46,7 +45,8 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk,
+ TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
@@ -56,15 +56,15 @@
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
- ASSERT_NE(stream.str().find(R::target::integer::literal::int1 +
- " -> 0x7f010000 (integer/int1 -> integer/int1)\n"),
+ ASSERT_NE(stream.str().find(StringPrintf("0x%08x -> 0x%08x (integer/int1 -> integer/int1)\n",
+ R::target::integer::int1, R::overlay::integer::int1)),
std::string::npos);
}
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
fclose(stderr); // silence expected warnings from libandroidfw
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
const auto idmap = Idmap::FromBinaryStream(raw_stream);
diff --git a/cmds/idmap2/tests/R.h b/cmds/idmap2/tests/R.h
index 854b57f..ac9b058 100644
--- a/cmds/idmap2/tests/R.h
+++ b/cmds/idmap2/tests/R.h
@@ -23,22 +23,11 @@
namespace android::idmap2 {
-static std::string hexify(ResourceId id) {
- std::stringstream stream;
- stream << std::hex << static_cast<uint32_t>(id);
- return stream.str();
-}
-
// clang-format off
namespace R::target {
namespace integer { // NOLINT(runtime/indentation_namespace)
constexpr ResourceId int1 = 0x7f010000;
-
- namespace literal { // NOLINT(runtime/indentation_namespace)
- inline const std::string int1 = hexify(R::target::integer::int1);
- }
}
-
namespace string { // NOLINT(runtime/indentation_namespace)
constexpr ResourceId not_overlayable = 0x7f020003;
constexpr ResourceId other = 0x7f020004;
@@ -54,56 +43,31 @@
constexpr ResourceId str1 = 0x7f02000e;
constexpr ResourceId str3 = 0x7f020010;
constexpr ResourceId str4 = 0x7f020011;
-
- namespace literal { // NOLINT(runtime/indentation_namespace)
- inline const std::string str1 = hexify(R::target::string::str1);
- inline const std::string str3 = hexify(R::target::string::str3);
- inline const std::string str4 = hexify(R::target::string::str4);
- }
} // namespace string
} // namespace R::target
namespace R::overlay {
namespace integer { // NOLINT(runtime/indentation_namespace)
constexpr ResourceId int1 = 0x7f010000;
+ constexpr ResourceId not_in_target = 0x7f010001;
}
namespace string { // NOLINT(runtime/indentation_namespace)
- constexpr ResourceId str1 = 0x7f020000;
- constexpr ResourceId str3 = 0x7f020001;
- constexpr ResourceId str4 = 0x7f020002;
+ constexpr ResourceId not_overlayable = 0x7f020000;
+ constexpr ResourceId other = 0x7f020001;
+ constexpr ResourceId policy_actor = 0x7f020002;
+ constexpr ResourceId policy_config_signature = 0x7f020003;
+ constexpr ResourceId policy_odm = 0x7f020004;
+ constexpr ResourceId policy_oem = 0x7f020005;
+ constexpr ResourceId policy_product = 0x7f020006;
+ constexpr ResourceId policy_public = 0x7f020007;
+ constexpr ResourceId policy_signature = 0x7f020008;
+ constexpr ResourceId policy_system = 0x7f020009;
+ constexpr ResourceId policy_system_vendor = 0x7f02000a;
+ constexpr ResourceId str1 = 0x7f02000b;
+ constexpr ResourceId str3 = 0x7f02000c;
+ constexpr ResourceId str4 = 0x7f02000d;
}
}
-
-namespace R::overlay_shared {
- namespace integer { // NOLINT(runtime/indentation_namespace)
- constexpr ResourceId int1 = 0x00010000;
- }
- namespace string { // NOLINT(runtime/indentation_namespace)
- constexpr ResourceId str1 = 0x00020000;
- constexpr ResourceId str3 = 0x00020001;
- constexpr ResourceId str4 = 0x00020002;
- }
-}
-
-namespace R::system_overlay::string {
- constexpr ResourceId policy_public = 0x7f010000;
- constexpr ResourceId policy_system = 0x7f010001;
- constexpr ResourceId policy_system_vendor = 0x7f010002;
-}
-
-namespace R::system_overlay_invalid::string {
- constexpr ResourceId not_overlayable = 0x7f010000;
- constexpr ResourceId other = 0x7f010001;
- constexpr ResourceId policy_actor = 0x7f010002;
- constexpr ResourceId policy_config_signature = 0x7f010003;
- constexpr ResourceId policy_odm = 0x7f010004;
- constexpr ResourceId policy_oem = 0x7f010005;
- constexpr ResourceId policy_product = 0x7f010006;
- constexpr ResourceId policy_public = 0x7f010007;
- constexpr ResourceId policy_signature = 0x7f010008;
- constexpr ResourceId policy_system = 0x7f010009;
- constexpr ResourceId policy_system_vendor = 0x7f01000a;
-} // namespace R::system_overlay_invalid::string
// clang-format on
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 95bd9473..88f85ef 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -56,8 +56,9 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_DEFAULT,
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
@@ -65,7 +66,7 @@
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000005 version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str());
ASSERT_CONTAINS_REGEX(
StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
stream.str());
@@ -76,22 +77,34 @@
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000008 string pool index offset\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "000000b4 string pool size\n", stream.str());
- ASSERT_CONTAINS_REGEX("000002bc: ........ string pool: ...\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000000 target inline entry count", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "0000000a string pool index offset", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f02000e target id: string/str1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f02000b overlay id: string/str1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020010 target id: string/str3", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f02000c overlay id: string/str3", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020011 target id: string/str4", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f02000d overlay id: string/str4", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f02000b overlay id: string/str1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f02000e target id: string/str1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f02000c overlay id: string/str3", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020010 target id: string/str3", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f02000d overlay id: string/str4", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020011 target id: string/str4", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "000000b4 string pool size", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "........ string pool", stream.str());
}
TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
fclose(stderr); // silence expected warnings from libandroidfw
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
const auto idmap = Idmap::FromBinaryStream(raw_stream);
@@ -102,11 +115,17 @@
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000005 version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "0000000b target path size\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "........ target path: targetX.apk\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "0000000c overlay path size\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay path: overlayX.apk\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "0000000b overlay name size\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay name: OverlayName\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str());
@@ -121,7 +140,7 @@
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 string pool size\n", stream.str());
- ASSERT_CONTAINS_REGEX("00000278: ........ string pool: ...\n", stream.str());
+ ASSERT_CONTAINS_REGEX("000000a8: ........ string pool\n", stream.str());
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 185e929..0362529 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -17,12 +17,10 @@
#include <cstdio> // fclose
#include <fstream>
#include <memory>
-#include <sstream>
#include <string>
-#include <utility>
-#include <vector>
#include "R.h"
+#include "TestConstants.h"
#include "TestHelpers.h"
#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
@@ -43,38 +41,32 @@
ASSERT_TRUE(result) << result.GetErrorMessage(); \
} while (0)
-Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
- const android::StringPiece& local_overlay_apk_path,
- const OverlayManifestInfo& overlay_info,
+Result<ResourceMapping> TestGetResourceMapping(const std::string& local_target_apk_path,
+ const std::string& local_overlay_apk_path,
+ const std::string& overlay_name,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
- const std::string target_apk_path(GetTestDataPath() + local_target_apk_path.data());
+ auto overlay_info =
+ ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name);
+ if (!overlay_info) {
+ return overlay_info.GetError();
+ }
+
+ const std::string target_apk_path(GetTestDataPath() + local_target_apk_path);
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
}
- const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path.data());
+ const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path);
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
if (!overlay_apk) {
return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
}
LogInfo log_info;
- return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info, fulfilled_policies,
- enforce_overlayable, log_info);
-}
-
-Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
- const android::StringPiece& local_overlay_apk_path,
- const PolicyBitmask& fulfilled_policies,
- bool enforce_overlayable) {
- auto overlay_info = ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path.data());
- if (!overlay_info) {
- return overlay_info.GetError();
- }
- return TestGetResourceMapping(local_target_apk_path, local_overlay_apk_path, *overlay_info,
- fulfilled_policies, enforce_overlayable);
+ return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info,
+ fulfilled_policies, enforce_overlayable, log_info);
}
Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource,
@@ -136,13 +128,8 @@
}
TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
- OverlayManifestInfo info{};
- info.target_package = "test.target";
- info.target_name = "TestResources";
- info.resource_mapping = 0U; // no xml
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::PUBLIC,
- /* enforce_overlayable */ false);
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "",
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
@@ -158,11 +145,7 @@
}
TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
- OverlayManifestInfo info{};
- info.target_package = "test.target";
- info.target_name = "TestResources";
- info.resource_mapping = 0x7f030003; // xml/overlays_swap
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "SwapNames",
PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -178,12 +161,8 @@
}
TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
- OverlayManifestInfo info{};
- info.target_package = "test.target";
- info.target_name = "TestResources";
- info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::PUBLIC,
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
+ "DifferentPackages", PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
@@ -192,19 +171,15 @@
ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
ASSERT_RESULT(MappingExists(res, R::target::string::str1, 0x0104000a,
false /* rewrite */)); // -> android:string/ok
- ASSERT_RESULT(MappingExists(res, R::target::string::str3, 0x7f020001, true /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::str3, R::overlay::string::str3, true /* rewrite */));
}
TEST(ResourceMappingTests, InlineResources) {
- OverlayManifestInfo info{};
- info.target_package = "test.target";
- info.target_name = "TestResources";
- info.resource_mapping = 0x7f030002; // xml/overlays_inline
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::PUBLIC,
- /* enforce_overlayable */ false);
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "Inline",
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
- constexpr size_t overlay_string_pool_size = 8U;
+ constexpr size_t overlay_string_pool_size = 10U;
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
@@ -215,28 +190,8 @@
}
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
- auto resources =
- TestGetResourceMapping("/target/target.apk", "/system-overlay/system-overlay.apk",
- PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
-
- ASSERT_TRUE(resources) << resources.GetErrorMessage();
- auto& res = *resources;
- ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
- ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
- R::system_overlay::string::policy_public, false /* rewrite */));
- ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
- R::system_overlay::string::policy_system, false /* rewrite */));
- ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
- R::system_overlay::string::policy_system_vendor,
- false /* rewrite */));
-}
-
-// Resources that are not declared as overlayable and resources that a protected by policies the
-// overlay does not fulfill must not map to overlay resources.
-TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
- auto resources = TestGetResourceMapping("/target/target.apk",
- "/system-overlay-invalid/system-overlay-invalid.apk",
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
+ TestConstants::OVERLAY_NAME_ALL_POLICIES,
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
@@ -244,22 +199,38 @@
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
- R::system_overlay_invalid::string::policy_public,
- false /* rewrite */));
+ R::overlay::string::policy_public, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
- R::system_overlay_invalid::string::policy_system,
- false /* rewrite */));
+ R::overlay::string::policy_system, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
- R::system_overlay_invalid::string::policy_system_vendor,
- false /* rewrite */));
+ R::overlay::string::policy_system_vendor, true /* rewrite */));
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfill must not map to overlay resources.
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
+ TestConstants::OVERLAY_NAME_ALL_POLICIES,
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
+ R::overlay::string::policy_public, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
+ R::overlay::string::policy_system, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
+ R::overlay::string::policy_system_vendor, true /* rewrite */));
}
// Resources that are not declared as overlayable and resources that a protected by policies the
// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
// off.
TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
- auto resources = TestGetResourceMapping("/target/target.apk",
- "/system-overlay-invalid/system-overlay-invalid.apk",
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
+ TestConstants::OVERLAY_NAME_ALL_POLICIES,
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -267,41 +238,33 @@
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 11U);
ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable,
- R::system_overlay_invalid::string::not_overlayable,
- false /* rewrite */));
- ASSERT_RESULT(MappingExists(res, R::target::string::other,
- R::system_overlay_invalid::string::other, false /* rewrite */));
+ R::overlay::string::not_overlayable, true /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::other, R::overlay::string::other, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor,
- R::system_overlay_invalid::string::policy_actor,
- false /* rewrite */));
- ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm,
- R::system_overlay_invalid::string::policy_odm, false /* rewrite */));
- ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem,
- R::system_overlay_invalid::string::policy_oem, false /* rewrite */));
+ R::overlay::string::policy_actor, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, R::overlay::string::policy_odm,
+ true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, R::overlay::string::policy_oem,
+ true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_product,
- R::system_overlay_invalid::string::policy_product,
- false /* rewrite */));
+ R::overlay::string::policy_product, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
- R::system_overlay_invalid::string::policy_public,
- false /* rewrite */));
+ R::overlay::string::policy_public, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature,
- R::system_overlay_invalid::string::policy_config_signature,
- false /* rewrite */));
+ R::overlay::string::policy_config_signature, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature,
- R::system_overlay_invalid::string::policy_signature,
- false /* rewrite */));
+ R::overlay::string::policy_signature, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
- R::system_overlay_invalid::string::policy_system,
- false /* rewrite */));
+ R::overlay::string::policy_system, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
- R::system_overlay_invalid::string::policy_system_vendor,
- false /* rewrite */));
+ R::overlay::string::policy_system_vendor, true /* rewrite */));
}
-// Overlays that do not target an <overlayable> tag can overlay resources defined within any
-// <overlayable> tag.
+// Overlays that do not target an <overlayable> tag can overlay any resource if overlayable
+// enforcement is disabled.
TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-no-name.apk",
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "",
PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -321,9 +284,10 @@
// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
// overlay packages that have not defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
- auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
- "/overlay/overlay-no-name.apk", PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ auto resources =
+ TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk",
+ "NoTargetName", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
@@ -334,46 +298,36 @@
// defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
- auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
- "/system-overlay-invalid/system-overlay-invalid.apk",
- fulfilled_policies,
- /* enforce_overlayable */ true);
+ auto resources =
+ TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk",
+ TestConstants::OVERLAY_NAME_ALL_POLICIES, fulfilled_policies,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 11U);
ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable,
- R::system_overlay_invalid::string::not_overlayable,
- false /* rewrite */));
- ASSERT_RESULT(MappingExists(res, R::target::string::other,
- R::system_overlay_invalid::string::other, false /* rewrite */));
+ R::overlay::string::not_overlayable, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::other, R::overlay::string::other,
+ true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor,
- R::system_overlay_invalid::string::policy_actor,
- false /* rewrite */));
- ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm,
- R::system_overlay_invalid::string::policy_odm,
- false /* rewrite */));
- ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem,
- R::system_overlay_invalid::string::policy_oem,
- false /* rewrite */));
+ R::overlay::string::policy_actor, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, R::overlay::string::policy_odm,
+ true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, R::overlay::string::policy_oem,
+ true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_product,
- R::system_overlay_invalid::string::policy_product,
- false /* rewrite */));
+ R::overlay::string::policy_product, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
- R::system_overlay_invalid::string::policy_public,
- false /* rewrite */));
+ R::overlay::string::policy_public, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature,
- R::system_overlay_invalid::string::policy_config_signature,
- false /* rewrite */));
+ R::overlay::string::policy_config_signature, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature,
- R::system_overlay_invalid::string::policy_signature,
- false /* rewrite */));
+ R::overlay::string::policy_signature, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
- R::system_overlay_invalid::string::policy_system,
- false /* rewrite */));
+ R::overlay::string::policy_system, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
- R::system_overlay_invalid::string::policy_system_vendor,
- false /* rewrite */));
+ R::overlay::string::policy_system_vendor, true /* rewrite */));
};
CheckEntries(PolicyFlags::SIGNATURE);
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
index 9ed807c..1f6bf49 100644
--- a/cmds/idmap2/tests/ResourceUtilsTests.cpp
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -59,4 +59,26 @@
ASSERT_FALSE(name);
}
-} // namespace android::idmap2
+TEST_F(ResourceUtilsTests, InvalidValidOverlayNameInvalidAttributes) {
+ auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
+ "InvalidName");
+ ASSERT_FALSE(info);
+}
+
+TEST_F(ResourceUtilsTests, ValidOverlayNameInvalidAttributes) {
+ auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
+ "ValidName");
+ ASSERT_FALSE(info);
+}
+
+TEST_F(ResourceUtilsTests, ValidOverlayNameAndTargetPackageInvalidAttributes) {
+ auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
+ "ValidNameAndTargetPackage");
+ ASSERT_TRUE(info);
+ ASSERT_EQ("ValidNameAndTargetPackage", info->name);
+ ASSERT_EQ("Valid", info->target_package);
+ ASSERT_EQ("", info->target_name); // Attribute resource id could not be found
+ ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found
+}
+
+}// namespace android::idmap2
diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h
index 69575b8..d5799ad 100644
--- a/cmds/idmap2/tests/TestConstants.h
+++ b/cmds/idmap2/tests/TestConstants.h
@@ -22,8 +22,11 @@
constexpr const auto TARGET_CRC = 0x7c2d4719;
constexpr const auto TARGET_CRC_STRING = "7c2d4719";
-constexpr const auto OVERLAY_CRC = 0x5afff726;
-constexpr const auto OVERLAY_CRC_STRING = "5afff726";
+constexpr const auto OVERLAY_CRC = 0xb71095cf;
+constexpr const auto OVERLAY_CRC_STRING = "b71095cf";
+
+constexpr const char* OVERLAY_NAME_DEFAULT = "Default";
+constexpr const char* OVERLAY_NAME_ALL_POLICIES = "AllPolicies";
} // namespace android::idmap2::TestConstants
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index d0a8e3d..842af3d 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -30,7 +30,7 @@
0x49, 0x44, 0x4d, 0x50,
// 0x4: version
- 0x05, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00,
// 0x8: target crc
0x34, 0x12, 0x00, 0x00,
@@ -44,125 +44,114 @@
// 0x14: enforce overlayable
0x01, 0x00, 0x00, 0x00,
- // 0x18: target path "targetX.apk"
- 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // 0x18: target path length
+ 0x0b, 0x00, 0x00, 0x00,
- // 0x118: overlay path "overlayX.apk"
- 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // 0x1c: target path "targetX.apk"
+ 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00,
- // 0x218: debug string
+ // 0x28: overlay path length
+ 0x0c, 0x00, 0x00, 0x00,
+
+ // 0x2c: overlay path "overlayX.apk"
+ 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b,
+
+ // 0x38: overlay name length
+ 0x0b, 0x00, 0x00, 0x00,
+
+ // 0x3c: overlay name "OverlayName"
+ 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6D, 0x65, 0x00,
+
+ // 0x48 -> 4c: debug string
// string length,
0x05, 0x00, 0x00, 0x00,
- // 0x21c string contents "debug\0\0\0" (padded to word alignment)
+ // 0x4c string contents "debug\0\0\0" (padded to word alignment)
0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
// DATA HEADER
- // 0x224: target_package_id
+ // 0x54: target_package_id
0x7f,
- // 0x225: overlay_package_id
+ // 0x55: overlay_package_id
0x7f,
- // 0x226: padding
+ // 0x56: padding
0x00, 0x00,
- // 0x228: target_entry_count
+ // 0x58: target_entry_count
0x03, 0x00, 0x00, 0x00,
- // 0x22c: target_inline_entry_count
+ // 0x5c: target_inline_entry_count
0x01, 0x00, 0x00, 0x00,
- // 0x230: overlay_entry_count
+ // 0x60: overlay_entry_count
0x03, 0x00, 0x00, 0x00,
- // 0x234: string_pool_offset
+ // 0x64: string_pool_offset
0x00, 0x00, 0x00, 0x00,
// TARGET ENTRIES
- // 0x238: target id (0x7f020000)
+ // 0x68: target id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
- // 0x23c: overlay_id (0x7f020000)
+ // 0x6c: overlay_id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
- // 0x240: target id (0x7f030000)
+ // 0x70: target id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
- // 0x244: overlay_id (0x7f030000)
+ // 0x74: overlay_id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
- // 0x248: target id (0x7f030002)
+ // 0x78: target id (0x7f030002)
0x02, 0x00, 0x03, 0x7f,
- // 0x24c: overlay_id (0x7f030001)
+ // 0x7c: overlay_id (0x7f030001)
0x01, 0x00, 0x03, 0x7f,
// INLINE TARGET ENTRIES
- // 0x250: target_id
+ // 0x80: target_id
0x00, 0x00, 0x04, 0x7f,
- // 0x254: Res_value::size (value ignored by idmap)
+ // 0x84: Res_value::size (value ignored by idmap)
0x08, 0x00,
- // 0x256: Res_value::res0 (value ignored by idmap)
+ // 0x87: Res_value::res0 (value ignored by idmap)
0x00,
- // 0x257: Res_value::dataType (TYPE_INT_HEX)
+ // 0x88: Res_value::dataType (TYPE_INT_HEX)
0x11,
- // 0x258: Res_value::data
+ // 0x8c: Res_value::data
0x78, 0x56, 0x34, 0x12,
// OVERLAY ENTRIES
- // 0x25c: 0x7f020000 -> 0x7f020000
+ // 0x90: 0x7f020000 -> 0x7f020000
0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
- // 0x264: 0x7f030000 -> 0x7f030000
+ // 0x98: 0x7f030000 -> 0x7f030000
0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
- // 0x26c: 0x7f030001 -> 0x7f030002
+ // 0xa0: 0x7f030001 -> 0x7f030002
0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
- // 0x274: string pool
+ // 0xa4: string pool
// string length,
0x04, 0x00, 0x00, 0x00,
- // 0x278 string contents "test" (padded to word alignment)
+ // 0xa8 string contents "test"
0x74, 0x65, 0x73, 0x74};
-const unsigned int idmap_raw_data_len = 0x27c;
+const unsigned int kIdmapRawDataLen = 0xac;
+const unsigned int kIdmapRawDataOffset = 0x54;
+const unsigned int kIdmapRawDataTargetCrc = 0x1234;
+const unsigned int kIdmapRawOverlayCrc = 0x5678;
+const unsigned int kIdmapRawDataPolicies = 0x11;
+inline const std::string kIdmapRawTargetPath = "targetX.apk";
+inline const std::string kIdmapRawOverlayPath = "overlayX.apk";
+inline const std::string kIdmapRawOverlayName = "OverlayName";
std::string GetTestDataPath();
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
index cf3691c..2c50dee 100644
--- a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
@@ -13,14 +13,37 @@
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="test.overlay">
-
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.overlay">
<application android:hasCode="false"/>
- <overlay
- android:targetPackage="test.target"
- android:targetName="TestResources"
- android:resourcesMap="@xml/overlays"/>
+ <overlay android:name="Default"
+ android:targetPackage="test.target"
+ android:targetName="TestResources"
+ android:resourcesMap="@xml/overlays"/>
+
+ <overlay android:name="NoTargetName"
+ android:targetPackage="test.target"
+ android:resourcesMap="@xml/overlays"/>
+
+ <overlay android:name="Inline"
+ android:targetName="TestResources"
+ android:targetPackage="test.target"
+ android:resourcesMap="@xml/overlays_inline"/>
+
+ <overlay android:name="DifferentPackages"
+ android:targetName="TestResources"
+ android:targetPackage="test.target"
+ android:resourcesMap="@xml/overlays_different_package"/>
+
+ <overlay android:name="SwapNames"
+ android:targetName="TestResources"
+ android:targetPackage="test.target"
+ android:resourcesMap="@xml/overlays_swap"/>
+
+ <overlay android:name="AllPolicies"
+ android:targetName="TestResources"
+ android:targetPackage="test.target"
+ android:resourcesMap="@xml/overlays_policies"/>
+
</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestInvalid.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestInvalid.xml
new file mode 100644
index 0000000..d61c36c
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifestInvalid.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.overlay">
+ <application android:hasCode="false"/>
+
+ <overlay name="InvalidName"
+ android:targetName="Invalid"
+ android:targetPackage="Invalid"
+ android:resourcesMap="@xml/overlays_swap"/>
+
+ <overlay android:name="ValidName"
+ targetName="Invalid"
+ targetPackage="Invalid"
+ resourcesMap="@xml/overlays_swap"/>
+
+ <overlay android:name="ValidNameAndTargetPackage"
+ targetName="Invalid"
+ android:targetPackage="Valid"
+ resourcesMap="@xml/overlays_swap"/>
+</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestLegacy.xml
similarity index 71%
rename from cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml
rename to cmds/idmap2/tests/data/overlay/AndroidManifestLegacy.xml
index 70efc86..9fc2105 100644
--- a/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifestLegacy.xml
@@ -13,12 +13,9 @@
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="test.overlay.static2">
- <overlay
- android:targetPackage="test.target"
- android:targetName="TestResources"
- android:isStatic="true"
- android:priority="2" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.overlay">
+ <application android:hasCode="false"/>
+
+ <overlay android:targetPackage="test.target"/>
</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestNoName.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestNoName.xml
deleted file mode 100644
index bc6b733..0000000
--- a/cmds/idmap2/tests/data/overlay/AndroidManifestNoName.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="test.overlay.no.name">
- <overlay
- android:targetPackage="test.target"/>
-</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestNoNameStatic.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestNoNameStatic.xml
deleted file mode 100644
index ed327ce..0000000
--- a/cmds/idmap2/tests/data/overlay/AndroidManifestNoNameStatic.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="test.overlay.no.name.static">
- <overlay
- android:targetPackage="test.target"
- android:targetName="TestResources"
- android:isStatic="true"
- android:priority="1" />
-</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml
deleted file mode 100644
index 1c4dae6..0000000
--- a/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="test.overlay.static1">
- <overlay
- android:targetPackage="test.target"
- android:targetName="TestResources"
- android:isStatic="true"
- android:priority="1" />
-</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
index 114b099..7b1a66f 100755
--- a/cmds/idmap2/tests/data/overlay/build
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -26,37 +26,23 @@
aapt2 link \
--no-resource-removal \
-I "$FRAMEWORK_RES_APK" \
- --manifest AndroidManifestNoName.xml \
- -o overlay-no-name.apk \
- compiled.flata
-
-aapt2 link \
- --no-resource-removal \
- -I "$FRAMEWORK_RES_APK" \
- --manifest AndroidManifestNoNameStatic.xml \
- -o overlay-no-name-static.apk \
- compiled.flata
-
-aapt2 link \
- --no-resource-removal \
- -I "$FRAMEWORK_RES_APK" \
- --manifest AndroidManifestStatic1.xml \
- -o overlay-static-1.apk \
- compiled.flata
-
-aapt2 link \
- --no-resource-removal \
- -I "$FRAMEWORK_RES_APK" \
- --manifest AndroidManifestStatic2.xml \
- -o overlay-static-2.apk \
- compiled.flata
-
-aapt2 link \
- --no-resource-removal \
- --shared-lib \
- -I "$FRAMEWORK_RES_APK" \
--manifest AndroidManifest.xml \
-o overlay-shared.apk \
+ --shared-lib \
+ compiled.flata
+
+aapt2 link \
+ --no-resource-removal \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifestLegacy.xml \
+ -o overlay-legacy.apk \
+ compiled.flata
+
+aapt2 link \
+ --no-resource-removal \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifestInvalid.xml \
+ -o overlay-invalid.apk \
compiled.flata
rm compiled.flata
diff --git a/cmds/idmap2/tests/data/overlay/overlay-invalid.apk b/cmds/idmap2/tests/data/overlay/overlay-invalid.apk
new file mode 100644
index 0000000..888c871e
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-invalid.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-legacy.apk b/cmds/idmap2/tests/data/overlay/overlay-legacy.apk
new file mode 100644
index 0000000..f03eebb
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-legacy.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
deleted file mode 100644
index dab25b1..0000000
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
+++ /dev/null
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
deleted file mode 100644
index c8b95c2..0000000
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
+++ /dev/null
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-shared.apk b/cmds/idmap2/tests/data/overlay/overlay-shared.apk
index 0a8b737..3c896ea7 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-shared.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-shared.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
deleted file mode 100644
index fd41182..0000000
--- a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
+++ /dev/null
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
deleted file mode 100644
index b24765f..0000000
--- a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
+++ /dev/null
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk
index 870575e..c7ea623 100644
--- a/cmds/idmap2/tests/data/overlay/overlay.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/res/values/values.xml b/cmds/idmap2/tests/data/overlay/res/values/values.xml
index 815d1a8..6e98b21 100644
--- a/cmds/idmap2/tests/data/overlay/res/values/values.xml
+++ b/cmds/idmap2/tests/data/overlay/res/values/values.xml
@@ -18,4 +18,25 @@
<string name="str3">overlay-3</string>
<integer name="int1">-1</integer>
<integer name="not_in_target">-1</integer>
+
+ <!-- This overlay will fulfill the policies "public|system". This allows it overlay the
+ following resources. -->
+ <string name="overlay_policy_system">overlaid</string>
+ <string name="overlay_policy_system_vendor">overlaid</string>
+ <string name="overlay_policy_public">overlaid</string>
+
+ <!-- Requests to overlay a resource that belongs to a policy the overlay does not fulfill. -->
+ <string name="overlay_policy_product">overlaid</string>
+ <string name="overlay_policy_signature">overlaid</string>
+ <string name="overlay_policy_odm">overlaid</string>
+ <string name="overlay_policy_oem">overlaid</string>
+ <string name="overlay_policy_actor">overlaid</string>
+ <string name="overlay_policy_config_signature">overlaid</string>
+
+ <!-- Requests to overlay a resource that is not declared as overlayable. -->
+ <string name="overlay_not_overlayable">overlaid</string>
+
+ <!-- Requests to overlay a resource that is defined in an overlayable with a name other than
+ the targetName in the manifest. -->
+ <string name="overlay_other">overlaid</string>
</resources>
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_policies.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_policies.xml
new file mode 100644
index 0000000..747f448
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_policies.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<overlay>
+ <item target="string/policy_system" value="@string/overlay_policy_system"/>
+ <item target="string/policy_system_vendor" value="@string/overlay_policy_system_vendor" />
+ <item target="string/policy_public" value="@string/overlay_policy_public" />
+ <item target="string/policy_product" value="@string/overlay_policy_product"/>
+ <item target="string/policy_signature" value="@string/overlay_policy_signature" />
+ <item target="string/policy_odm" value="@string/overlay_policy_odm" />
+ <item target="string/policy_oem" value="@string/overlay_policy_oem"/>
+ <item target="string/policy_actor" value="@string/overlay_policy_actor" />
+ <item target="string/policy_config_signature" value="@string/overlay_policy_config_signature" />
+
+ <!-- Requests to overlay a resource that is not declared as overlayable. -->
+ <item target="string/not_overlayable" value="@string/overlay_not_overlayable" />
+
+ <!-- Requests to overlay a resource that is defined in an overlayable with a name other than
+ the targetName in the manifest. -->
+ <item target="string/other" value="@string/overlay_other" />
+</overlay>
\ No newline at end of file
diff --git a/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml
deleted file mode 100644
index 5df0bea..0000000
--- a/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="test.overlay.system">
- <application android:hasCode="false"/>
- <overlay
- android:targetPackage="test.target"
- android:targetName="TestResources"
- android:isStatic="true"
- android:priority="10"/>
-</manifest>
diff --git a/cmds/idmap2/tests/data/signature-overlay/build b/cmds/idmap2/tests/data/signature-overlay/build
deleted file mode 100755
index fdd8301..0000000
--- a/cmds/idmap2/tests/data/signature-overlay/build
+++ /dev/null
@@ -1,26 +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.
-
-FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
-
-aapt2 compile --dir res -o compiled.flata
-
-aapt2 link \
- --no-resource-removal \
- -I "$FRAMEWORK_RES_APK" \
- --manifest AndroidManifest.xml \
- -o signature-overlay.apk \
- compiled.flata
-
-rm compiled.flata
diff --git a/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml b/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml
deleted file mode 100644
index 59e7d8e..0000000
--- a/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <!-- This overlay will fulfill the policy "signature". This allows it overlay the
- following resources. -->
- <string name="policy_signature">policy_signature</string>
-</resources>
diff --git a/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk b/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk
deleted file mode 100644
index e0fd204..0000000
--- a/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk
+++ /dev/null
Binary files differ
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml b/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml
deleted file mode 100644
index c7b652c..0000000
--- a/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="test.overlay.system.invalid">
- <application android:hasCode="false"/>
- <overlay
- android:targetPackage="test.target"
- android:targetName="TestResources"/>
-</manifest>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/build b/cmds/idmap2/tests/data/system-overlay-invalid/build
deleted file mode 100755
index 920e1f8..0000000
--- a/cmds/idmap2/tests/data/system-overlay-invalid/build
+++ /dev/null
@@ -1,26 +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.
-
-FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
-
-aapt2 compile --dir res -o compiled.flata
-
-aapt2 link \
- --no-resource-removal \
- -I "$FRAMEWORK_RES_APK" \
- --manifest AndroidManifest.xml \
- -o system-overlay-invalid.apk \
- compiled.flata
-
-rm compiled.flata
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
deleted file mode 100644
index ebaf49c..0000000
--- a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <!-- This overlay will fulfill the policies "public|system". This allows it overlay the
- following resources. -->
- <string name="policy_system">policy_system</string>
- <string name="policy_system_vendor">policy_system_vendor</string>
- <string name="policy_public">policy_public</string>
-
- <!-- Requests to overlay a resource that belongs to a policy the overlay does not fulfill. -->
- <string name="policy_product">policy_product</string>
- <string name="policy_signature">policy_signature</string>
- <string name="policy_odm">policy_odm</string>
- <string name="policy_oem">policy_oem</string>
- <string name="policy_actor">policy_actor</string>
- <string name="policy_config_signature">policy_config_signature</string>
-
- <!-- Requests to overlay a resource that is not declared as overlayable. -->
- <string name="not_overlayable">not_overlayable</string>
-
- <!-- Requests to overlay a resource that is defined in an overlayable with a name other than
- the targetName in the manifest. -->
- <string name="other">other</string>
-</resources>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
deleted file mode 100644
index a63daf8..0000000
--- a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
+++ /dev/null
Binary files differ
diff --git a/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml
deleted file mode 100644
index 9e6a453..0000000
--- a/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="test.overlay.system">
- <application android:hasCode="false"/>
- <overlay
- android:targetPackage="test.target"
- android:targetName="TestResources"/>
-</manifest>
diff --git a/cmds/idmap2/tests/data/system-overlay/build b/cmds/idmap2/tests/data/system-overlay/build
deleted file mode 100755
index be0d239..0000000
--- a/cmds/idmap2/tests/data/system-overlay/build
+++ /dev/null
@@ -1,26 +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.
-
-FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
-
-aapt2 compile --dir res -o compiled.flata
-
-aapt2 link \
- --no-resource-removal \
- -I "$FRAMEWORK_RES_APK" \
- --manifest AndroidManifest.xml \
- -o system-overlay.apk \
- compiled.flata
-
-rm compiled.flata
diff --git a/cmds/idmap2/tests/data/system-overlay/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay/res/values/values.xml
deleted file mode 100644
index 6aaa0b0..0000000
--- a/cmds/idmap2/tests/data/system-overlay/res/values/values.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <!-- This overlay will fulfill the policies "public|system". This allows it overlay the
- following resources. -->
- <string name="policy_system">policy_system</string>
- <string name="policy_system_vendor">policy_system_vendor</string>
- <string name="policy_public">policy_public</string>
-</resources>
diff --git a/cmds/idmap2/tests/data/system-overlay/system-overlay.apk b/cmds/idmap2/tests/data/system-overlay/system-overlay.apk
deleted file mode 100644
index 90d2803..0000000
--- a/cmds/idmap2/tests/data/system-overlay/system-overlay.apk
+++ /dev/null
Binary files differ
diff --git a/config/hiddenapi-temp-blocklist.txt b/config/hiddenapi-temp-blocklist.txt
index 246eeea..753bc69b 100644
--- a/config/hiddenapi-temp-blocklist.txt
+++ b/config/hiddenapi-temp-blocklist.txt
@@ -47,9 +47,6 @@
Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z
Lcom/android/internal/appwidget/IAppWidgetService$Stub;->TRANSACTION_bindAppWidgetId:I
-Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
-Lcom/android/internal/location/ILocationProviderManager$Stub;-><init>()V
-Lcom/android/internal/location/ILocationProviderManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProviderManager;
Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I
Lcom/android/internal/widget/IRemoteViewsFactory$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/widget/IRemoteViewsFactory;
diff --git a/config/hiddenapi-unsupported.txt b/config/hiddenapi-unsupported.txt
index 8a377ac..90a526b 100644
--- a/config/hiddenapi-unsupported.txt
+++ b/config/hiddenapi-unsupported.txt
@@ -269,7 +269,6 @@
Lcom/android/internal/app/IVoiceInteractionManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IVoiceInteractionManagerService;
Lcom/android/internal/appwidget/IAppWidgetService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/appwidget/IAppWidgetService;
Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
-Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
Lcom/android/internal/os/IDropBoxManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/os/IDropBoxManagerService;
Lcom/android/internal/policy/IKeyguardService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardService;
Lcom/android/internal/policy/IKeyguardStateCallback$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardStateCallback;
diff --git a/core/api/current.txt b/core/api/current.txt
index 4ad5e49..32134fc 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7135,7 +7135,7 @@
field public static final String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
field public static final String ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED";
field public static final String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL";
- field public static final String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
+ field @Deprecated public static final String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
field public static final String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE";
field public static final String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
field public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
@@ -7186,7 +7186,7 @@
field public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER";
field public static final String EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS = "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS";
field public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
- field public static final String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT";
+ field @Deprecated public static final String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT";
field public static final String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
field public static final String EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY = "android.app.extra.PROVISIONING_WIFI_ANONYMOUS_IDENTITY";
field public static final String EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_CA_CERTIFICATE";
@@ -7492,6 +7492,7 @@
method public int getMaxTextEms();
method public int getMaxTextLength();
method public int getMinTextEms();
+ method @Nullable public String[] getOnReceiveContentMimeTypes();
method public int getScrollX();
method public int getScrollY();
method @Nullable public CharSequence getText();
@@ -7826,6 +7827,52 @@
}
+package android.app.people {
+
+ public final class ConversationStatus implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getActivity();
+ method public int getAvailability();
+ method @Nullable public CharSequence getDescription();
+ method public long getEndTimeMillis();
+ method @Nullable public android.graphics.drawable.Icon getIcon();
+ method @NonNull public String getId();
+ method public long getStartTimeMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int ACTIVITY_ANNIVERSARY = 2; // 0x2
+ field public static final int ACTIVITY_BIRTHDAY = 1; // 0x1
+ field public static final int ACTIVITY_GAME = 5; // 0x5
+ field public static final int ACTIVITY_LOCATION = 6; // 0x6
+ field public static final int ACTIVITY_MEDIA = 4; // 0x4
+ field public static final int ACTIVITY_NEW_STORY = 3; // 0x3
+ field public static final int ACTIVITY_OTHER = 0; // 0x0
+ field public static final int ACTIVITY_UPCOMING_BIRTHDAY = 7; // 0x7
+ field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
+ field public static final int AVAILABILITY_BUSY = 1; // 0x1
+ field public static final int AVAILABILITY_OFFLINE = 2; // 0x2
+ field public static final int AVAILABILITY_UNKNOWN = -1; // 0xffffffff
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.people.ConversationStatus> CREATOR;
+ }
+
+ public static final class ConversationStatus.Builder {
+ ctor public ConversationStatus.Builder(@NonNull String, @NonNull int);
+ method @NonNull public android.app.people.ConversationStatus build();
+ method @NonNull public android.app.people.ConversationStatus.Builder setAvailability(int);
+ method @NonNull public android.app.people.ConversationStatus.Builder setDescription(@Nullable CharSequence);
+ method @NonNull public android.app.people.ConversationStatus.Builder setEndTimeMillis(long);
+ method @NonNull public android.app.people.ConversationStatus.Builder setIcon(@Nullable android.graphics.drawable.Icon);
+ method @NonNull public android.app.people.ConversationStatus.Builder setStartTimeMillis(long);
+ }
+
+ public final class PeopleManager {
+ method public void addOrUpdateStatus(@NonNull String, @NonNull android.app.people.ConversationStatus);
+ method public void clearStatus(@NonNull String, @NonNull String);
+ method public void clearStatuses(@NonNull String);
+ method @NonNull public java.util.List<android.app.people.ConversationStatus> getStatuses(@NonNull String);
+ }
+
+}
+
package android.app.role {
public final class RoleManager {
@@ -10315,6 +10362,7 @@
field public static final String NFC_SERVICE = "nfc";
field public static final String NOTIFICATION_SERVICE = "notification";
field public static final String NSD_SERVICE = "servicediscovery";
+ field public static final String PEOPLE_SERVICE = "people";
field public static final String POWER_SERVICE = "power";
field public static final String PRINT_SERVICE = "print";
field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
@@ -19320,6 +19368,7 @@
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean addNmeaListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.OnNmeaMessageListener);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void addProximityAlert(double, double, float, long, @NonNull android.app.PendingIntent);
method public void addTestProvider(@NonNull String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
+ method public void addTestProvider(@NonNull String, @NonNull android.location.provider.ProviderProperties);
method @Deprecated public void clearTestProviderEnabled(@NonNull String);
method @Deprecated public void clearTestProviderLocation(@NonNull String);
method @Deprecated public void clearTestProviderStatus(@NonNull String);
@@ -19334,7 +19383,7 @@
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.location.GpsStatus getGpsStatus(@Nullable android.location.GpsStatus);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.location.Location getLastKnownLocation(@NonNull String);
method @Deprecated @Nullable public android.location.LocationProvider getProvider(@NonNull String);
- method @Nullable public android.location.ProviderProperties getProviderProperties(@NonNull String);
+ method @Nullable public android.location.provider.ProviderProperties getProviderProperties(@NonNull String);
method @NonNull public java.util.List<java.lang.String> getProviders(boolean);
method @NonNull public java.util.List<java.lang.String> getProviders(@NonNull android.location.Criteria, boolean);
method public boolean hasProvider(@NonNull String);
@@ -19466,6 +19515,24 @@
method public void onNmeaMessage(String, long);
}
+ public abstract class SettingInjectorService extends android.app.Service {
+ ctor public SettingInjectorService(String);
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method protected abstract boolean onGetEnabled();
+ method protected abstract String onGetSummary();
+ method public final void onStart(android.content.Intent, int);
+ method public final int onStartCommand(android.content.Intent, int, int);
+ method public static final void refreshSettings(@NonNull android.content.Context);
+ field public static final String ACTION_INJECTED_SETTING_CHANGED = "android.location.InjectedSettingChanged";
+ field public static final String ACTION_SERVICE_INTENT = "android.location.SettingInjectorService";
+ field public static final String ATTRIBUTES_NAME = "injected-location-setting";
+ field public static final String META_DATA_NAME = "android.location.SettingInjectorService";
+ }
+
+}
+
+package android.location.provider {
+
public final class ProviderProperties implements android.os.Parcelable {
method public int describeContents();
method public int getAccuracy();
@@ -19480,24 +19547,25 @@
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int ACCURACY_COARSE = 2; // 0x2
field public static final int ACCURACY_FINE = 1; // 0x1
- field @NonNull public static final android.os.Parcelable.Creator<android.location.ProviderProperties> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.provider.ProviderProperties> CREATOR;
field public static final int POWER_USAGE_HIGH = 3; // 0x3
field public static final int POWER_USAGE_LOW = 1; // 0x1
field public static final int POWER_USAGE_MEDIUM = 2; // 0x2
}
- public abstract class SettingInjectorService extends android.app.Service {
- ctor public SettingInjectorService(String);
- method public final android.os.IBinder onBind(android.content.Intent);
- method protected abstract boolean onGetEnabled();
- method protected abstract String onGetSummary();
- method public final void onStart(android.content.Intent, int);
- method public final int onStartCommand(android.content.Intent, int, int);
- method public static final void refreshSettings(@NonNull android.content.Context);
- field public static final String ACTION_INJECTED_SETTING_CHANGED = "android.location.InjectedSettingChanged";
- field public static final String ACTION_SERVICE_INTENT = "android.location.SettingInjectorService";
- field public static final String ATTRIBUTES_NAME = "injected-location-setting";
- field public static final String META_DATA_NAME = "android.location.SettingInjectorService";
+ public static final class ProviderProperties.Builder {
+ ctor public ProviderProperties.Builder();
+ ctor public ProviderProperties.Builder(@NonNull android.location.provider.ProviderProperties);
+ method @NonNull public android.location.provider.ProviderProperties build();
+ method @NonNull public android.location.provider.ProviderProperties.Builder setAccuracy(int);
+ method @NonNull public android.location.provider.ProviderProperties.Builder setHasAltitudeSupport(boolean);
+ method @NonNull public android.location.provider.ProviderProperties.Builder setHasBearingSupport(boolean);
+ method @NonNull public android.location.provider.ProviderProperties.Builder setHasCellRequirement(boolean);
+ method @NonNull public android.location.provider.ProviderProperties.Builder setHasMonetaryCost(boolean);
+ method @NonNull public android.location.provider.ProviderProperties.Builder setHasNetworkRequirement(boolean);
+ method @NonNull public android.location.provider.ProviderProperties.Builder setHasSatelliteRequirement(boolean);
+ method @NonNull public android.location.provider.ProviderProperties.Builder setHasSpeedSupport(boolean);
+ method @NonNull public android.location.provider.ProviderProperties.Builder setPowerUsage(int);
}
}
@@ -39775,6 +39843,8 @@
method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
method public void notifyConfigChangedForSubId(int);
field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ field public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY = 0; // 0x0
+ field public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING = 1; // 0x1
field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1; // 0xffffffff
field public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool";
@@ -39816,6 +39886,7 @@
field public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY = "carrier_certificate_string_array";
field public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool";
field public static final String KEY_CARRIER_CONFIG_VERSION_STRING = "carrier_config_version_string";
+ field public static final String KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL = "carrier_cross_sim_ims_available_bool";
field public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = "carrier_data_call_permanent_failure_strings";
field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY = "carrier_default_actions_on_dcfailure_string_array";
field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE = "carrier_default_actions_on_default_network_available_string_array";
@@ -39867,6 +39938,7 @@
field public static final String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
field public static final String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool";
field public static final String KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm";
+ field public static final String KEY_CROSS_SIM_SPN_FORMAT_INT = "cross_sim_spn_format_int";
field public static final String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_bool";
field public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
@@ -48693,6 +48765,7 @@
method public void setMaxTextEms(int);
method public void setMaxTextLength(int);
method public void setMinTextEms(int);
+ method public void setOnReceiveContentMimeTypes(@Nullable String[]);
method public abstract void setOpaque(boolean);
method public abstract void setSelected(boolean);
method public abstract void setText(CharSequence);
@@ -54046,7 +54119,7 @@
ctor public RemoteViews(android.widget.RemoteViews, android.widget.RemoteViews);
ctor public RemoteViews(android.widget.RemoteViews);
ctor public RemoteViews(android.os.Parcel);
- method public void addView(int, android.widget.RemoteViews);
+ method public void addView(@IdRes int, android.widget.RemoteViews);
method public android.view.View apply(android.content.Context, android.view.ViewGroup);
method @Deprecated public android.widget.RemoteViews clone();
method public int describeContents();
@@ -54054,53 +54127,53 @@
method public String getPackage();
method @Deprecated public boolean onLoadClass(Class);
method public void reapply(android.content.Context, android.view.View);
- method public void removeAllViews(int);
- method public void setAccessibilityTraversalAfter(int, int);
- method public void setAccessibilityTraversalBefore(int, int);
- method public void setBitmap(int, String, android.graphics.Bitmap);
- method public void setBoolean(int, String, boolean);
- method public void setBundle(int, String, android.os.Bundle);
- method public void setByte(int, String, byte);
- method public void setChar(int, String, char);
- method public void setCharSequence(int, String, CharSequence);
- method public void setChronometer(int, long, String, boolean);
- method public void setChronometerCountDown(int, boolean);
- method public void setContentDescription(int, CharSequence);
- method public void setDisplayedChild(int, int);
- method public void setDouble(int, String, double);
- method public void setEmptyView(int, int);
- method public void setFloat(int, String, float);
- method public void setIcon(int, String, android.graphics.drawable.Icon);
- method public void setImageViewBitmap(int, android.graphics.Bitmap);
- method public void setImageViewIcon(int, android.graphics.drawable.Icon);
- method public void setImageViewResource(int, int);
- method public void setImageViewUri(int, android.net.Uri);
- method public void setInt(int, String, int);
- method public void setIntent(int, String, android.content.Intent);
- method public void setLabelFor(int, int);
+ method public void removeAllViews(@IdRes int);
+ method public void setAccessibilityTraversalAfter(@IdRes int, @IdRes int);
+ method public void setAccessibilityTraversalBefore(@IdRes int, @IdRes int);
+ method public void setBitmap(@IdRes int, String, android.graphics.Bitmap);
+ method public void setBoolean(@IdRes int, String, boolean);
+ method public void setBundle(@IdRes int, String, android.os.Bundle);
+ method public void setByte(@IdRes int, String, byte);
+ method public void setChar(@IdRes int, String, char);
+ method public void setCharSequence(@IdRes int, String, CharSequence);
+ method public void setChronometer(@IdRes int, long, String, boolean);
+ method public void setChronometerCountDown(@IdRes int, boolean);
+ method public void setContentDescription(@IdRes int, CharSequence);
+ method public void setDisplayedChild(@IdRes int, int);
+ method public void setDouble(@IdRes int, String, double);
+ method public void setEmptyView(@IdRes int, @IdRes int);
+ method public void setFloat(@IdRes int, String, float);
+ method public void setIcon(@IdRes int, String, android.graphics.drawable.Icon);
+ method public void setImageViewBitmap(@IdRes int, android.graphics.Bitmap);
+ method public void setImageViewIcon(@IdRes int, android.graphics.drawable.Icon);
+ method public void setImageViewResource(@IdRes int, @DrawableRes int);
+ method public void setImageViewUri(@IdRes int, android.net.Uri);
+ method public void setInt(@IdRes int, String, int);
+ method public void setIntent(@IdRes int, String, android.content.Intent);
+ method public void setLabelFor(@IdRes int, @IdRes int);
method public void setLightBackgroundLayoutId(@LayoutRes int);
- method public void setLong(int, String, long);
- method public void setOnClickFillInIntent(int, android.content.Intent);
- method public void setOnClickPendingIntent(int, android.app.PendingIntent);
- method public void setOnClickResponse(int, @NonNull android.widget.RemoteViews.RemoteResponse);
- method public void setPendingIntentTemplate(int, android.app.PendingIntent);
- method public void setProgressBar(int, int, int, boolean);
- method public void setRelativeScrollPosition(int, int);
- method @Deprecated public void setRemoteAdapter(int, int, android.content.Intent);
- method public void setRemoteAdapter(int, android.content.Intent);
- method public void setScrollPosition(int, int);
- method public void setShort(int, String, short);
- method public void setString(int, String, String);
- method public void setTextColor(int, @ColorInt int);
- method public void setTextViewCompoundDrawables(int, int, int, int, int);
- method public void setTextViewCompoundDrawablesRelative(int, int, int, int, int);
- method public void setTextViewText(int, CharSequence);
- method public void setTextViewTextSize(int, int, float);
- method public void setUri(int, String, android.net.Uri);
- method public void setViewPadding(int, int, int, int, int);
- method public void setViewVisibility(int, int);
- method public void showNext(int);
- method public void showPrevious(int);
+ method public void setLong(@IdRes int, String, long);
+ method public void setOnClickFillInIntent(@IdRes int, android.content.Intent);
+ method public void setOnClickPendingIntent(@IdRes int, android.app.PendingIntent);
+ method public void setOnClickResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse);
+ method public void setPendingIntentTemplate(@IdRes int, android.app.PendingIntent);
+ method public void setProgressBar(@IdRes int, int, int, boolean);
+ method public void setRelativeScrollPosition(@IdRes int, int);
+ method @Deprecated public void setRemoteAdapter(int, @IdRes int, android.content.Intent);
+ method public void setRemoteAdapter(@IdRes int, android.content.Intent);
+ method public void setScrollPosition(@IdRes int, int);
+ method public void setShort(@IdRes int, String, short);
+ method public void setString(@IdRes int, String, String);
+ method public void setTextColor(@IdRes int, @ColorInt int);
+ method public void setTextViewCompoundDrawables(@IdRes int, @DrawableRes int, @DrawableRes int, @DrawableRes int, @DrawableRes int);
+ method public void setTextViewCompoundDrawablesRelative(@IdRes int, @DrawableRes int, @DrawableRes int, @DrawableRes int, @DrawableRes int);
+ method public void setTextViewText(@IdRes int, CharSequence);
+ method public void setTextViewTextSize(@IdRes int, int, float);
+ method public void setUri(@IdRes int, String, android.net.Uri);
+ method public void setViewPadding(@IdRes int, @Px int, @Px int, @Px int, @Px int);
+ method public void setViewVisibility(@IdRes int, int);
+ method public void showNext(@IdRes int);
+ method public void showPrevious(@IdRes int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.widget.RemoteViews> CREATOR;
field public static final String EXTRA_SHARED_ELEMENT_BOUNDS = "android.widget.extra.SHARED_ELEMENT_BOUNDS";
@@ -54113,7 +54186,7 @@
public static class RemoteViews.RemoteResponse {
ctor public RemoteViews.RemoteResponse();
- method @NonNull public android.widget.RemoteViews.RemoteResponse addSharedElement(int, @NonNull String);
+ method @NonNull public android.widget.RemoteViews.RemoteResponse addSharedElement(@IdRes int, @NonNull String);
method @NonNull public static android.widget.RemoteViews.RemoteResponse fromFillInIntent(@NonNull android.content.Intent);
method @NonNull public static android.widget.RemoteViews.RemoteResponse fromPendingIntent(@NonNull android.app.PendingIntent);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 36b565b..1f08d96 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1370,8 +1370,6 @@
method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
@@ -1807,6 +1805,7 @@
field @NonNull public static final android.os.ParcelUuid AVRCP_TARGET;
field @NonNull public static final android.os.ParcelUuid BASE_UUID;
field @NonNull public static final android.os.ParcelUuid BNEP;
+ field @NonNull public static final android.os.ParcelUuid DIP;
field @NonNull public static final android.os.ParcelUuid HEARING_AID;
field @NonNull public static final android.os.ParcelUuid HFP;
field @NonNull public static final android.os.ParcelUuid HFP_AG;
@@ -4381,6 +4380,56 @@
}
+package android.location.provider {
+
+ public abstract class LocationProviderBase {
+ ctor public LocationProviderBase(@NonNull android.content.Context, @NonNull String, @NonNull android.location.provider.ProviderProperties);
+ method @Nullable public final android.os.IBinder getBinder();
+ method @NonNull public android.location.provider.ProviderProperties getProperties();
+ method public boolean isAllowed();
+ method public abstract void onFlush(@NonNull android.location.provider.LocationProviderBase.OnFlushCompleteCallback);
+ method public abstract void onSendExtraCommand(@NonNull String, @Nullable android.os.Bundle);
+ method public abstract void onSetRequest(@NonNull android.location.provider.ProviderRequest);
+ method public void reportLocation(@NonNull android.location.Location);
+ method public void reportLocation(@NonNull android.location.LocationResult);
+ method public void setAllowed(boolean);
+ method public void setProperties(@NonNull android.location.provider.ProviderProperties);
+ field public static final String ACTION_FUSED_PROVIDER = "com.android.location.service.FusedLocationProvider";
+ field public static final String ACTION_NETWORK_PROVIDER = "com.android.location.service.v3.NetworkLocationProvider";
+ }
+
+ public static interface LocationProviderBase.OnFlushCompleteCallback {
+ method public void onFlushComplete();
+ }
+
+ public final class ProviderRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0) public long getIntervalMillis();
+ method @IntRange(from=0) public long getMaxUpdateDelayMillis();
+ method public int getQuality();
+ method @NonNull public android.os.WorkSource getWorkSource();
+ method public boolean isActive();
+ method public boolean isLocationSettingsIgnored();
+ method public boolean isLowPower();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.provider.ProviderRequest> CREATOR;
+ field @NonNull public static final android.location.provider.ProviderRequest EMPTY_REQUEST;
+ field public static final long INTERVAL_DISABLED = 9223372036854775807L; // 0x7fffffffffffffffL
+ }
+
+ public static final class ProviderRequest.Builder {
+ ctor public ProviderRequest.Builder();
+ method @NonNull public android.location.provider.ProviderRequest build();
+ method @NonNull public android.location.provider.ProviderRequest.Builder setIntervalMillis(@IntRange(from=0) long);
+ method @NonNull public android.location.provider.ProviderRequest.Builder setLocationSettingsIgnored(boolean);
+ method @NonNull public android.location.provider.ProviderRequest.Builder setLowPower(boolean);
+ method @NonNull public android.location.provider.ProviderRequest.Builder setMaxUpdateDelayMillis(@IntRange(from=0) long);
+ method @NonNull public android.location.provider.ProviderRequest.Builder setQuality(int);
+ method @NonNull public android.location.provider.ProviderRequest.Builder setWorkSource(@NonNull android.os.WorkSource);
+ }
+
+}
+
package android.media {
public final class AudioAttributes implements android.os.Parcelable {
@@ -8567,6 +8616,7 @@
field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot";
field public static final String NAMESPACE_APP_COMPAT = "app_compat";
+ field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
field public static final String NAMESPACE_AUTOFILL = "autofill";
field public static final String NAMESPACE_BATTERY_SAVER = "battery_saver";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d226d625..c03461a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -18,6 +18,7 @@
field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES";
field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
+ field public static final String MANAGE_TOAST_RATE_LIMITING = "android.permission.MANAGE_TOAST_RATE_LIMITING";
field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE";
field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
@@ -138,7 +139,6 @@
method public static boolean currentUiModeSupportsErrorDialogs(@NonNull android.content.Context);
method public static int getMaxNumPictureInPictureActions(@NonNull android.content.Context);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void moveTaskToRootTask(int, int, boolean);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean moveTopActivityToPinnedRootTask(int, @NonNull android.graphics.Rect);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void removeRootTasksInWindowingModes(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void removeRootTasksWithActivityTypes(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void resizePrimarySplitScreen(@NonNull android.graphics.Rect, @NonNull android.graphics.Rect);
@@ -275,9 +275,14 @@
method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String);
method public boolean matchesCallFilter(android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) public void setToastRateLimitingEnabled(boolean);
method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel);
}
+ public final class PendingIntent implements android.os.Parcelable {
+ field @Deprecated public static final int FLAG_MUTABLE_UNAUDITED = 33554432; // 0x2000000
+ }
+
public final class PictureInPictureParams implements android.os.Parcelable {
method public java.util.List<android.app.RemoteAction> getActions();
method public float getAspectRatio();
@@ -292,7 +297,9 @@
}
public class TaskInfo {
+ method public boolean containsLaunchCookie(@NonNull android.os.IBinder);
method @NonNull public android.content.res.Configuration getConfiguration();
+ method public int getParentTaskId();
method @Nullable public android.app.PictureInPictureParams getPictureInPictureParams();
method @NonNull public android.window.WindowContainerToken getToken();
method public boolean hasParentTask();
@@ -442,6 +449,11 @@
package android.app.role {
+ public class RoleControllerManager {
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ }
+
public final class RoleManager {
method @Nullable public String getSmsRoleHolder(int);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 55df824..55b858e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5151,12 +5151,6 @@
* #checkSelfPermission(String)}.
* </p>
* <p>
- * Calling this API for permissions already granted to your app would show UI
- * to the user to decide whether the app can still hold these permissions. This
- * can be useful if the way your app uses data guarded by the permissions
- * changes significantly.
- * </p>
- * <p>
* You cannot request a permission if your activity sets {@link
* android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
* <code>true</code> because in this case the activity would not receive
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index fbc3b0d..70fa444 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -295,21 +295,6 @@
}
/**
- * Moves the top activity in the input rootTaskId to the pinned root task.
- * @param rootTaskId Id of root task to move the top activity to pinned root task.
- * @param bounds Bounds to use for pinned root task.
- * @return True if the top activity of root task was successfully moved to the pinned root task.
- */
- @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
- public boolean moveTopActivityToPinnedRootTask(int rootTaskId, @NonNull Rect bounds) {
- try {
- return getService().moveTopActivityToPinnedRootTask(rootTaskId, bounds);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Start to enter lock task mode for given task by system(UI).
* @param taskId Id of task to lock.
*/
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 94b2118..1b8f049 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -508,7 +508,6 @@
boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void suppressResizeConfigChanges(boolean suppress);
- boolean moveTopActivityToPinnedRootTask(int rootTaskId, in Rect bounds);
boolean isAppStartModeDisabled(int uid, in String packageName);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean unlockUser(int userid, in byte[] token, in byte[] secret,
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 1d65711..b085340 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -257,7 +257,6 @@
void keyguardGoingAway(int flags);
void suppressResizeConfigChanges(boolean suppress);
- boolean moveTopActivityToPinnedRootTask(int rootTaskId, in Rect bounds);
/**
* Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 697a377..bda2fa9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -228,4 +228,6 @@
NotificationListenerFilter getListenerFilter(in ComponentName cn, int userId);
void setListenerFilter(in ComponentName cn, int userId, in NotificationListenerFilter nlf);
+
+ void setToastRateLimitingEnabled(boolean enable);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4c5044d..5fbc948 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -652,7 +652,8 @@
MessagingStyle.class);
/** @hide */
- @IntDef({FLAG_SHOW_LIGHTS, FLAG_ONGOING_EVENT, FLAG_INSISTENT, FLAG_ONLY_ALERT_ONCE,
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {FLAG_SHOW_LIGHTS, FLAG_ONGOING_EVENT,
+ FLAG_INSISTENT, FLAG_ONLY_ALERT_ONCE,
FLAG_AUTO_CANCEL, FLAG_NO_CLEAR, FLAG_FOREGROUND_SERVICE, FLAG_HIGH_PRIORITY,
FLAG_LOCAL_ONLY, FLAG_GROUP_SUMMARY, FLAG_AUTOGROUP_SUMMARY, FLAG_BUBBLE,
FLAG_IMMEDIATE_FGS_DISPLAY})
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 58f382d..6cce270 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1730,6 +1730,23 @@
}
/**
+ * Controls whether toast rate limiting is enabled for the calling uid.
+ *
+ * @param enable true to enable toast rate limiting, false to disable it
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING)
+ public void setToastRateLimitingEnabled(boolean enable) {
+ INotificationManager service = getService();
+ try {
+ service.setToastRateLimitingEnabled(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Notification policy configuration. Represents user-preferences for notification
* filtering.
*/
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 9dbf1ff6..602e9a3 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -221,6 +222,7 @@
* @hide
*/
@Deprecated
+ @TestApi
public static final int FLAG_MUTABLE_UNAUDITED = FLAG_MUTABLE;
/**
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 2ce0e87..050d194 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -29,7 +29,9 @@
import android.app.contentsuggestions.ContentSuggestionsManager;
import android.app.contentsuggestions.IContentSuggestionsManager;
import android.app.job.JobSchedulerFrameworkInitializer;
+import android.app.people.PeopleManager;
import android.app.prediction.AppPredictionManager;
+import android.app.role.RoleControllerManager;
import android.app.role.RoleManager;
import android.app.search.SearchUiManager;
import android.app.slice.SliceManager;
@@ -585,6 +587,13 @@
return new NsdManager(ctx.getOuterContext(), service);
}});
+ registerService(Context.PEOPLE_SERVICE, PeopleManager.class,
+ new CachedServiceFetcher<PeopleManager>() {
+ @Override
+ public PeopleManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ return new PeopleManager(ctx);
+ }});
+
registerService(Context.POWER_SERVICE, PowerManager.class,
new CachedServiceFetcher<PowerManager>() {
@Override
@@ -1290,6 +1299,14 @@
return new RoleManager(ctx.getOuterContext());
}});
+ registerService(Context.ROLE_CONTROLLER_SERVICE, RoleControllerManager.class,
+ new CachedServiceFetcher<RoleControllerManager>() {
+ @Override
+ public RoleControllerManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ return new RoleControllerManager(ctx.getOuterContext());
+ }});
+
registerService(Context.DYNAMIC_SYSTEM_SERVICE, DynamicSystemManager.class,
new CachedServiceFetcher<DynamicSystemManager>() {
@Override
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 61c4d39..623c878d 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -36,6 +36,7 @@
import android.window.WindowContainerToken;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -279,6 +280,24 @@
launchCookies.add(cookie);
}
+ /**
+ * @return {@code true} if this task contains the launch cookie.
+ * @hide
+ */
+ @TestApi
+ public boolean containsLaunchCookie(@NonNull IBinder cookie) {
+ return launchCookies.contains(cookie);
+ }
+
+ /**
+ * @return The parent task id of this task.
+ * @hide
+ */
+ @TestApi
+ public int getParentTaskId() {
+ return parentTaskId;
+ }
+
/** @hide */
@TestApi
public boolean hasParentTask() {
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 787393e..05872ba 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -438,8 +438,8 @@
* Adopt the permission identity of the shell UID for all permissions. This allows
* you to call APIs protected permissions which normal apps cannot hold but are
* granted to the shell UID. If you already adopted all shell permissions by calling
- * this method or {@link #adoptShellPermissionIdentity(String...)} a subsequent call
- * would be a no-op. Note that your permission state becomes that of the shell UID
+ * this method or {@link #adoptShellPermissionIdentity(String...)} a subsequent call will
+ * replace any previous adoption. Note that your permission state becomes that of the shell UID
* and it is not a combination of your and the shell UID permissions.
* <p>
* <strong>Note:<strong/> Calling this method adopts all shell permissions and overrides
@@ -460,13 +460,13 @@
/**
* Adopt the permission identity of the shell UID only for the provided permissions.
* This allows you to call APIs protected permissions which normal apps cannot hold
- * but are granted to the shell UID. If you already adopted the specified shell
- * permissions by calling this method or {@link #adoptShellPermissionIdentity()} a
- * subsequent call would be a no-op. Note that your permission state becomes that of the
- * shell UID and it is not a combination of your and the shell UID permissions.
+ * but are granted to the shell UID. If you already adopted shell permissions by calling
+ * this method, or {@link #adoptShellPermissionIdentity()} a subsequent call will replace any
+ * previous adoption.
* <p>
- * <strong>Note:<strong/> Calling this method adopts only the specified shell permissions
- * and overrides all adopted permissions via {@link #adoptShellPermissionIdentity()}.
+ * <strong>Note:<strong/> This method behave differently from
+ * {@link #adoptShellPermissionIdentity()}. Only the listed permissions will use the shell
+ * identity and other permissions will still check against the original UID
*
* @param permissions The permissions to adopt or <code>null</code> to adopt all.
*
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 806cb49..6b997f0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -306,7 +306,13 @@
* of the provisioning flow was successful, although this doesn't guarantee the full flow will
* succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies
* that the user backed-out of provisioning, or some precondition for provisioning wasn't met.
+ *
+ * @deprecated admin apps must now implement activities with intent filters for the {@link
+ * #ACTION_GET_PROVISIONING_MODE} and {@link #ACTION_ADMIN_POLICY_COMPLIANCE} intent actions;
+ * using {@link #ACTION_PROVISION_MANAGED_DEVICE} to start provisioning will cause the
+ * provisioning to fail.
*/
+ @Deprecated
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_PROVISION_MANAGED_DEVICE
= "android.app.action.PROVISION_MANAGED_DEVICE";
@@ -598,6 +604,12 @@
* properties will be converted into a {@link android.os.PersistableBundle} and passed to the
* management application after provisioning.
*
+ * <p>Admin apps will receive this extra in their {@link #ACTION_GET_PROVISIONING_MODE} and
+ * {@link #ACTION_ADMIN_POLICY_COMPLIANCE} intent handlers. Additionally, {@link
+ * #ACTION_GET_PROVISIONING_MODE} may also return this extra which will then be sent over to
+ * {@link #ACTION_ADMIN_POLICY_COMPLIANCE}, alongside the original values that were passed to
+ * {@link #ACTION_GET_PROVISIONING_MODE}.
+ *
* <p>
* In both cases the application receives the data in
* {@link DeviceAdminReceiver#onProfileProvisioningComplete} via an intent with the action
@@ -655,7 +667,9 @@
* profile provisioning. If the account supplied is present in the primary user, it will be
* copied, along with its credentials to the managed profile and removed from the primary user.
*
- * Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}.
+ * Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}, with managed account provisioning, or
+ * return as an extra to the intent result from the {@link #ACTION_GET_PROVISIONING_MODE}
+ * activity.
*/
public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE
@@ -669,8 +683,10 @@
*
* <p> Defaults to {@code false}
*
- * <p> Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} and
- * {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE}
+ * <p> Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} or set as an extra to the
+ * intent result of the {@link #ACTION_GET_PROVISIONING_MODE} activity.
+ *
+ * @see #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE
*/
public static final String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION
= "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION";
@@ -697,8 +713,9 @@
* A Boolean extra that can be used by the mobile device management application to skip the
* disabling of system apps during provisioning when set to {@code true}.
*
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} or an intent with action
- * {@link #ACTION_PROVISION_MANAGED_DEVICE} that starts device owner provisioning.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC}, an intent with action
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE} that starts profile owner provisioning or
+ * set as an extra to the intent result of the {@link #ACTION_GET_PROVISIONING_MODE} activity.
*/
public static final String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED =
"android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
@@ -1175,27 +1192,15 @@
"android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";
/**
- * A boolean extra indicating if user setup should be skipped, for when provisioning is started
- * during setup-wizard.
- *
- * <p>If unspecified, defaults to {@code true} to match the behavior in
- * {@link android.os.Build.VERSION_CODES#M} and earlier.
- *
- * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE} or
- * {@link #ACTION_PROVISION_MANAGED_USER}.
- *
- * @hide
- */
- public static final String EXTRA_PROVISIONING_SKIP_USER_SETUP =
- "android.app.extra.PROVISIONING_SKIP_USER_SETUP";
-
- /**
* A boolean extra indicating if the user consent steps from the provisioning flow should be
* skipped. If unspecified, defaults to {@code false}.
*
* It can only be used by an existing device owner trying to create a managed profile via
* {@link #ACTION_PROVISION_MANAGED_PROFILE}. Otherwise it is ignored.
+ *
+ * @deprecated this extra is no longer relevant as device owners cannot create managed profiles
*/
+ @Deprecated
public static final String EXTRA_PROVISIONING_SKIP_USER_CONSENT =
"android.app.extra.PROVISIONING_SKIP_USER_CONSENT";
@@ -2456,6 +2461,16 @@
* <p>The target activity may also return the account that needs to be migrated from primary
* user to managed profile in case of a profile owner provisioning in
* {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} as result.
+ *
+ * <p>The target activity may also include the {@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}
+ * extra in the intent result. The values of this {@link android.os.PersistableBundle} will be
+ * sent as an intent extra of the same name to the {@link #ACTION_ADMIN_POLICY_COMPLIANCE}
+ * activity, along with the values of the {@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE} extra
+ * that are already supplied to this activity.
+ *
+ * @see #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION
+ * @see #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED
+ * @see #ACTION_ADMIN_POLICY_COMPLIANCE
*/
public static final String ACTION_GET_PROVISIONING_MODE =
"android.app.action.GET_PROVISIONING_MODE";
@@ -2545,6 +2560,11 @@
* provisioning flow (in which case this gets sent after {@link #ACTION_GET_PROVISIONING_MODE}),
* or it could happen during provisioning finalization if the administrator supports
* finalization during setup wizard.
+ *
+ * <p>Intents with this action may also be supplied with the {@link
+ * #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE} extra.
+ *
+ * @see #ACTION_GET_PROVISIONING_MODE
*/
public static final String ACTION_ADMIN_POLICY_COMPLIANCE =
"android.app.action.ADMIN_POLICY_COMPLIANCE";
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 0f7fac4..65a2164 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -672,6 +672,7 @@
static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
static final int FLAGS_OPAQUE = 0x00008000;
+ static final int FLAGS_HAS_MIME_TYPES = 0x80000000;
static final int FLAGS_HAS_MATRIX = 0x40000000;
static final int FLAGS_HAS_ALPHA = 0x20000000;
static final int FLAGS_HAS_ELEVATION = 0x10000000;
@@ -715,6 +716,7 @@
String mWebDomain;
Bundle mExtras;
LocaleList mLocaleList;
+ String[] mOnReceiveContentMimeTypes;
ViewNode[] mChildren;
@@ -880,6 +882,9 @@
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
mLocaleList = in.readParcelable(null);
}
+ if ((flags & FLAGS_HAS_MIME_TYPES) != 0) {
+ mOnReceiveContentMimeTypes = in.readStringArray();
+ }
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
mExtras = in.readBundle();
}
@@ -939,6 +944,9 @@
if (mLocaleList != null) {
flags |= FLAGS_HAS_LOCALE_LIST;
}
+ if (mOnReceiveContentMimeTypes != null) {
+ flags |= FLAGS_HAS_MIME_TYPES;
+ }
if (mExtras != null) {
flags |= FLAGS_HAS_EXTRAS;
}
@@ -1109,6 +1117,9 @@
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
out.writeParcelable(mLocaleList, 0);
}
+ if ((flags & FLAGS_HAS_MIME_TYPES) != 0) {
+ out.writeStringArray(mOnReceiveContentMimeTypes);
+ }
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
out.writeBundle(mExtras);
}
@@ -1527,6 +1538,15 @@
}
/**
+ * Returns the MIME types accepted by {@link View#performReceiveContent} for this view. See
+ * {@link View#getOnReceiveContentMimeTypes()} for details.
+ */
+ @Nullable
+ public String[] getOnReceiveContentMimeTypes() {
+ return mOnReceiveContentMimeTypes;
+ }
+
+ /**
* Returns any text associated with the node that is displayed to the user, or null
* if there is none.
*/
@@ -2146,6 +2166,11 @@
}
@Override
+ public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) {
+ mNode.mOnReceiveContentMimeTypes = mimeTypes;
+ }
+
+ @Override
public void setInputType(int inputType) {
mNode.mInputType = inputType;
}
@@ -2422,6 +2447,10 @@
if (localeList != null) {
Log.i(TAG, prefix + " LocaleList: " + localeList);
}
+ String[] mimeTypes = node.getOnReceiveContentMimeTypes();
+ if (mimeTypes != null) {
+ Log.i(TAG, prefix + " MIME types: " + Arrays.toString(mimeTypes));
+ }
String hint = node.getHint();
if (hint != null) {
Log.i(TAG, prefix + " Hint: " + hint);
diff --git a/location/java/android/location/ProviderProperties.aidl b/core/java/android/app/people/ConversationStatus.aidl
similarity index 82%
copy from location/java/android/location/ProviderProperties.aidl
copy to core/java/android/app/people/ConversationStatus.aidl
index 8b1d79f..acfe135 100644
--- a/location/java/android/location/ProviderProperties.aidl
+++ b/core/java/android/app/people/ConversationStatus.aidl
@@ -1,5 +1,5 @@
-/*
- * Copyright (C) 2012, The Android Open Source Project
+/**
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.location;
+package android.app.people;
-parcelable ProviderProperties;
+parcelable ConversationStatus;
\ No newline at end of file
diff --git a/core/java/android/app/people/ConversationStatus.java b/core/java/android/app/people/ConversationStatus.java
new file mode 100644
index 0000000..d2a0255
--- /dev/null
+++ b/core/java/android/app/people/ConversationStatus.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.people;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+public final class ConversationStatus implements Parcelable {
+ private static final String TAG = "ConversationStatus";
+
+ /** @hide */
+ @IntDef(prefix = { "ACTIVITY_" }, value = {
+ ACTIVITY_OTHER,
+ ACTIVITY_BIRTHDAY,
+ ACTIVITY_ANNIVERSARY,
+ ACTIVITY_NEW_STORY,
+ ACTIVITY_MEDIA,
+ ACTIVITY_GAME,
+ ACTIVITY_LOCATION,
+ ACTIVITY_UPCOMING_BIRTHDAY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ActivityType {}
+
+ public static final int ACTIVITY_OTHER = 0;
+ public static final int ACTIVITY_BIRTHDAY = 1;
+ public static final int ACTIVITY_ANNIVERSARY = 2;
+ public static final int ACTIVITY_NEW_STORY = 3;
+ public static final int ACTIVITY_MEDIA = 4;
+ public static final int ACTIVITY_GAME = 5;
+ public static final int ACTIVITY_LOCATION = 6;
+ public static final int ACTIVITY_UPCOMING_BIRTHDAY = 7;
+
+ /** @hide */
+ @IntDef(prefix = { "AVAILABILITY_" }, value = {
+ AVAILABILITY_UNKNOWN,
+ AVAILABILITY_AVAILABLE,
+ AVAILABILITY_BUSY,
+ AVAILABILITY_OFFLINE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Availability {}
+
+ public static final int AVAILABILITY_UNKNOWN = -1;
+ public static final int AVAILABILITY_AVAILABLE = 0;
+ public static final int AVAILABILITY_BUSY = 1;
+ public static final int AVAILABILITY_OFFLINE = 2;
+
+ private final String mId;
+ private final int mActivity;
+
+ private int mAvailability;
+ private CharSequence mDescription;
+ private Icon mIcon;
+ private long mStartTimeMs;
+ private long mEndTimeMs;
+
+ private ConversationStatus(Builder b) {
+ mId = b.mId;
+ mActivity = b.mActivity;
+ mAvailability = b.mAvailability;
+ mDescription = b.mDescription;
+ mIcon = b.mIcon;
+ mStartTimeMs = b.mStartTimeMs;
+ mEndTimeMs = b.mEndTimeMs;
+ }
+
+ private ConversationStatus(Parcel p) {
+ mId = p.readString();
+ mActivity = p.readInt();
+ mAvailability = p.readInt();
+ mDescription = p.readCharSequence();
+ mIcon = p.readParcelable(Icon.class.getClassLoader());
+ mStartTimeMs = p.readLong();
+ mEndTimeMs = p.readLong();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeInt(mActivity);
+ dest.writeInt(mAvailability);
+ dest.writeCharSequence(mDescription);
+ dest.writeParcelable(mIcon, flags);
+ dest.writeLong(mStartTimeMs);
+ dest.writeLong(mEndTimeMs);
+ }
+
+ public @NonNull String getId() {
+ return mId;
+ }
+
+ public @ActivityType int getActivity() {
+ return mActivity;
+ }
+
+ public @Availability
+ int getAvailability() {
+ return mAvailability;
+ }
+
+ public @Nullable
+ CharSequence getDescription() {
+ return mDescription;
+ }
+
+ public @Nullable Icon getIcon() {
+ return mIcon;
+ }
+
+ public long getStartTimeMillis() {
+ return mStartTimeMs;
+ }
+
+ public long getEndTimeMillis() {
+ return mEndTimeMs;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ConversationStatus that = (ConversationStatus) o;
+ return mActivity == that.mActivity &&
+ mAvailability == that.mAvailability &&
+ mStartTimeMs == that.mStartTimeMs &&
+ mEndTimeMs == that.mEndTimeMs &&
+ mId.equals(that.mId) &&
+ Objects.equals(mDescription, that.mDescription) &&
+ Objects.equals(mIcon, that.mIcon);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId, mActivity, mAvailability, mDescription, mIcon, mStartTimeMs,
+ mEndTimeMs);
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationStatus{" +
+ "mId='" + mId + '\'' +
+ ", mActivity=" + mActivity +
+ ", mAvailability=" + mAvailability +
+ ", mDescription=" + mDescription +
+ ", mIcon=" + mIcon +
+ ", mStartTimeMs=" + mStartTimeMs +
+ ", mEndTimeMs=" + mEndTimeMs +
+ '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<ConversationStatus> CREATOR
+ = new Creator<ConversationStatus>() {
+ public ConversationStatus createFromParcel(Parcel source) {
+ return new ConversationStatus(source);
+ }
+
+ public ConversationStatus[] newArray(int size) {
+ return new ConversationStatus[size];
+ }
+ };
+
+ public static final class Builder {
+ final String mId;
+ final int mActivity;
+ int mAvailability = AVAILABILITY_UNKNOWN;
+ CharSequence mDescription;
+ Icon mIcon;
+ long mStartTimeMs = -1;
+ long mEndTimeMs = -1;
+
+ /**
+ * Creates a new builder.
+ *
+ * @param id The unique id for this status
+ * @param activity The type of status
+ */
+ public Builder(@NonNull String id, @ActivityType @NonNull int activity) {
+ mId = id;
+ mActivity = activity;
+ }
+
+
+ public @NonNull Builder setAvailability(@Availability int availability) {
+ mAvailability = availability;
+ return this;
+ }
+
+ public @NonNull Builder setDescription(@Nullable CharSequence description) {
+ mDescription = description;
+ return this;
+ }
+
+ public @NonNull Builder setIcon(@Nullable Icon icon) {
+ mIcon = icon;
+ return this;
+ }
+
+ public @NonNull Builder setStartTimeMillis(long startTimeMs) {
+ mStartTimeMs = startTimeMs;
+ return this;
+ }
+
+ public @NonNull Builder setEndTimeMillis(long endTimeMs) {
+ mEndTimeMs = endTimeMs;
+ return this;
+ }
+
+ public @NonNull ConversationStatus build() {
+ return new ConversationStatus(this);
+ }
+ }
+}
diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl
index c547ef1..0d12ed0 100644
--- a/core/java/android/app/people/IPeopleManager.aidl
+++ b/core/java/android/app/people/IPeopleManager.aidl
@@ -16,6 +16,7 @@
package android.app.people;
+import android.app.people.ConversationStatus;
import android.content.pm.ParceledListSlice;
import android.net.Uri;
import android.os.IBinder;
@@ -45,4 +46,9 @@
* conversation can't be found or no interactions have been recorded, returns 0L.
*/
long getLastInteraction(in String packageName, int userId, in String shortcutId);
+
+ void addOrUpdateStatus(in String packageName, int userId, in String conversationId, in ConversationStatus status);
+ void clearStatus(in String packageName, int userId, in String conversationId, in String statusId);
+ void clearStatuses(in String packageName, int userId, in String conversationId);
+ ParceledListSlice getStatuses(in String packageName, int userId, in String conversationId);
}
diff --git a/core/java/android/app/people/PeopleManager.java b/core/java/android/app/people/PeopleManager.java
new file mode 100644
index 0000000..de7ba62
--- /dev/null
+++ b/core/java/android/app/people/PeopleManager.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.people;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * This class allows interaction with conversation and people data.
+ */
+@SystemService(Context.PEOPLE_SERVICE)
+public final class PeopleManager {
+
+ private static final String LOG_TAG = PeopleManager.class.getSimpleName();
+
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final IPeopleManager mService;
+
+ /**
+ * @hide
+ */
+ public PeopleManager(@NonNull Context context) throws ServiceManager.ServiceNotFoundException {
+ mContext = context;
+ mService = IPeopleManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
+ Context.PEOPLE_SERVICE));
+ }
+
+
+ /**
+ * Sets or updates a {@link ConversationStatus} for a conversation.
+ *
+ * <p>Statuses are meant to represent current information about the conversation. Like
+ * notifications, they are transient and are not persisted beyond a reboot, nor are they
+ * backed up and restored.</p>
+ * <p>If the provided conversation shortcut is not already pinned, or cached by the system,
+ * it will remain cached as long as the status is active.</p>
+ *
+ * @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the
+ * conversation that has an active status
+ * @param status the current status for the given conversation
+ *
+ * @return whether the role is available in the system
+ */
+ public void addOrUpdateStatus(@NonNull String conversationId,
+ @NonNull ConversationStatus status) {
+ Preconditions.checkStringNotEmpty(conversationId);
+ Objects.requireNonNull(status);
+ try {
+ mService.addOrUpdateStatus(
+ mContext.getPackageName(), mContext.getUserId(), conversationId, status);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unpublishes a given status from the given conversation.
+ *
+ * @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the
+ * conversation that has an active status
+ * @param statusId the {@link ConversationStatus#getId() id} of a published status for the given
+ * conversation
+ */
+ public void clearStatus(@NonNull String conversationId, @NonNull String statusId) {
+ Preconditions.checkStringNotEmpty(conversationId);
+ Preconditions.checkStringNotEmpty(statusId);
+ try {
+ mService.clearStatus(
+ mContext.getPackageName(), mContext.getUserId(), conversationId, statusId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes all published statuses for the given conversation.
+ *
+ * @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the
+ * conversation that has one or more active statuses
+ */
+ public void clearStatuses(@NonNull String conversationId) {
+ Preconditions.checkStringNotEmpty(conversationId);
+ try {
+ mService.clearStatuses(
+ mContext.getPackageName(), mContext.getUserId(), conversationId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns all of the currently published statuses for a given conversation.
+ *
+ * @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the
+ * conversation that has one or more active statuses
+ */
+ public @NonNull List<ConversationStatus> getStatuses(@NonNull String conversationId) {
+ try {
+ final ParceledListSlice<ConversationStatus> parceledList
+ = mService.getStatuses(
+ mContext.getPackageName(), mContext.getUserId(), conversationId);
+ if (parceledList != null) {
+ return parceledList.getList();
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return new ArrayList<>();
+ }
+}
diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java
index ba1f612..8dde2c5 100644
--- a/core/java/android/app/role/RoleControllerManager.java
+++ b/core/java/android/app/role/RoleControllerManager.java
@@ -20,6 +20,8 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
@@ -46,6 +48,8 @@
*
* @hide
*/
+@SystemService(Context.ROLE_CONTROLLER_SERVICE)
+@TestApi
public class RoleControllerManager {
private static final String LOG_TAG = RoleControllerManager.class.getSimpleName();
@@ -195,11 +199,32 @@
}
/**
+ * @see RoleControllerService#onIsApplicationQualifiedForRole(String, String)
+ *
+ * @deprecated Use {@link #isApplicationVisibleForRole(String, String, Executor, Consumer)}
+ * instead.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ public void isApplicationQualifiedForRole(@NonNull String roleName, @NonNull String packageName,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
+ AndroidFuture<Bundle> future = new AndroidFuture<>();
+ service.isApplicationQualifiedForRole(roleName, packageName,
+ new RemoteCallback(future::complete));
+ return future;
+ });
+ propagateCallback(operation, "isApplicationQualifiedForRole", executor, callback);
+ }
+
+ /**
* @see RoleControllerService#onIsApplicationVisibleForRole(String, String)
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @TestApi
public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
@@ -217,6 +242,7 @@
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @TestApi
public void isRoleVisible(@NonNull String roleName,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 9ddd5be..8b2e07b 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -174,9 +174,6 @@
@NonNull
private final Object mListenersLock = new Object();
- @NonNull
- private final RoleControllerManager mRoleControllerManager;
-
/**
* @hide
*/
@@ -184,7 +181,6 @@
mContext = context;
mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
Context.ROLE_SERVICE));
- mRoleControllerManager = new RoleControllerManager(context);
}
/**
@@ -680,44 +676,6 @@
}
}
- /**
- * Check whether a role should be visible to user.
- *
- * @param roleName name of the role to check for
- * @param executor the executor to execute callback on
- * @param callback the callback to receive whether the role should be visible to user
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- @SystemApi
- public void isRoleVisible(@NonNull String roleName,
- @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
- mRoleControllerManager.isRoleVisible(roleName, executor, callback);
- }
-
- /**
- * Check whether an application is visible for a role.
- *
- * While an application can be qualified for a role, it can still stay hidden from user (thus
- * not visible). If an application is visible for a role, we may show things related to the role
- * for it, e.g. showing an entry pointing to the role settings in its application info page.
- *
- * @param roleName the name of the role to check for
- * @param packageName the package name of the application to check for
- * @param executor the executor to execute callback on
- * @param callback the callback to receive whether the application is visible for the role
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- @SystemApi
- public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName,
- @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
- mRoleControllerManager.isApplicationVisibleForRole(roleName, packageName, executor,
- callback);
- }
-
private static class OnRoleHoldersChangedListenerDelegate
extends IOnRoleHoldersChangedListener.Stub {
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 56c4824..c0736a6 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -162,6 +162,11 @@
/** @hide */
@NonNull
@SystemApi
+ public static final ParcelUuid DIP =
+ ParcelUuid.fromString("00001200-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
diff --git a/core/java/android/bluetooth/SdpDipRecord.java b/core/java/android/bluetooth/SdpDipRecord.java
new file mode 100644
index 0000000..84b0eef
--- /dev/null
+++ b/core/java/android/bluetooth/SdpDipRecord.java
@@ -0,0 +1,104 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* 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.bluetooth;
+
+import java.util.Arrays;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data representation of a Object Push Profile Server side SDP record.
+ */
+/** @hide */
+public class SdpDipRecord implements Parcelable {
+ private final int mSpecificationId;
+ private final int mVendorId;
+ private final int mVendorIdSource;
+ private final int mProductId;
+ private final int mVersion;
+ private final boolean mPrimaryRecord;
+
+ public SdpDipRecord(int specificationId,
+ int vendorId, int vendorIdSource,
+ int productId, int version,
+ boolean primaryRecord) {
+ super();
+ this.mSpecificationId = specificationId;
+ this.mVendorId = vendorId;
+ this.mVendorIdSource = vendorIdSource;
+ this.mProductId = productId;
+ this.mVersion = version;
+ this.mPrimaryRecord = primaryRecord;
+ }
+
+ public SdpDipRecord(Parcel in) {
+ this.mSpecificationId = in.readInt();
+ this.mVendorId = in.readInt();
+ this.mVendorIdSource = in.readInt();
+ this.mProductId = in.readInt();
+ this.mVersion = in.readInt();
+ this.mPrimaryRecord = in.readBoolean();
+ }
+
+ public int getSpecificationId() {
+ return mSpecificationId;
+ }
+
+ public int getVendorId() {
+ return mVendorId;
+ }
+
+ public int getVendorIdSource() {
+ return mVendorIdSource;
+ }
+
+ public int getProductId() {
+ return mProductId;
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ public boolean getPrimaryRecord() {
+ return mPrimaryRecord;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSpecificationId);
+ dest.writeInt(mVendorId);
+ dest.writeInt(mVendorIdSource);
+ dest.writeInt(mProductId);
+ dest.writeInt(mVersion);
+ dest.writeBoolean(mPrimaryRecord);
+ }
+
+ @Override
+ public int describeContents() {
+ /* No special objects */
+ return 0;
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ public SdpDipRecord createFromParcel(Parcel in) {
+ return new SdpDipRecord(in);
+ }
+ public SdpDipRecord[] newArray(int size) {
+ return new SdpDipRecord[size];
+ }
+ };
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 33f9607..43011fc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -40,6 +40,7 @@
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.VrManager;
+import android.app.people.PeopleManager;
import android.app.time.TimeManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ApplicationInfo;
@@ -4821,6 +4822,16 @@
public static final String ROLE_SERVICE = "role";
/**
+ * Official published name of the (internal) role controller service.
+ *
+ * @see #getSystemService(String)
+ * @see android.app.role.RoleControllerService
+ *
+ * @hide
+ */
+ public static final String ROLE_CONTROLLER_SERVICE = "role_controller";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.camera2.CameraManager} for interacting with
* camera devices.
@@ -5301,10 +5312,10 @@
public static final String SMS_SERVICE = "sms";
/**
- * Use with {@link #getSystemService(String)} to access people service.
+ * Use with {@link #getSystemService(String)} to access a {@link PeopleManager} to interact
+ * with your published conversations.
*
* @see #getSystemService(String)
- * @hide
*/
public static final String PEOPLE_SERVICE = "people";
diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl
index 4e368d0..a54e88f 100644
--- a/core/java/android/hardware/ISensorPrivacyManager.aidl
+++ b/core/java/android/hardware/ISensorPrivacyManager.aidl
@@ -26,18 +26,19 @@
// =============== Beginning of transactions used on native side as well ======================
void addSensorPrivacyListener(in ISensorPrivacyListener listener);
+ void addIndividualSensorPrivacyListener(int userId, int sensor,
+ in ISensorPrivacyListener listener);
+
void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
boolean isSensorPrivacyEnabled();
+ boolean isIndividualSensorPrivacyEnabled(int userId, int sensor);
+
void setSensorPrivacy(boolean enable);
+
+ void setIndividualSensorPrivacy(int userId, int sensor, boolean enable);
+
+ void setIndividualSensorPrivacyForProfileGroup(int userId, int sensor, boolean enable);
// =============== End of transactions used on native side as well ============================
-
- // TODO(evanseverson) add to native interface
- boolean isIndividualSensorPrivacyEnabled(int sensor);
-
- // TODO(evanseverson) add to native interface
- void setIndividualSensorPrivacy(int sensor, boolean enable);
-
- // TODO(evanseverson) listeners
}
\ No newline at end of file
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index c647239..8497525 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -160,6 +160,37 @@
}
/**
+ * Registers a new listener to receive notification when the state of sensor privacy
+ * changes.
+ *
+ * @param sensor the sensor to listen to changes to
+ * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
+ * privacy changes.
+ */
+ public void addSensorPrivacyListener(@IndividualSensor int sensor,
+ final OnSensorPrivacyChangedListener listener) {
+ synchronized (mListeners) {
+ ISensorPrivacyListener iListener = mListeners.get(listener);
+ if (iListener == null) {
+ iListener = new ISensorPrivacyListener.Stub() {
+ @Override
+ public void onSensorPrivacyChanged(boolean enabled) {
+ listener.onSensorPrivacyChanged(enabled);
+ }
+ };
+ mListeners.put(listener, iListener);
+ }
+
+ try {
+ mService.addIndividualSensorPrivacyListener(mContext.getUserId(), sensor,
+ iListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Unregisters the specified listener from receiving notifications when the state of sensor
* privacy changes.
*
@@ -200,7 +231,7 @@
*/
public boolean isIndividualSensorPrivacyEnabled(@IndividualSensor int sensor) {
try {
- return mService.isIndividualSensorPrivacyEnabled(sensor);
+ return mService.isIndividualSensorPrivacyEnabled(mContext.getUserId(), sensor);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -209,12 +240,32 @@
/**
* Sets sensor privacy to the specified state for an individual sensor.
*
+ * @param sensor the sensor which to change the state for
* @param enable the state to which sensor privacy should be set.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
- public void setIndividualSensorPrivacy(@IndividualSensor int sensor, boolean enable) {
+ public void setIndividualSensorPrivacy(@IndividualSensor int sensor,
+ boolean enable) {
try {
- mService.setIndividualSensorPrivacy(sensor, enable);
+ mService.setIndividualSensorPrivacy(mContext.getUserId(), sensor, enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets sensor privacy to the specified state for an individual sensor for the profile group of
+ * context's user.
+ *
+ * @param sensor the sensor which to change the state for
+ * @param enable the state to which sensor privacy should be set.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ public void setIndividualSensorPrivacyForProfileGroup(@IndividualSensor int sensor,
+ boolean enable) {
+ try {
+ mService.setIndividualSensorPrivacyForProfileGroup(mContext.getUserId(), sensor,
+ enable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl
index 9dd0114..04b585c 100644
--- a/core/java/android/net/vcn/IVcnManagementService.aidl
+++ b/core/java/android/net/vcn/IVcnManagementService.aidl
@@ -23,6 +23,6 @@
* @hide
*/
interface IVcnManagementService {
- void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config);
+ void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config, in String opPkgName);
void clearVcnConfig(in ParcelUuid subscriptionGroup);
}
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index d4a3fa7..ede8faa 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -45,11 +46,17 @@
public final class VcnConfig implements Parcelable {
@NonNull private static final String TAG = VcnConfig.class.getSimpleName();
+ private static final String PACKAGE_NAME_KEY = "mPackageName";
+ @NonNull private final String mPackageName;
+
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);
+ private VcnConfig(
+ @NonNull String packageName,
+ @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs) {
+ mPackageName = packageName;
+ mGatewayConnectionConfigs = Collections.unmodifiableSet(gatewayConnectionConfigs);
validate();
}
@@ -61,6 +68,8 @@
*/
@VisibleForTesting(visibility = Visibility.PRIVATE)
public VcnConfig(@NonNull PersistableBundle in) {
+ mPackageName = in.getString(PACKAGE_NAME_KEY);
+
final PersistableBundle gatewayConnectionConfigsBundle =
in.getPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY);
mGatewayConnectionConfigs =
@@ -72,8 +81,19 @@
}
private void validate() {
+ Objects.requireNonNull(mPackageName, "packageName was null");
Preconditions.checkCollectionNotEmpty(
- mGatewayConnectionConfigs, "gatewayConnectionConfigs");
+ mGatewayConnectionConfigs, "gatewayConnectionConfigs was empty");
+ }
+
+ /**
+ * Retrieve the package name of the provisioning app.
+ *
+ * @hide
+ */
+ @NonNull
+ public String getProvisioningPackageName() {
+ return mPackageName;
}
/** Retrieves the set of configured tunnels. */
@@ -91,6 +111,8 @@
public PersistableBundle toPersistableBundle() {
final PersistableBundle result = new PersistableBundle();
+ result.putString(PACKAGE_NAME_KEY, mPackageName);
+
final PersistableBundle gatewayConnectionConfigsBundle =
PersistableBundleUtils.fromList(
new ArrayList<>(mGatewayConnectionConfigs),
@@ -102,7 +124,7 @@
@Override
public int hashCode() {
- return Objects.hash(mGatewayConnectionConfigs);
+ return Objects.hash(mPackageName, mGatewayConnectionConfigs);
}
@Override
@@ -112,7 +134,8 @@
}
final VcnConfig rhs = (VcnConfig) other;
- return mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
+ return mPackageName.equals(rhs.mPackageName)
+ && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
}
// Parcelable methods
@@ -143,9 +166,17 @@
/** This class is used to incrementally build {@link VcnConfig} objects. */
public static class Builder {
+ @NonNull private final String mPackageName;
+
@NonNull
private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
+ public Builder(@NonNull Context context) {
+ Objects.requireNonNull(context, "context was null");
+
+ mPackageName = context.getOpPackageName();
+ }
+
/**
* Adds a configuration for an individual gateway connection.
*
@@ -168,7 +199,7 @@
*/
@NonNull
public VcnConfig build() {
- return new VcnConfig(mGatewayConnectionConfigs);
+ return new VcnConfig(mPackageName, mGatewayConnectionConfigs);
}
}
}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 19c183f..b881a339 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -101,7 +101,7 @@
requireNonNull(config, "config was null");
try {
- mService.setVcnConfig(subscriptionGroup, config);
+ mService.setVcnConfig(subscriptionGroup, config, mContext.getOpPackageName());
} catch (ServiceSpecificException e) {
throw new IOException(e);
} catch (RemoteException e) {
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 7243234..4868827 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -241,7 +241,7 @@
opEntry.getAttributedOpEntries().get(attributionTag);
long lastAccessTime = attrOpEntry.getLastAccessTime(opFlags);
- if (lastAccessTime < recentThreshold) {
+ if (lastAccessTime < recentThreshold && !attrOpEntry.isRunning()) {
continue;
}
if (!isUserSensitive(packageName, user, op)
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index ec4d81c..7a856e8 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -101,6 +101,13 @@
public static final String NAMESPACE_APP_COMPAT = "app_compat";
/**
+ * Namespace for all app hibernation related features.
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
+
+ /**
* Namespace for app standby configurations.
*
* @hide
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index ac6208b..021d5fc 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -38,12 +38,11 @@
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState;
-import android.telephony.emergency.EmergencyNumber;
-import android.telephony.ims.ImsReasonInfo;
import android.telephony.NetworkRegistrationInfo.Domain;
import android.telephony.TelephonyManager.DataEnabledReason;
import android.telephony.TelephonyManager.DataState;
-import android.util.Log;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IPhoneStateListener;
@@ -1082,7 +1081,9 @@
/**
* Create a PhoneStateListener for the Phone with the default subscription.
- * This class requires Looper.myLooper() not return null.
+ * If this is created for use with deprecated API
+ * {@link TelephonyManager#listen(PhoneStateListener, int)}, then this class requires
+ * Looper.myLooper() not return null.
*/
public PhoneStateListener() {
this(null, Looper.myLooper());
@@ -1120,7 +1121,10 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public PhoneStateListener(Integer subId, Looper looper) {
- this(subId, new HandlerExecutor(new Handler(looper)));
+ if (looper != null) {
+ setExecutor(new HandlerExecutor(new Handler(looper)));
+ }
+ mSubId = subId;
if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
>= Build.VERSION_CODES.Q) {
throw new IllegalArgumentException("PhoneStateListener with subId: "
@@ -1140,7 +1144,8 @@
*/
@Deprecated
public PhoneStateListener(@NonNull Executor executor) {
- this(null, executor);
+ setExecutor(executor);
+ mSubId = null;
}
private @NonNull Executor mExecutor;
@@ -1153,12 +1158,14 @@
throw new IllegalArgumentException("PhoneStateListener Executor must be non-null");
}
mExecutor = executor;
+ callback = new IPhoneStateListenerStub(this, mExecutor);
}
- private PhoneStateListener(Integer subId, Executor executor) {
- setExecutor(executor);
- mSubId = subId;
- callback = new IPhoneStateListenerStub(this, mExecutor);
+ /**
+ * @hide
+ */
+ public boolean isExecutorSet() {
+ return mExecutor != null;
}
/**
diff --git a/core/java/android/uwb/CloseReason.aidl b/core/java/android/uwb/CloseReason.aidl
deleted file mode 100644
index bef129e..0000000
--- a/core/java/android/uwb/CloseReason.aidl
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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;
-
-/**
- * @hide
- */
-@Backing(type="int")
-enum CloseReason {
- /**
- * Unknown reason
- */
- UNKNOWN,
-
- /**
- * A local API call triggered the close, such as a call to
- * IUwbAdapter.stopRanging.
- */
- LOCAL_API,
-
- /**
- * The maximum number of sessions has been reached. This error may be generated
- * for an active session if a higher priority session begins.
- */
- MAX_SESSIONS_REACHED,
-
- /**
- * The system state has changed resulting in the session ending (e.g. the user
- * disables UWB, or the user's locale changes and an active channel is no longer
- * permitted to be used).
- */
- SYSTEM_POLICY,
-
- /**
- * The remote device has requested to terminate the session
- */
- REMOTE_REQUEST,
-
- /**
- * The session was closed for a protocol specific reason
- */
- PROTOCOL_SPECIFIC,
-}
-
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index 2c8b2e4..b9c5508 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -119,42 +119,96 @@
PersistableBundle getSpecificationInfo();
/**
- * Request to start a new ranging session
+ * Request to open a new ranging session
*
- * This function must return before calling IUwbAdapterCallbacks
- * #onRangingStarted, #onRangingClosed, or #onRangingResult.
+ * This function must return before calling any functions in
+ * IUwbAdapterCallbacks.
*
- * A ranging session does not need to be started before returning.
+ * This function does not start the ranging session, but all necessary
+ * components must be initialized and ready to start a new ranging
+ * session prior to calling IUwbAdapterCallback#onRangingOpened.
*
- * IUwbAdapterCallbacks#onRangingStarted must be called within
- * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called
- * if the ranging session is scheduled to start successfully.
+ * IUwbAdapterCallbacks#onRangingOpened must be called within
+ * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being
+ * called if the ranging session is opened successfully.
*
- * IUwbAdapterCallbacks#onRangingStartFailed must be called within
- * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called
- * if the ranging session fails to be scheduled to start successfully.
+ * IUwbAdapterCallbacks#onRangingOpenFailed must be called within
+ * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being called
+ * if the ranging session fails to be opened.
*
* @param rangingCallbacks the callbacks used to deliver ranging information
* @param parameters the configuration to use for ranging
* @return a SessionHandle used to identify this ranging request
*/
- SessionHandle startRanging(in IUwbRangingCallbacks rangingCallbacks,
- in PersistableBundle parameters);
+ SessionHandle openRanging(in IUwbRangingCallbacks rangingCallbacks,
+ in PersistableBundle parameters);
/**
- * Stop and close ranging for the session associated with the given handle
+ * Request to start ranging
+ *
+ * IUwbAdapterCallbacks#onRangingStarted must be called within
+ * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being
+ * called if the ranging session starts successfully.
+ *
+ * IUwbAdapterCallbacks#onRangingStartFailed must be called within
+ * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being
+ * called if the ranging session fails to be started.
+ *
+ * @param sessionHandle the session handle to start ranging for
+ * @param parameters additional configuration required to start ranging
+ */
+ void startRanging(in SessionHandle sessionHandle,
+ in PersistableBundle parameters);
+
+ /**
+ * Request to reconfigure ranging
+ *
+ * IUwbAdapterCallbacks#onRangingReconfigured must be called after
+ * successfully reconfiguring the session.
+ *
+ * IUwbAdapterCallbacks#onRangingReconfigureFailed must be called after
+ * failing to reconfigure the session.
+ *
+ * A session must not be modified by a failed call to #reconfigureRanging.
+ *
+ * @param sessionHandle the session handle to start ranging for
+ * @param parameters the parameters to reconfigure and their new values
+ */
+ void reconfigureRanging(in SessionHandle sessionHandle,
+ in PersistableBundle parameters);
+
+ /**
+ * Request to stop ranging
+ *
+ * IUwbAdapterCallbacks#onRangingStopped must be called after
+ * successfully stopping the session.
+ *
+ * IUwbAdapterCallbacks#onRangingStopFailed must be called after failing
+ * to stop the session.
+ *
+ * @param sessionHandle the session handle to stop ranging for
+ */
+ void stopRanging(in SessionHandle sessionHandle);
+
+ /**
+ * Close ranging for the session associated with the given handle
*
* Calling with an invalid handle or a handle that has already been closed
* is a no-op.
*
* IUwbAdapterCallbacks#onRangingClosed must be called within
- * RANGING_SESSION_CLOSE_THRESHOLD_MS of #stopRanging being called.
+ * RANGING_SESSION_CLOSE_THRESHOLD_MS of #closeRanging being called.
*
- * @param sessionHandle the session handle to stop ranging for
+ * @param sessionHandle the session handle to close ranging for
*/
void closeRanging(in SessionHandle sessionHandle);
/**
+ * The maximum allowed time to open a ranging session.
+ */
+ const int RANGING_SESSION_OPEN_THRESHOLD_MS = 3000; // Value TBD
+
+ /**
* The maximum allowed time to start a ranging session.
*/
const int RANGING_SESSION_START_THRESHOLD_MS = 3000; // Value TBD
diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl
index 1fc3bfd..f71f3ff 100644
--- a/core/java/android/uwb/IUwbRangingCallbacks.aidl
+++ b/core/java/android/uwb/IUwbRangingCallbacks.aidl
@@ -17,16 +17,33 @@
package android.uwb;
import android.os.PersistableBundle;
-import android.uwb.CloseReason;
+import android.uwb.RangingChangeReason;
import android.uwb.RangingReport;
import android.uwb.SessionHandle;
-import android.uwb.StartFailureReason;
/**
* @hide
*/
interface IUwbRangingCallbacks {
/**
+ * Called when the ranging session has been opened
+ *
+ * @param sessionHandle the session the callback is being invoked for
+ */
+ void onRangingOpened(in SessionHandle sessionHandle);
+
+ /**
+ * Called when a ranging session fails to start
+ *
+ * @param sessionHandle the session the callback is being invoked for
+ * @param reason the reason the session failed to start
+ * @param parameters protocol specific parameters
+ */
+ void onRangingOpenFailed(in SessionHandle sessionHandle,
+ RangingChangeReason reason,
+ in PersistableBundle parameters);
+
+ /**
* Called when ranging has started
*
* May output parameters generated by the lower layers that must be sent to the
@@ -47,8 +64,49 @@
* @param reason the reason the session failed to start
* @param parameters protocol specific parameters
*/
- void onRangingStartFailed(in SessionHandle sessionHandle, StartFailureReason reason,
+ void onRangingStartFailed(in SessionHandle sessionHandle,
+ RangingChangeReason reason,
in PersistableBundle parameters);
+
+ /**
+ * Called when ranging has been reconfigured
+ *
+ * @param sessionHandle the session the callback is being invoked for
+ * @param parameters the updated ranging configuration
+ */
+ void onRangingReconfigured(in SessionHandle sessionHandle,
+ in PersistableBundle parameters);
+
+ /**
+ * Called when a ranging session fails to be reconfigured
+ *
+ * @param sessionHandle the session the callback is being invoked for
+ * @param reason the reason the session failed to reconfigure
+ * @param parameters protocol specific parameters
+ */
+ void onRangingReconfigureFailed(in SessionHandle sessionHandle,
+ RangingChangeReason reason,
+ in PersistableBundle parameters);
+
+ /**
+ * Called when the ranging session has been stopped
+ *
+ * @param sessionHandle the session the callback is being invoked for
+ */
+
+ void onRangingStopped(in SessionHandle sessionHandle);
+
+ /**
+ * Called when a ranging session fails to stop
+ *
+ * @param sessionHandle the session the callback is being invoked for
+ * @param reason the reason the session failed to stop
+ * @param parameters protocol specific parameters
+ */
+ void onRangingStopFailed(in SessionHandle sessionHandle,
+ RangingChangeReason reason,
+ in PersistableBundle parameters);
+
/**
* Called when a ranging session is closed
*
@@ -56,7 +114,8 @@
* @param reason the reason the session was closed
* @param parameters protocol specific parameters
*/
- void onRangingClosed(in SessionHandle sessionHandle, CloseReason reason,
+ void onRangingClosed(in SessionHandle sessionHandle,
+ RangingChangeReason reason,
in PersistableBundle parameters);
/**
diff --git a/core/java/android/uwb/RangingChangeReason.aidl b/core/java/android/uwb/RangingChangeReason.aidl
new file mode 100644
index 0000000..19d4b39
--- /dev/null
+++ b/core/java/android/uwb/RangingChangeReason.aidl
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum RangingChangeReason {
+ /**
+ * Unknown reason
+ */
+ UNKNOWN,
+
+ /**
+ * A local API call triggered the change, such as a call to
+ * IUwbAdapter.closeRanging.
+ */
+ LOCAL_API,
+
+ /**
+ * The maximum number of sessions has been reached. This may be generated for
+ * an active session if a higher priority session begins.
+ */
+ MAX_SESSIONS_REACHED,
+
+ /**
+ * The system state has changed resulting in the session changing (e.g. the
+ * user disables UWB, or the user's locale changes and an active channel is no
+ * longer permitted to be used).
+ */
+ SYSTEM_POLICY,
+
+ /**
+ * The remote device has requested to change the session
+ */
+ REMOTE_REQUEST,
+
+ /**
+ * The session changed for a protocol specific reason
+ */
+ PROTOCOL_SPECIFIC,
+
+ /**
+ * The provided parameters were invalid
+ */
+ BAD_PARAMETERS,
+}
+
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index a9bf4ab..5ac95d4 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -50,7 +50,7 @@
@NonNull RangingSession.Callback callbacks) {
SessionHandle sessionHandle;
try {
- sessionHandle = mAdapter.startRanging(this, params);
+ sessionHandle = mAdapter.openRanging(this, params);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -59,7 +59,7 @@
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,
+ RangingSession.Callback.REASON_GENERIC_ERROR,
new PersistableBundle()));
}
@@ -75,6 +75,67 @@
}
@Override
+ public void onRangingOpened(SessionHandle sessionHandle) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(TAG,
+ "onRangingOpened - received unexpected SessionHandle: " + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingOpened();
+ }
+ }
+
+ @Override
+ public void onRangingOpenFailed(SessionHandle sessionHandle, @RangingChangeReason int reason,
+ PersistableBundle parameters) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(TAG,
+ "onRangingOpened - received unexpected SessionHandle: " + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingOpenFailed(convertToReason(reason), parameters);
+ mRangingSessionTable.remove(sessionHandle);
+ }
+ }
+
+ @Override
+ public void onRangingReconfigured(SessionHandle sessionHandle, PersistableBundle parameters) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(TAG,
+ "onRangingReconfigured - received unexpected SessionHandle: "
+ + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingReconfigured(parameters);
+ }
+ }
+
+ @Override
+ public void onRangingReconfigureFailed(SessionHandle sessionHandle,
+ @RangingChangeReason int reason, PersistableBundle params) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: "
+ + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingReconfigureFailed(convertToReason(reason), params);
+ }
+ }
+
+
+ @Override
public void onRangingStarted(SessionHandle sessionHandle, PersistableBundle parameters) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
@@ -89,7 +150,7 @@
}
@Override
- public void onRangingStartFailed(SessionHandle sessionHandle, int reason,
+ public void onRangingStartFailed(SessionHandle sessionHandle, @RangingChangeReason int reason,
PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
@@ -99,13 +160,42 @@
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingClosed(convertStartFailureToCloseReason(reason), params);
- mRangingSessionTable.remove(sessionHandle);
+ session.onRangingStartFailed(convertToReason(reason), params);
}
}
@Override
- public void onRangingClosed(SessionHandle sessionHandle, int reason, PersistableBundle params) {
+ public void onRangingStopped(SessionHandle sessionHandle) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(TAG, "onRangingStopped - received unexpected SessionHandle: "
+ + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingStopped();
+ }
+ }
+
+ @Override
+ public void onRangingStopFailed(SessionHandle sessionHandle, @RangingChangeReason int reason,
+ PersistableBundle parameters) {
+ synchronized (this) {
+ if (!hasSession(sessionHandle)) {
+ Log.w(TAG, "onRangingStopFailed - received unexpected SessionHandle: "
+ + sessionHandle);
+ return;
+ }
+
+ RangingSession session = mRangingSessionTable.get(sessionHandle);
+ session.onRangingStopFailed(convertToReason(reason), parameters);
+ }
+ }
+
+ @Override
+ public void onRangingClosed(SessionHandle sessionHandle, @RangingChangeReason int reason,
+ PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG, "onRangingClosed - received unexpected SessionHandle: " + sessionHandle);
@@ -113,7 +203,7 @@
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingClosed(convertToCloseReason(reason), params);
+ session.onRangingClosed(convertToReason(reason), params);
mRangingSessionTable.remove(sessionHandle);
}
}
@@ -131,48 +221,30 @@
}
}
- @RangingSession.Callback.CloseReason
- private static int convertToCloseReason(@CloseReason int reason) {
+ @RangingSession.Callback.Reason
+ private static int convertToReason(@RangingChangeReason int reason) {
switch (reason) {
- case CloseReason.LOCAL_API:
- return RangingSession.Callback.CLOSE_REASON_LOCAL_CLOSE_API;
+ case RangingChangeReason.LOCAL_API:
+ return RangingSession.Callback.REASON_LOCAL_REQUEST;
- case CloseReason.MAX_SESSIONS_REACHED:
- return RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED;
+ case RangingChangeReason.MAX_SESSIONS_REACHED:
+ return RangingSession.Callback.REASON_MAX_SESSIONS_REACHED;
- case CloseReason.SYSTEM_POLICY:
- return RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY;
+ case RangingChangeReason.SYSTEM_POLICY:
+ return RangingSession.Callback.REASON_SYSTEM_POLICY;
- case CloseReason.REMOTE_REQUEST:
- return RangingSession.Callback.CLOSE_REASON_REMOTE_REQUEST;
+ case RangingChangeReason.REMOTE_REQUEST:
+ return RangingSession.Callback.REASON_REMOTE_REQUEST;
- case CloseReason.PROTOCOL_SPECIFIC:
- return RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC;
+ case RangingChangeReason.PROTOCOL_SPECIFIC:
+ return RangingSession.Callback.REASON_PROTOCOL_SPECIFIC_ERROR;
- case CloseReason.UNKNOWN:
+ case RangingChangeReason.BAD_PARAMETERS:
+ return RangingSession.Callback.REASON_BAD_PARAMETERS;
+
+ case RangingChangeReason.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;
+ return RangingSession.Callback.REASON_UNKNOWN;
}
}
}
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
index b0dbd85..0f87af4 100644
--- a/core/java/android/uwb/RangingSession.java
+++ b/core/java/android/uwb/RangingSession.java
@@ -36,9 +36,9 @@
* <p>To get an instance of {@link RangingSession}, first use
* {@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, PersistableBundle)} with the failure reason.
+ * {@link RangingSession.Callback#onOpened(RangingSession)}. If opening a session fails, the failure
+ * is reported through {@link RangingSession.Callback#onOpenFailed(int, PersistableBundle)} with the
+ * failure reason.
*
* @hide
*/
@@ -50,103 +50,162 @@
private final Callback mCallback;
private enum State {
+ /**
+ * The state of the {@link RangingSession} until
+ * {@link RangingSession.Callback#onOpened(RangingSession)} is invoked
+ */
INIT,
- OPEN,
- CLOSED,
+
+ /**
+ * The {@link RangingSession} is initialized and ready to begin ranging
+ */
+ IDLE,
+
+ /**
+ * The {@link RangingSession} is actively ranging
+ */
+ ACTIVE,
+
+ /**
+ * The {@link RangingSession} is closed and may not be used for ranging.
+ */
+ CLOSED
}
- private State mState;
+ private State mState = State.INIT;
/**
* Interface for receiving {@link RangingSession} events
*/
public interface Callback {
/**
- * Invoked when {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
- * is successful
- *
- * @param session the newly opened {@link RangingSession}
- * @param sessionInfo session specific parameters from lower layers
- */
- void onOpenSuccess(@NonNull RangingSession session, @NonNull PersistableBundle sessionInfo);
-
- /**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
- CLOSE_REASON_UNKNOWN,
- CLOSE_REASON_LOCAL_CLOSE_API,
- CLOSE_REASON_LOCAL_BAD_PARAMETERS,
- CLOSE_REASON_LOCAL_GENERIC_ERROR,
- CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED,
- CLOSE_REASON_LOCAL_SYSTEM_POLICY,
- CLOSE_REASON_REMOTE_GENERIC_ERROR,
- CLOSE_REASON_REMOTE_REQUEST})
- @interface CloseReason {}
+ REASON_UNKNOWN,
+ REASON_LOCAL_REQUEST,
+ REASON_REMOTE_REQUEST,
+ REASON_BAD_PARAMETERS,
+ REASON_GENERIC_ERROR,
+ REASON_MAX_SESSIONS_REACHED,
+ REASON_SYSTEM_POLICY,
+ REASON_PROTOCOL_SPECIFIC_ERROR})
+ @interface Reason {}
/**
* Indicates that the session was closed or failed to open due to an unknown reason
*/
- int CLOSE_REASON_UNKNOWN = 0;
+ int REASON_UNKNOWN = 0;
/**
* Indicates that the session was closed or failed to open because
* {@link AutoCloseable#close()} or {@link RangingSession#close()} was called
*/
- int CLOSE_REASON_LOCAL_CLOSE_API = 1;
-
- /**
- * Indicates that the session failed to open due to erroneous parameters passed
- * to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
- */
- int CLOSE_REASON_LOCAL_BAD_PARAMETERS = 2;
-
- /**
- * Indicates that the session was closed due to some local error on this device besides the
- * error code already listed
- */
- int CLOSE_REASON_LOCAL_GENERIC_ERROR = 3;
-
- /**
- * Indicates that the session failed to open because the number of currently open sessions
- * is equal to {@link UwbManager#getMaxSimultaneousSessions()}
- */
- int CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED = 4;
-
- /**
- * Indicates that the session was closed or failed to open due to local system policy, such
- * as privacy policy, power management policy, permissions, and more.
- */
- int CLOSE_REASON_LOCAL_SYSTEM_POLICY = 5;
-
- /**
- * Indicates that the session was closed or failed to open due to an error with the remote
- * device besides error codes already listed.
- */
- int CLOSE_REASON_REMOTE_GENERIC_ERROR = 6;
+ int REASON_LOCAL_REQUEST = 1;
/**
* Indicates that the session was closed or failed to open due to an explicit request from
* the remote device.
*/
- int CLOSE_REASON_REMOTE_REQUEST = 7;
+ int REASON_REMOTE_REQUEST = 2;
/**
- * Indicates that the session was closed for a protocol specific reason. The associated
- * {@link PersistableBundle} should be consulted for additional information.
+ * Indicates that the session was closed or failed to open due to erroneous parameters
*/
- int CLOSE_REASON_PROTOCOL_SPECIFIC = 8;
+ int REASON_BAD_PARAMETERS = 3;
/**
+ * Indicates an error on this device besides the error code already listed
+ */
+ int REASON_GENERIC_ERROR = 4;
+
+ /**
+ * Indicates that the number of currently open sessions is equal to
+ * {@link UwbManager#getMaxSimultaneousSessions()} and additional sessions may not be
+ * opened.
+ */
+ int REASON_MAX_SESSIONS_REACHED = 5;
+
+ /**
+ * Indicates that the local system policy caused the change, such
+ * as privacy policy, power management policy, permissions, and more.
+ */
+ int REASON_SYSTEM_POLICY = 6;
+
+ /**
+ * Indicates a protocol specific error. The associated {@link PersistableBundle} should be
+ * consulted for additional information.
+ */
+ int REASON_PROTOCOL_SPECIFIC_ERROR = 7;
+
+ /**
+ * Invoked when {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
+ * is successful
+ *
+ * @param session the newly opened {@link RangingSession}
+ */
+ void onOpened(@NonNull RangingSession session);
+
+ /**
+ * Invoked if {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}}
+ * fails
+ *
+ * @param reason the failure reason
+ * @param params protocol specific parameters
+ */
+ void onOpenFailed(@Reason int reason, @NonNull PersistableBundle params);
+
+ /**
+ * Invoked when {@link RangingSession#start(PersistableBundle)} is successful
+ * @param sessionInfo session specific parameters from the lower layers
+ */
+ void onStarted(@NonNull PersistableBundle sessionInfo);
+
+ /**
+ * Invoked when {@link RangingSession#start(PersistableBundle)} fails
+ *
+ * @param reason the failure reason
+ * @param params protocol specific parameters
+ */
+ void onStartFailed(@Reason int reason, @NonNull PersistableBundle params);
+
+ /**
+ * Invoked when a request to reconfigure the session succeeds
+ *
+ * @param params the updated ranging configuration
+ */
+ void onReconfigured(@NonNull PersistableBundle params);
+
+ /**
+ * Invoked when a request to reconfigure the session fails
+ *
+ * @param reason reason the session failed to be reconfigured
+ * @param params protocol specific failure reasons
+ */
+ void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params);
+
+ /**
+ * Invoked when a request to stop the session succeeds
+ */
+ void onStopped();
+
+ /**
+ * Invoked when a request to stop the session fails
+ *
+ * @param reason reason the session failed to be stopped
+ * @param params protocol specific failure reasons
+ */
+ void onStopFailed(@Reason int reason, @NonNull PersistableBundle params);
+
+ /**
* 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.
+ * {@link RangingSession#close()} or {@link AutoCloseable#close()}.
*
* @param reason reason for the session closure
* @param parameters protocol specific parameters related to the close reason
*/
- void onClosed(@CloseReason int reason, @NonNull PersistableBundle parameters);
+ void onClosed(@Reason int reason, @NonNull PersistableBundle parameters);
/**
* Called once per ranging interval even when a ranging measurement fails
@@ -172,12 +231,95 @@
* @hide
*/
public boolean isOpen() {
- return mState == State.OPEN;
+ return mState == State.IDLE || mState == State.ACTIVE;
+ }
+
+ /**
+ * Begins ranging for the session.
+ *
+ * <p>On successfully starting a ranging session,
+ * {@link RangingSession.Callback#onStarted(PersistableBundle)} is invoked.
+ *
+ * <p>On failure to start the session,
+ * {@link RangingSession.Callback#onStartFailed(int, PersistableBundle)} is invoked.
+ *
+ * @param params configuration parameters for starting the session
+ */
+ public void start(@NonNull PersistableBundle params) {
+ if (mState != State.IDLE) {
+ throw new IllegalStateException();
+ }
+
+ try {
+ mAdapter.startRanging(mSessionHandle, params);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attempts to reconfigure the session with the given parameters
+ * <p>This call may be made when the session is open.
+ *
+ * <p>On successfully reconfiguring the session
+ * {@link RangingSession.Callback#onReconfigured(PersistableBundle)} is invoked.
+ *
+ * <p>On failure to reconfigure the session,
+ * {@link RangingSession.Callback#onReconfigureFailed(int, PersistableBundle)} is invoked.
+ *
+ * @param params the parameters to reconfigure and their new values
+ */
+ public void reconfigure(@NonNull PersistableBundle params) {
+ if (mState != State.ACTIVE && mState != State.IDLE) {
+ throw new IllegalStateException();
+ }
+
+ try {
+ mAdapter.reconfigureRanging(mSessionHandle, params);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Stops actively ranging
+ *
+ * <p>A session that has been stopped may be resumed by calling
+ * {@link RangingSession#start(PersistableBundle)} without the need to open a new session.
+ *
+ * <p>Stopping a {@link RangingSession} is useful when the lower layers should not discard
+ * the parameters of the session, or when a session needs to be able to be resumed quickly.
+ *
+ * <p>If the {@link RangingSession} is no longer needed, use {@link RangingSession#close()} to
+ * completely close the session and allow lower layers of the stack to perform necessarily
+ * cleanup.
+ *
+ * <p>Stopped sessions may be closed by the system at any time. In such a case,
+ * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} is invoked.
+ *
+ * <p>On failure to stop the session,
+ * {@link RangingSession.Callback#onStopFailed(int, PersistableBundle)} is invoked.
+ */
+ public void stop() {
+ if (mState != State.ACTIVE) {
+ throw new IllegalStateException();
+ }
+
+ try {
+ mAdapter.stopRanging(mSessionHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
* Close the ranging session
- * <p>If this session is currently open, it will close and stop the session.
+ *
+ * <p>After calling this function, in order resume ranging, a new {@link RangingSession} must
+ * be opened by calling
+ * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}.
+ *
+ * <p>If this session is currently ranging, it will stop and close 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
@@ -192,7 +334,7 @@
public void close() {
if (mState == State.CLOSED) {
mExecutor.execute(() -> mCallback.onClosed(
- Callback.CLOSE_REASON_LOCAL_CLOSE_API, new PersistableBundle()));
+ Callback.REASON_LOCAL_REQUEST, new PersistableBundle()));
return;
}
@@ -206,32 +348,114 @@
/**
* @hide
*/
+ public void onRangingOpened() {
+ if (mState == State.CLOSED) {
+ Log.w(TAG, "onRangingOpened invoked for a closed session");
+ return;
+ }
+
+ mState = State.IDLE;
+ executeCallback(() -> mCallback.onOpened(this));
+ }
+
+ /**
+ * @hide
+ */
+ public void onRangingOpenFailed(@Callback.Reason int reason,
+ @NonNull PersistableBundle params) {
+ if (mState == State.CLOSED) {
+ Log.w(TAG, "onRangingOpenFailed invoked for a closed session");
+ return;
+ }
+
+ mState = State.CLOSED;
+ executeCallback(() -> mCallback.onOpenFailed(reason, params));
+ }
+
+ /**
+ * @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);
- }
+ mState = State.ACTIVE;
+ executeCallback(() -> mCallback.onStarted(parameters));
}
/**
* @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);
+ public void onRangingStartFailed(@Callback.Reason int reason,
+ @NonNull PersistableBundle params) {
+ if (mState == State.CLOSED) {
+ Log.w(TAG, "onRangingStartFailed invoked for a closed session");
+ return;
}
+
+ executeCallback(() -> mCallback.onStartFailed(reason, params));
+ }
+
+ /**
+ * @hide
+ */
+ public void onRangingReconfigured(@NonNull PersistableBundle params) {
+ if (mState == State.CLOSED) {
+ Log.w(TAG, "onRangingReconfigured invoked for a closed session");
+ return;
+ }
+
+ executeCallback(() -> mCallback.onReconfigured(params));
+ }
+
+ /**
+ * @hide
+ */
+ public void onRangingReconfigureFailed(@Callback.Reason int reason,
+ @NonNull PersistableBundle params) {
+ if (mState == State.CLOSED) {
+ Log.w(TAG, "onRangingReconfigureFailed invoked for a closed session");
+ return;
+ }
+
+ executeCallback(() -> mCallback.onReconfigureFailed(reason, params));
+ }
+
+ /**
+ * @hide
+ */
+ public void onRangingStopped() {
+ if (mState == State.CLOSED) {
+ Log.w(TAG, "onRangingStopped invoked for a closed session");
+ return;
+ }
+
+ mState = State.IDLE;
+ executeCallback(() -> mCallback.onStopped());
+ }
+
+ /**
+ * @hide
+ */
+ public void onRangingStopFailed(@Callback.Reason int reason,
+ @NonNull PersistableBundle params) {
+ if (mState == State.CLOSED) {
+ Log.w(TAG, "onRangingStopFailed invoked for a closed session");
+ return;
+ }
+
+ executeCallback(() -> mCallback.onStopFailed(reason, params));
+ }
+
+ /**
+ * @hide
+ */
+ public void onRangingClosed(@Callback.Reason int reason,
+ @NonNull PersistableBundle parameters) {
+ mState = State.CLOSED;
+ executeCallback(() -> mCallback.onClosed(reason, parameters));
}
/**
@@ -243,9 +467,16 @@
return;
}
+ executeCallback(() -> mCallback.onReportReceived(report));
+ }
+
+ /**
+ * @hide
+ */
+ private void executeCallback(@NonNull Runnable runnable) {
final long identity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> mCallback.onReportReceived(report));
+ mExecutor.execute(runnable);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/core/java/android/uwb/StartFailureReason.aidl b/core/java/android/uwb/StartFailureReason.aidl
deleted file mode 100644
index 4d9c962..0000000
--- a/core/java/android/uwb/StartFailureReason.aidl
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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;
-
-/**
- * @hide
- */
-@Backing(type="int")
-enum StartFailureReason {
- /**
- * Unknown start failure reason
- */
- UNKNOWN,
-
- /**
- * The provided parameters were invalid and ranging could not start
- */
- BAD_PARAMETERS,
-
- /**
- * The maximum number of sessions has been reached. This error may be generated
- * for an active session if a higher priority session begins.
- */
- MAX_SESSIONS_REACHED,
-
- /**
- * The system state has changed resulting in the session ending (e.g. the user
- * disables UWB, or the user's locale changes and an active channel is no longer
- * permitted to be used).
- */
- SYSTEM_POLICY,
-
- /**
- * The session could not start because of a protocol specific reason.
- */
- PROTOCOL_SPECIFIC,
-}
-
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index f4d8018..15ee5b5 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -369,13 +369,13 @@
/**
* Open a {@link RangingSession} with the given parameters
- * <p>This function is asynchronous and will return before ranging begins. The
- * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)} function is
- * called with a {@link RangingSession} object used to control ranging when the session is
- * successfully opened.
+ * <p>The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a
+ * {@link RangingSession} object used to control ranging when the session is successfully
+ * opened.
*
- * <p>If a session cannot be opened, then {@link RangingSession.Callback#onClosed(int)} will be
- * invoked with the appropriate {@link RangingSession.Callback.CloseReason}.
+ * <p>If a session cannot be opened, then
+ * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} will be invoked with the
+ * appropriate {@link RangingSession.Callback.Reason}.
*
* <p>An open {@link RangingSession} will be automatically closed if client application process
* dies.
@@ -391,7 +391,7 @@
* @return an {@link AutoCloseable} that is able to be used to close or cancel the opening of a
* {@link RangingSession} that has been requested through {@link #openRangingSession}
* but has not yet been made available by
- * {@link RangingSession.Callback#onOpenSuccess}.
+ * {@link RangingSession.Callback#onOpened(RangingSession)}.
*/
@NonNull
public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters,
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c87e51e..37f0a64 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -519,8 +519,13 @@
/** Key code constant: Settings key.
* Starts the system settings activity. */
public static final int KEYCODE_SETTINGS = 176;
- /** Key code constant: TV power key.
- * On TV remotes, toggles the power on a television screen. */
+ /**
+ * Key code constant: TV power key.
+ * On HDMI TV panel devices and Android TV devices that don't support HDMI, toggles the power
+ * state of the device.
+ * On HDMI source devices, toggles the power state of the HDMI-connected TV via HDMI-CEC and
+ * makes the source device follow this power state.
+ */
public static final int KEYCODE_TV_POWER = 177;
/** Key code constant: TV input key.
* On TV remotes, switches the input on a television screen. */
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index f642d75..2f97357 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -333,6 +333,8 @@
*/
public long mNativeObject;
private long mNativeHandle;
+ private boolean mDebugRelease = false;
+ private Throwable mReleaseStack = null;
// TODO: Move width/height to native and fix locking through out.
private final Object mLock = new Object();
@@ -580,6 +582,13 @@
}
mNativeObject = nativeObject;
mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
+ if (mNativeObject == 0) {
+ if (mDebugRelease) {
+ mReleaseStack = new Throwable("assigned zero nativeObject here");
+ }
+ } else {
+ mReleaseStack = null;
+ }
}
/**
@@ -590,6 +599,7 @@
mWidth = other.mWidth;
mHeight = other.mHeight;
mLocalOwnerView = other.mLocalOwnerView;
+ mDebugRelease = other.mDebugRelease;
assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite);
}
@@ -1419,6 +1429,7 @@
mName = in.readString8();
mWidth = in.readInt();
mHeight = in.readInt();
+ mDebugRelease = in.readBoolean();
long object = 0;
if (in.readInt() != 0) {
@@ -1437,8 +1448,12 @@
dest.writeString8(mName);
dest.writeInt(mWidth);
dest.writeInt(mHeight);
+ dest.writeBoolean(mDebugRelease);
if (mNativeObject == 0) {
dest.writeInt(0);
+ if (mReleaseStack != null) {
+ Log.w(TAG, "Sending invalid " + this + " caused by:", mReleaseStack);
+ }
} else {
dest.writeInt(1);
}
@@ -1450,6 +1465,13 @@
}
/**
+ * @hide
+ */
+ public void setDebugRelease(boolean debug) {
+ mDebugRelease = debug;
+ }
+
+ /**
* Checks whether two {@link SurfaceControl} objects represent the same surface.
*
* @param other The other object to check
@@ -1519,6 +1541,9 @@
nativeRelease(mNativeObject);
mNativeObject = 0;
mNativeHandle = 0;
+ if (mDebugRelease) {
+ mReleaseStack = new Throwable("released here");
+ }
mCloseGuard.close();
}
}
@@ -1534,8 +1559,11 @@
}
private void checkNotReleased() {
- if (mNativeObject == 0) throw new NullPointerException(
- "Invalid " + this + ", mNativeObject is null. Have you called release() already?");
+ if (mNativeObject == 0) {
+ Log.wtf(TAG, "Invalid " + this + " caused by:", mReleaseStack);
+ throw new NullPointerException(
+ "mNativeObject of " + this + " is null. Have you called release() already?");
+ }
}
/**
@@ -2383,6 +2411,7 @@
public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf) {
long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject);
SurfaceControl sc = new SurfaceControl();
+ sc.mDebugRelease = mirrorOf.mDebugRelease;
sc.assignNativeObject(nativeObj, "mirrorSurface");
return sc;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0f53215..e8584da 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8821,6 +8821,7 @@
structure.setAutofillValue(getAutofillValue());
}
structure.setImportantForAutofill(getImportantForAutofill());
+ structure.setOnReceiveContentMimeTypes(getOnReceiveContentMimeTypes());
}
int ignoredParentLeft = 0;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d863ea5..0097b2e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3150,6 +3150,8 @@
mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
+ final boolean wasReportNextDraw = mReportNextDraw;
+
// Remember if we must report the next draw.
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw();
@@ -3177,12 +3179,25 @@
if (isViewVisible) {
// Try again
scheduleTraversals();
- } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
- for (int i = 0; i < mPendingTransitions.size(); ++i) {
- mPendingTransitions.get(i).endChangingAnimations();
+ } else {
+ if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
+ for (int i = 0; i < mPendingTransitions.size(); ++i) {
+ mPendingTransitions.get(i).endChangingAnimations();
+ }
+ mPendingTransitions.clear();
}
- mPendingTransitions.clear();
+
+ // We may never draw since it's not visible. Report back that we're finished
+ // drawing.
+ if (!wasReportNextDraw && mReportNextDraw) {
+ mReportNextDraw = false;
+ pendingDrawFinished();
+ }
}
+
+ // We were unable to draw this traversal. Unset this flag since we'll block without
+ // ever being able to draw again
+ mNextDrawUseBlastSync = false;
}
if (mAttachInfo.mContentCaptureEvents != null) {
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 29ce231..f5aa97a 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -372,6 +372,14 @@
public void setImportantForAutofill(@AutofillImportance int mode) {}
/**
+ * Sets the MIME types accepted by this view. See {@link View#getOnReceiveContentMimeTypes()}.
+ *
+ * <p>Should only be set when the node is used for Autofill or Content Capture purposes - it
+ * will be ignored when used for Assist.
+ */
+ public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) {}
+
+ /**
* Sets the {@link android.text.InputType} bits of this node.
*
* @param inputType inputType bits as defined by {@link android.text.InputType}.
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index e731d4b..4e9c229 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -81,6 +81,7 @@
private static final long FLAGS_HAS_AUTOFILL_HINTS = 1L << 33;
private static final long FLAGS_HAS_AUTOFILL_OPTIONS = 1L << 34;
private static final long FLAGS_HAS_HINT_ID_ENTRY = 1L << 35;
+ private static final long FLAGS_HAS_MIME_TYPES = 1L << 36;
/** Flags used to optimize what's written to the parcel */
private long mFlags;
@@ -113,6 +114,7 @@
private String[] mAutofillHints;
private AutofillValue mAutofillValue;
private CharSequence[] mAutofillOptions;
+ private String[] mOnReceiveContentMimeTypes;
/** @hide */
public ViewNode() {
@@ -169,6 +171,9 @@
if ((nodeFlags & FLAGS_HAS_LOCALE_LIST) != 0) {
mLocaleList = parcel.readParcelable(null);
}
+ if ((nodeFlags & FLAGS_HAS_MIME_TYPES) != 0) {
+ mOnReceiveContentMimeTypes = parcel.readStringArray();
+ }
if ((nodeFlags & FLAGS_HAS_INPUT_TYPE) != 0) {
mInputType = parcel.readInt();
}
@@ -463,6 +468,12 @@
return mAutofillOptions;
}
+ @Override
+ @Nullable
+ public String[] getOnReceiveContentMimeTypes() {
+ return mOnReceiveContentMimeTypes;
+ }
+
@Nullable
@Override
public LocaleList getLocaleList() {
@@ -508,6 +519,9 @@
if (mLocaleList != null) {
nodeFlags |= FLAGS_HAS_LOCALE_LIST;
}
+ if (mOnReceiveContentMimeTypes != null) {
+ nodeFlags |= FLAGS_HAS_MIME_TYPES;
+ }
if (mInputType != 0) {
nodeFlags |= FLAGS_HAS_INPUT_TYPE;
}
@@ -584,6 +598,9 @@
if ((nodeFlags & FLAGS_HAS_LOCALE_LIST) != 0) {
parcel.writeParcelable(mLocaleList, 0);
}
+ if ((nodeFlags & FLAGS_HAS_MIME_TYPES) != 0) {
+ parcel.writeStringArray(mOnReceiveContentMimeTypes);
+ }
if ((nodeFlags & FLAGS_HAS_INPUT_TYPE) != 0) {
parcel.writeInt(mInputType);
}
@@ -912,6 +929,11 @@
}
@Override
+ public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) {
+ mNode.mOnReceiveContentMimeTypes = mimeTypes;
+ }
+
+ @Override
public void setAutofillHints(String[] hints) {
mNode.mAutofillHints = hints;
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0025d1e..26dd5e3 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -99,6 +99,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnReceiveContentListener;
import android.view.SubMenu;
import android.view.View;
import android.view.View.DragShadowBuilder;
@@ -588,8 +589,18 @@
mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
}
- @VisibleForTesting
- public @NonNull TextViewOnReceiveContentListener getDefaultOnReceiveContentListener() {
+ /**
+ * Returns the default handler for receiving content in an editable {@link TextView}. This
+ * listener impl is used to encapsulate the default behavior but it is not part of the public
+ * API. If an app wants to execute the default platform behavior for receiving content, it
+ * should call {@link View#onReceiveContent}. Alternatively, if an app implements a custom
+ * listener for receiving content and wants to delegate some of the content to be handled by
+ * the platform, it should return the corresponding content from its listener. See
+ * {@link View#setOnReceiveContentListener} and {@link OnReceiveContentListener} for more info.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @NonNull
+ public TextViewOnReceiveContentListener getDefaultOnReceiveContentListener() {
return mDefaultOnReceiveContentListener;
}
@@ -6613,6 +6624,9 @@
}
updateSelection(event);
+ if (mTextView.hasSelection() && mEndHandle != null) {
+ mEndHandle.updateMagnifier(event);
+ }
break;
case MotionEvent.ACTION_UP:
@@ -6623,6 +6637,9 @@
break;
}
updateSelection(event);
+ if (mEndHandle != null) {
+ mEndHandle.dismissMagnifier();
+ }
// No longer dragging to select text, let the parent intercept events.
mTextView.getParent().requestDisallowInterceptTouchEvent(false);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5176b7e..8dafc5d 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -18,11 +18,13 @@
import android.annotation.ColorInt;
import android.annotation.DimenRes;
+import android.annotation.DrawableRes;
import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.Px;
import android.annotation.StyleRes;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -332,7 +334,7 @@
/**
* @hide
*/
- public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) {
+ public void setRemoteInputs(@IdRes int viewId, RemoteInput[] remoteInputs) {
mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
}
@@ -368,7 +370,7 @@
*
* @hide
*/
- public void setIntTag(int viewId, int key, int tag) {
+ public void setIntTag(@IdRes int viewId, @IdRes int key, int tag) {
addAction(new SetIntTagAction(viewId, key, tag));
}
@@ -524,6 +526,7 @@
// Nothing to visit by default
}
+ @IdRes
@UnsupportedAppUsage
int viewId;
}
@@ -648,7 +651,7 @@
private class SetEmptyView extends Action {
int emptyViewId;
- SetEmptyView(int viewId, int emptyViewId) {
+ SetEmptyView(@IdRes int viewId, @IdRes int emptyViewId) {
this.viewId = viewId;
this.emptyViewId = emptyViewId;
}
@@ -683,7 +686,7 @@
}
private class SetPendingIntentTemplate extends Action {
- public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
+ public SetPendingIntentTemplate(@IdRes int id, PendingIntent pendingIntentTemplate) {
this.viewId = id;
this.pendingIntentTemplate = pendingIntentTemplate;
}
@@ -754,7 +757,8 @@
}
private class SetRemoteViewsAdapterList extends Action {
- public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
+ public SetRemoteViewsAdapterList(@IdRes int id, ArrayList<RemoteViews> list,
+ int viewTypeCount) {
this.viewId = id;
this.list = list;
this.viewTypeCount = viewTypeCount;
@@ -819,7 +823,7 @@
}
private class SetRemoteViewsAdapterIntent extends Action {
- public SetRemoteViewsAdapterIntent(int id, Intent intent) {
+ public SetRemoteViewsAdapterIntent(@IdRes int id, Intent intent) {
this.viewId = id;
this.intent = intent;
}
@@ -894,7 +898,7 @@
*/
private class SetOnClickResponse extends Action {
- SetOnClickResponse(int id, RemoteResponse response) {
+ SetOnClickResponse(@IdRes int id, RemoteResponse response) {
this.viewId = id;
this.mResponse = response;
}
@@ -1056,8 +1060,8 @@
* <p>
*/
private class SetDrawableTint extends Action {
- SetDrawableTint(int id, boolean targetBackground,
- int colorFilter, @NonNull PorterDuff.Mode mode) {
+ SetDrawableTint(@IdRes int id, boolean targetBackground,
+ @ColorInt int colorFilter, @NonNull PorterDuff.Mode mode) {
this.viewId = id;
this.targetBackground = targetBackground;
this.colorFilter = colorFilter;
@@ -1103,7 +1107,7 @@
}
boolean targetBackground;
- int colorFilter;
+ @ColorInt int colorFilter;
PorterDuff.Mode filterMode;
}
@@ -1120,7 +1124,7 @@
ColorStateList mColorStateList;
- SetRippleDrawableColor(int id, ColorStateList colorStateList) {
+ SetRippleDrawableColor(@IdRes int id, ColorStateList colorStateList) {
this.viewId = id;
this.mColorStateList = colorStateList;
}
@@ -1157,7 +1161,7 @@
private final class ViewContentNavigation extends Action {
final boolean mNext;
- ViewContentNavigation(int viewId, boolean next) {
+ ViewContentNavigation(@IdRes int viewId, boolean next) {
this.viewId = viewId;
this.mNext = next;
}
@@ -1254,7 +1258,7 @@
@UnsupportedAppUsage
String methodName;
- BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
+ BitmapReflectionAction(@IdRes int viewId, String methodName, Bitmap bitmap) {
this.bitmap = bitmap;
this.viewId = viewId;
this.methodName = methodName;
@@ -1323,7 +1327,7 @@
@UnsupportedAppUsage
Object value;
- ReflectionAction(int viewId, String methodName, int type, Object value) {
+ ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) {
this.viewId = viewId;
this.methodName = methodName;
this.type = type;
@@ -1617,11 +1621,11 @@
private RemoteViews mNestedViews;
private int mIndex;
- ViewGroupActionAdd(int viewId, RemoteViews nestedViews) {
+ ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews) {
this(viewId, nestedViews, -1 /* index */);
}
- ViewGroupActionAdd(int viewId, RemoteViews nestedViews, int index) {
+ ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index) {
this.viewId = viewId;
mNestedViews = nestedViews;
mIndex = index;
@@ -1731,11 +1735,11 @@
private int mViewIdToKeep;
- ViewGroupActionRemove(int viewId) {
+ ViewGroupActionRemove(@IdRes int viewId) {
this(viewId, REMOVE_ALL_VIEWS_ID);
}
- ViewGroupActionRemove(int viewId, int viewIdToKeep) {
+ ViewGroupActionRemove(@IdRes int viewId, @IdRes int viewIdToKeep) {
this.viewId = viewId;
mViewIdToKeep = viewIdToKeep;
}
@@ -1903,7 +1907,8 @@
* (s/t/e/b) or cardinal (l/t/r/b) arrangement.
*/
private class TextViewDrawableAction extends Action {
- public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
+ public TextViewDrawableAction(@IdRes int viewId, boolean isRelative, @DrawableRes int d1,
+ @DrawableRes int d2, @DrawableRes int d3, @DrawableRes int d4) {
this.viewId = viewId;
this.isRelative = isRelative;
this.useIcons = false;
@@ -1913,7 +1918,7 @@
this.d4 = d4;
}
- public TextViewDrawableAction(int viewId, boolean isRelative,
+ public TextViewDrawableAction(@IdRes int viewId, boolean isRelative,
Icon i1, Icon i2, Icon i3, Icon i4) {
this.viewId = viewId;
this.isRelative = isRelative;
@@ -2086,7 +2091,8 @@
* Helper action to set padding on a View.
*/
private class ViewPaddingAction extends Action {
- public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
+ public ViewPaddingAction(@IdRes int viewId, @Px int left, @Px int top,
+ @Px int right, @Px int bottom) {
this.viewId = viewId;
this.left = left;
this.top = top;
@@ -2122,7 +2128,7 @@
return VIEW_PADDING_ACTION_TAG;
}
- int left, top, right, bottom;
+ @Px int left, top, right, bottom;
}
/**
@@ -2281,7 +2287,7 @@
*/
private class SetRemoteInputsAction extends Action {
- public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) {
+ public SetRemoteInputsAction(@IdRes int viewId, RemoteInput[] remoteInputs) {
this.viewId = viewId;
this.remoteInputs = remoteInputs;
}
@@ -2359,11 +2365,11 @@
}
private class SetIntTagAction extends Action {
- private final int mViewId;
- private final int mKey;
+ @IdRes private final int mViewId;
+ @IdRes private final int mKey;
private final int mTag;
- SetIntTagAction(int viewId, int key, int tag) {
+ SetIntTagAction(@IdRes int viewId, @IdRes int key, int tag) {
mViewId = viewId;
mKey = key;
mTag = tag;
@@ -2697,7 +2703,7 @@
* @param viewId The id of the parent {@link ViewGroup} to add child into.
* @param nestedView {@link RemoteViews} that describes the child.
*/
- public void addView(int viewId, RemoteViews nestedView) {
+ public void addView(@IdRes int viewId, RemoteViews nestedView) {
// Clear all children when nested views omitted
addAction(nestedView == null
? new ViewGroupActionRemove(viewId)
@@ -2715,7 +2721,7 @@
* @hide
*/
@UnsupportedAppUsage
- public void addView(int viewId, RemoteViews nestedView, int index) {
+ public void addView(@IdRes int viewId, RemoteViews nestedView, int index) {
addAction(new ViewGroupActionAdd(viewId, nestedView, index));
}
@@ -2725,7 +2731,7 @@
* @param viewId The id of the parent {@link ViewGroup} to remove all
* children from.
*/
- public void removeAllViews(int viewId) {
+ public void removeAllViews(@IdRes int viewId) {
addAction(new ViewGroupActionRemove(viewId));
}
@@ -2738,7 +2744,7 @@
*
* @hide
*/
- public void removeAllViewsExceptId(int viewId, int viewIdToKeep) {
+ public void removeAllViewsExceptId(@IdRes int viewId, @IdRes int viewIdToKeep) {
addAction(new ViewGroupActionRemove(viewId, viewIdToKeep));
}
@@ -2759,7 +2765,7 @@
*
* @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
*/
- public void showNext(int viewId) {
+ public void showNext(@IdRes int viewId) {
addAction(new ViewContentNavigation(viewId, true /* next */));
}
@@ -2768,7 +2774,7 @@
*
* @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
*/
- public void showPrevious(int viewId) {
+ public void showPrevious(@IdRes int viewId) {
addAction(new ViewContentNavigation(viewId, false /* next */));
}
@@ -2778,7 +2784,7 @@
* @param viewId The id of the view on which to call
* {@link AdapterViewAnimator#setDisplayedChild(int)}
*/
- public void setDisplayedChild(int viewId, int childIndex) {
+ public void setDisplayedChild(@IdRes int viewId, int childIndex) {
setInt(viewId, "setDisplayedChild", childIndex);
}
@@ -2788,7 +2794,7 @@
* @param viewId The id of the view whose visibility should change
* @param visibility The new visibility for the view
*/
- public void setViewVisibility(int viewId, int visibility) {
+ public void setViewVisibility(@IdRes int viewId, @View.Visibility int visibility) {
setInt(viewId, "setVisibility", visibility);
}
@@ -2798,7 +2804,7 @@
* @param viewId The id of the view whose text should change
* @param text The new text for the view
*/
- public void setTextViewText(int viewId, CharSequence text) {
+ public void setTextViewText(@IdRes int viewId, CharSequence text) {
setCharSequence(viewId, "setText", text);
}
@@ -2809,7 +2815,7 @@
* @param units The units of size (e.g. COMPLEX_UNIT_SP)
* @param size The size of the text
*/
- public void setTextViewTextSize(int viewId, int units, float size) {
+ public void setTextViewTextSize(@IdRes int viewId, int units, float size) {
addAction(new TextViewSizeAction(viewId, units, size));
}
@@ -2823,7 +2829,8 @@
* @param right The id of a drawable to place to the right of the text, or 0
* @param bottom The id of a drawable to place below the text, or 0
*/
- public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
+ public void setTextViewCompoundDrawables(@IdRes int viewId, @DrawableRes int left,
+ @DrawableRes int top, @DrawableRes int right, @DrawableRes int bottom) {
addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
}
@@ -2838,7 +2845,8 @@
* @param end The id of a drawable to place after the text, or 0
* @param bottom The id of a drawable to place below the text, or 0
*/
- public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
+ public void setTextViewCompoundDrawablesRelative(@IdRes int viewId, @DrawableRes int start,
+ @DrawableRes int top, @DrawableRes int end, @DrawableRes int bottom) {
addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
}
@@ -2855,7 +2863,8 @@
*
* @hide
*/
- public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) {
+ public void setTextViewCompoundDrawables(@IdRes int viewId,
+ Icon left, Icon top, Icon right, Icon bottom) {
addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
}
@@ -2873,7 +2882,8 @@
*
* @hide
*/
- public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) {
+ public void setTextViewCompoundDrawablesRelative(@IdRes int viewId,
+ Icon start, Icon top, Icon end, Icon bottom) {
addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
}
@@ -2883,7 +2893,7 @@
* @param viewId The id of the view whose drawable should change
* @param srcId The new resource id for the drawable
*/
- public void setImageViewResource(int viewId, int srcId) {
+ public void setImageViewResource(@IdRes int viewId, @DrawableRes int srcId) {
setInt(viewId, "setImageResource", srcId);
}
@@ -2893,7 +2903,7 @@
* @param viewId The id of the view whose drawable should change
* @param uri The Uri for the image
*/
- public void setImageViewUri(int viewId, Uri uri) {
+ public void setImageViewUri(@IdRes int viewId, Uri uri) {
setUri(viewId, "setImageURI", uri);
}
@@ -2903,7 +2913,7 @@
* @param viewId The id of the view whose bitmap should change
* @param bitmap The new Bitmap for the drawable
*/
- public void setImageViewBitmap(int viewId, Bitmap bitmap) {
+ public void setImageViewBitmap(@IdRes int viewId, Bitmap bitmap) {
setBitmap(viewId, "setImageBitmap", bitmap);
}
@@ -2913,7 +2923,7 @@
* @param viewId The id of the view whose bitmap should change
* @param icon The new Icon for the ImageView
*/
- public void setImageViewIcon(int viewId, Icon icon) {
+ public void setImageViewIcon(@IdRes int viewId, Icon icon) {
setIcon(viewId, "setImageIcon", icon);
}
@@ -2923,7 +2933,7 @@
* @param viewId The id of the view on which to set the empty view
* @param emptyViewId The view id of the empty view
*/
- public void setEmptyView(int viewId, int emptyViewId) {
+ public void setEmptyView(@IdRes int viewId, @IdRes int emptyViewId) {
addAction(new SetEmptyView(viewId, emptyViewId));
}
@@ -2943,7 +2953,7 @@
*
* @see #setChronometerCountDown(int, boolean)
*/
- public void setChronometer(int viewId, long base, String format, boolean started) {
+ public void setChronometer(@IdRes int viewId, long base, String format, boolean started) {
setLong(viewId, "setBase", base);
setString(viewId, "setFormat", format);
setBoolean(viewId, "setStarted", started);
@@ -2957,7 +2967,7 @@
* @param isCountDown True if you want the chronometer to count down to base instead of
* counting up.
*/
- public void setChronometerCountDown(int viewId, boolean isCountDown) {
+ public void setChronometerCountDown(@IdRes int viewId, boolean isCountDown) {
setBoolean(viewId, "setCountDown", isCountDown);
}
@@ -2974,7 +2984,7 @@
* @param indeterminate True if the progress bar is indeterminate,
* false if not.
*/
- public void setProgressBar(int viewId, int max, int progress,
+ public void setProgressBar(@IdRes int viewId, int max, int progress,
boolean indeterminate) {
setBoolean(viewId, "setIndeterminate", indeterminate);
if (!indeterminate) {
@@ -3000,7 +3010,7 @@
* @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
* @param pendingIntent The {@link PendingIntent} to send when user clicks
*/
- public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
+ public void setOnClickPendingIntent(@IdRes int viewId, PendingIntent pendingIntent) {
setOnClickResponse(viewId, RemoteResponse.fromPendingIntent(pendingIntent));
}
@@ -3012,7 +3022,7 @@
* @param viewId The id of the view that will trigger the {@link RemoteResponse} when clicked
* @param response The {@link RemoteResponse} to send when user clicks
*/
- public void setOnClickResponse(int viewId, @NonNull RemoteResponse response) {
+ public void setOnClickResponse(@IdRes int viewId, @NonNull RemoteResponse response) {
addAction(new SetOnClickResponse(viewId, response));
}
@@ -3028,7 +3038,7 @@
* @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
* by a child of viewId and executed when that child is clicked
*/
- public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
+ public void setPendingIntentTemplate(@IdRes int viewId, PendingIntent pendingIntentTemplate) {
addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
}
@@ -3049,7 +3059,7 @@
* @param fillInIntent The intent which will be combined with the parent's PendingIntent
* in order to determine the on-click behavior of the view specified by viewId
*/
- public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
+ public void setOnClickFillInIntent(@IdRes int viewId, Intent fillInIntent) {
setOnClickResponse(viewId, RemoteResponse.fromFillInIntent(fillInIntent));
}
@@ -3073,8 +3083,8 @@
* @param mode Specify a PorterDuff mode for this drawable, or null to leave
* unchanged.
*/
- public void setDrawableTint(int viewId, boolean targetBackground,
- int colorFilter, @NonNull PorterDuff.Mode mode) {
+ public void setDrawableTint(@IdRes int viewId, boolean targetBackground,
+ @ColorInt int colorFilter, @NonNull PorterDuff.Mode mode) {
addAction(new SetDrawableTint(viewId, targetBackground, colorFilter, mode));
}
@@ -3090,7 +3100,7 @@
* @param colorStateList Specify a color for a
* {@link ColorStateList} for this drawable.
*/
- public void setRippleDrawableColor(int viewId, ColorStateList colorStateList) {
+ public void setRippleDrawableColor(@IdRes int viewId, ColorStateList colorStateList) {
addAction(new SetRippleDrawableColor(viewId, colorStateList));
}
@@ -3101,7 +3111,7 @@
* @param viewId The id of the view whose tint should change
* @param tint the tint to apply, may be {@code null} to clear tint
*/
- public void setProgressTintList(int viewId, ColorStateList tint) {
+ public void setProgressTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setProgressTintList",
ReflectionAction.COLOR_STATE_LIST, tint));
}
@@ -3113,7 +3123,7 @@
* @param viewId The id of the view whose tint should change
* @param tint the tint to apply, may be {@code null} to clear tint
*/
- public void setProgressBackgroundTintList(int viewId, ColorStateList tint) {
+ public void setProgressBackgroundTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
ReflectionAction.COLOR_STATE_LIST, tint));
}
@@ -3125,7 +3135,7 @@
* @param viewId The id of the view whose tint should change
* @param tint the tint to apply, may be {@code null} to clear tint
*/
- public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) {
+ public void setProgressIndeterminateTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
ReflectionAction.COLOR_STATE_LIST, tint));
}
@@ -3137,7 +3147,7 @@
* @param color Sets the text color for all the states (normal, selected,
* focused) to be this color.
*/
- public void setTextColor(int viewId, @ColorInt int color) {
+ public void setTextColor(@IdRes int viewId, @ColorInt int color) {
setInt(viewId, "setTextColor", color);
}
@@ -3148,7 +3158,7 @@
* @param viewId The id of the view whose text color should change
* @param colors the text colors to set
*/
- public void setTextColor(int viewId, @ColorInt ColorStateList colors) {
+ public void setTextColor(@IdRes int viewId, ColorStateList colors) {
addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST,
colors));
}
@@ -3165,7 +3175,7 @@
* {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
*/
@Deprecated
- public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
+ public void setRemoteAdapter(int appWidgetId, @IdRes int viewId, Intent intent) {
setRemoteAdapter(viewId, intent);
}
@@ -3177,7 +3187,7 @@
* @param intent The intent of the service which will be
* providing data to the RemoteViewsAdapter
*/
- public void setRemoteAdapter(int viewId, Intent intent) {
+ public void setRemoteAdapter(@IdRes int viewId, Intent intent) {
addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
}
@@ -3205,7 +3215,8 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Deprecated
- public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
+ public void setRemoteAdapter(@IdRes int viewId, ArrayList<RemoteViews> list,
+ int viewTypeCount) {
addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
}
@@ -3215,7 +3226,7 @@
* @param viewId The id of the view to change
* @param position Scroll to this adapter position
*/
- public void setScrollPosition(int viewId, int position) {
+ public void setScrollPosition(@IdRes int viewId, int position) {
setInt(viewId, "smoothScrollToPosition", position);
}
@@ -3225,7 +3236,7 @@
* @param viewId The id of the view to change
* @param offset Scroll by this adapter position offset
*/
- public void setRelativeScrollPosition(int viewId, int offset) {
+ public void setRelativeScrollPosition(@IdRes int viewId, int offset) {
setInt(viewId, "smoothScrollByOffset", offset);
}
@@ -3238,7 +3249,8 @@
* @param right the right padding in pixels
* @param bottom the bottom padding in pixels
*/
- public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
+ public void setViewPadding(@IdRes int viewId,
+ @Px int left, @Px int top, @Px int right, @Px int bottom) {
addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
}
@@ -3340,7 +3352,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setBoolean(int viewId, String methodName, boolean value) {
+ public void setBoolean(@IdRes int viewId, String methodName, boolean value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
}
@@ -3351,7 +3363,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setByte(int viewId, String methodName, byte value) {
+ public void setByte(@IdRes int viewId, String methodName, byte value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
}
@@ -3362,7 +3374,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setShort(int viewId, String methodName, short value) {
+ public void setShort(@IdRes int viewId, String methodName, short value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
}
@@ -3373,7 +3385,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setInt(int viewId, String methodName, int value) {
+ public void setInt(@IdRes int viewId, String methodName, int value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
}
@@ -3386,7 +3398,7 @@
*
* @hide
*/
- public void setColorStateList(int viewId, String methodName, ColorStateList value) {
+ public void setColorStateList(@IdRes int viewId, String methodName, ColorStateList value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.COLOR_STATE_LIST,
value));
}
@@ -3399,7 +3411,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setLong(int viewId, String methodName, long value) {
+ public void setLong(@IdRes int viewId, String methodName, long value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
}
@@ -3410,7 +3422,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setFloat(int viewId, String methodName, float value) {
+ public void setFloat(@IdRes int viewId, String methodName, float value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
}
@@ -3421,7 +3433,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setDouble(int viewId, String methodName, double value) {
+ public void setDouble(@IdRes int viewId, String methodName, double value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
}
@@ -3432,7 +3444,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setChar(int viewId, String methodName, char value) {
+ public void setChar(@IdRes int viewId, String methodName, char value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
}
@@ -3443,7 +3455,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setString(int viewId, String methodName, String value) {
+ public void setString(@IdRes int viewId, String methodName, String value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
}
@@ -3454,7 +3466,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setCharSequence(int viewId, String methodName, CharSequence value) {
+ public void setCharSequence(@IdRes int viewId, String methodName, CharSequence value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
}
@@ -3465,7 +3477,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setUri(int viewId, String methodName, Uri value) {
+ public void setUri(@IdRes int viewId, String methodName, Uri value) {
if (value != null) {
// Resolve any filesystem path before sending remotely
value = value.getCanonicalUri();
@@ -3486,7 +3498,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setBitmap(int viewId, String methodName, Bitmap value) {
+ public void setBitmap(@IdRes int viewId, String methodName, Bitmap value) {
addAction(new BitmapReflectionAction(viewId, methodName, value));
}
@@ -3497,7 +3509,7 @@
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
- public void setBundle(int viewId, String methodName, Bundle value) {
+ public void setBundle(@IdRes int viewId, String methodName, Bundle value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
}
@@ -3508,7 +3520,7 @@
* @param methodName The name of the method to call.
* @param value The {@link android.content.Intent} to pass the method.
*/
- public void setIntent(int viewId, String methodName, Intent value) {
+ public void setIntent(@IdRes int viewId, String methodName, Intent value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
}
@@ -3519,7 +3531,7 @@
* @param methodName The name of the method to call.
* @param value The {@link android.graphics.drawable.Icon} to pass the method.
*/
- public void setIcon(int viewId, String methodName, Icon value) {
+ public void setIcon(@IdRes int viewId, String methodName, Icon value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
}
@@ -3529,7 +3541,7 @@
* @param viewId The id of the view whose content description should change.
* @param contentDescription The new content description for the view.
*/
- public void setContentDescription(int viewId, CharSequence contentDescription) {
+ public void setContentDescription(@IdRes int viewId, CharSequence contentDescription) {
setCharSequence(viewId, "setContentDescription", contentDescription);
}
@@ -3539,7 +3551,7 @@
* @param viewId The id of the view whose before view in accessibility traversal to set.
* @param nextId The id of the next in the accessibility traversal.
**/
- public void setAccessibilityTraversalBefore(int viewId, int nextId) {
+ public void setAccessibilityTraversalBefore(@IdRes int viewId, @IdRes int nextId) {
setInt(viewId, "setAccessibilityTraversalBefore", nextId);
}
@@ -3549,7 +3561,7 @@
* @param viewId The id of the view whose after view in accessibility traversal to set.
* @param nextId The id of the next in the accessibility traversal.
**/
- public void setAccessibilityTraversalAfter(int viewId, int nextId) {
+ public void setAccessibilityTraversalAfter(@IdRes int viewId, @IdRes int nextId) {
setInt(viewId, "setAccessibilityTraversalAfter", nextId);
}
@@ -3559,7 +3571,7 @@
* @param viewId The id of the view whose property to set.
* @param labeledId The id of a view for which this view serves as a label.
*/
- public void setLabelFor(int viewId, int labeledId) {
+ public void setLabelFor(@IdRes int viewId, @IdRes int labeledId) {
setInt(viewId, "setLabelFor", labeledId);
}
@@ -4083,7 +4095,7 @@
}
}
- public ViewTree findViewTreeById(int id) {
+ public ViewTree findViewTreeById(@IdRes int id) {
if (mRoot.getId() == id) {
return this;
}
@@ -4121,7 +4133,7 @@
createTree();
}
- public <T extends View> T findViewById(int id) {
+ public <T extends View> T findViewById(@IdRes int id) {
if (mChildren == null) {
return mRoot.findViewById(id);
}
@@ -4255,7 +4267,8 @@
* @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])
*/
@NonNull
- public RemoteResponse addSharedElement(int viewId, @NonNull String sharedElementName) {
+ public RemoteResponse addSharedElement(@IdRes int viewId,
+ @NonNull String sharedElementName) {
if (mViewIds == null) {
mViewIds = new IntArray();
mElementNames = new ArrayList<>();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1599f2b..b866025 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11717,6 +11717,18 @@
}
}
}
+ String[] mimeTypes = getOnReceiveContentMimeTypes();
+ if (mimeTypes == null && mEditor != null) {
+ // If the app hasn't set a listener for receiving content on this view (ie,
+ // getOnReceiveContentMimeTypes() returns null), check if it implements the
+ // keyboard image API and, if possible, use those MIME types as fallback.
+ // This fallback is only in place for autofill, not other mechanisms for
+ // inserting content. See AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_LISTENER
+ // in TextViewOnReceiveContentListener for more info.
+ mimeTypes = mEditor.getDefaultOnReceiveContentListener()
+ .getFallbackMimeTypesForAutofill(this);
+ }
+ structure.setOnReceiveContentMimeTypes(mimeTypes);
}
if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
diff --git a/core/java/android/widget/TextViewOnReceiveContentListener.java b/core/java/android/widget/TextViewOnReceiveContentListener.java
index 8cef106..91fac55 100644
--- a/core/java/android/widget/TextViewOnReceiveContentListener.java
+++ b/core/java/android/widget/TextViewOnReceiveContentListener.java
@@ -60,7 +60,7 @@
*
* @hide
*/
-@VisibleForTesting
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final class TextViewOnReceiveContentListener implements OnReceiveContentListener {
private static final String LOG_TAG = "ReceiveContent";
@@ -261,10 +261,17 @@
mInputConnectionInfo = null;
}
- /** @hide */
- @VisibleForTesting
+ /**
+ * Returns the MIME types accepted by {@link View#performReceiveContent} for the given view,
+ * <strong>for autofill purposes</strong>. This will be non-null only if fallback to the
+ * keyboard image API {@link #isUsageOfImeCommitContentEnabled is enabled} and the view has an
+ * {@link InputConnection} with {@link EditorInfo#contentMimeTypes} populated.
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@Nullable
- public String[] getEditorInfoMimeTypes(@NonNull TextView view) {
+ public String[] getFallbackMimeTypesForAutofill(@NonNull TextView view) {
if (!isUsageOfImeCommitContentEnabled(view)) {
return null;
}
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 1254647..8784399 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -92,6 +92,14 @@
public static final int FEATURE_IME_PLACEHOLDER = FEATURE_SYSTEM_FIRST + 7;
/**
+ * Display area for one handed background layer, which preventing when user
+ * turning the Dark theme on, they can not clearly identify the screen has entered
+ * one handed mode.
+ * @hide
+ */
+ public static final int FEATURE_ONE_HANDED_BACKGROUND_PANEL = FEATURE_SYSTEM_FIRST + 8;
+
+ /**
* The last boundary of display area for system features
*/
public static final int FEATURE_SYSTEM_LAST = 10_000;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c05e7a2..8c9da66 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -308,6 +308,11 @@
linux_glibc: {
srcs: [
"android_content_res_ApkAssets.cpp",
+ "android_database_CursorWindow.cpp",
+ "android_database_SQLiteCommon.cpp",
+ "android_database_SQLiteConnection.cpp",
+ "android_database_SQLiteGlobal.cpp",
+ "android_database_SQLiteDebug.cpp",
"android_hardware_input_InputApplicationHandle.cpp",
"android_os_MessageQueue.cpp",
"android_os_Parcel.cpp",
@@ -331,6 +336,7 @@
static_libs: [
"libinput",
"libbinderthreadstateutils",
+ "libsqlite",
],
shared_libs: [
// libbinder needs to be shared since it has global state
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 4e50f87..3e513df 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -41,6 +41,10 @@
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
extern int register_android_content_res_ApkAssets(JNIEnv* env);
+extern int register_android_database_CursorWindow(JNIEnv* env);
+extern int register_android_database_SQLiteConnection(JNIEnv* env);
+extern int register_android_database_SQLiteGlobal(JNIEnv* env);
+extern int register_android_database_SQLiteDebug(JNIEnv* env);
extern int register_android_os_FileObserver(JNIEnv* env);
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_SystemClock(JNIEnv* env);
@@ -65,6 +69,11 @@
#ifdef __linux__
{"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
{"android.content.res.AssetManager", REG_JNI(register_android_content_AssetManager)},
+ {"android.database.CursorWindow", REG_JNI(register_android_database_CursorWindow)},
+ {"android.database.sqlite.SQLiteConnection",
+ REG_JNI(register_android_database_SQLiteConnection)},
+ {"android.database.sqlite.SQLiteGlobal", REG_JNI(register_android_database_SQLiteGlobal)},
+ {"android.database.sqlite.SQLiteDebug", REG_JNI(register_android_database_SQLiteDebug)},
#endif
{"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)},
{"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3156f71..efede21 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -843,7 +843,7 @@
PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
- bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false);
+ bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, true);
if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
diff --git a/core/proto/android/hardware/sensorprivacy.proto b/core/proto/android/hardware/sensorprivacy.proto
index 07e938d..401e003 100644
--- a/core/proto/android/hardware/sensorprivacy.proto
+++ b/core/proto/android/hardware/sensorprivacy.proto
@@ -25,11 +25,29 @@
message SensorPrivacyServiceDumpProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ // DEPRECATED
// Is global sensor privacy enabled
optional bool is_enabled = 1;
+ // DEPRECATED
// Per sensor privacy enabled
repeated SensorPrivacyIndividualEnabledSensorProto individual_enabled_sensor = 2;
+
+ // Per user settings for sensor privacy
+ repeated SensorPrivacyUserProto user = 3;
+}
+
+message SensorPrivacyUserProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // User id
+ optional int32 user_id = 1;
+
+ // Is global sensor privacy enabled
+ optional bool is_enabled = 2;
+
+ // Per sensor privacy enabled
+ repeated SensorPrivacyIndividualEnabledSensorProto individual_enabled_sensor = 3;
}
message SensorPrivacyIndividualEnabledSensorProto {
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 8de30f8..944edb0 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -521,6 +521,11 @@
(section).args = "power_stats --proto model"
];
+ optional com.android.server.powerstats.PowerStatsServiceResidencyProto powerstats_residency = 3056 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "power_stats --proto residency"
+ ];
+
// Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
optional android.util.TextDumpProto textdump_wifi = 4000 [
(section).type = SECTION_TEXT_DUMPSYS,
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index 9a7ed7c..30c4274 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -41,6 +41,16 @@
}
/**
+ * IncidentReportResidencyProto is used only in the parsing tool located
+ * in frameworks/base/tools which is used to parse this data out of
+ * incident reports.
+ */
+message IncidentReportResidencyProto {
+ /** Section number matches that in incident.proto */
+ optional PowerStatsServiceResidencyProto incident_report = 3056;
+}
+
+/**
* EnergyConsumer (model) data is exposed by the PowerStats HAL. This data
* represents modeled energy consumption estimates and is provided per
* subsystem. The default subsystems are defined in EnergyConsumerId.aidl.
@@ -63,6 +73,99 @@
}
/**
+ * A PowerEntity is defined as a platform subsystem, peripheral, or power domain
+ * that impacts the total device power consumption. PowerEntityInfo is
+ * information related to each power entity. Each PowerEntity may reside in one
+ * of multiple states. It may also transition from one state to another.
+ * StateResidency is defined as an accumulation of time that a PowerEntity
+ * resided in each of its possible states, the number of times that each state
+ * was entered, and a timestamp corresponding to the last time that state was
+ * entered.
+ */
+message PowerStatsServiceResidencyProto {
+ repeated PowerEntityInfoProto power_entity_info = 1;
+ repeated StateResidencyResultProto state_residency_result = 2;
+}
+
+/**
+ * Information about the possible states for a particular PowerEntity.
+ */
+message StateInfoProto {
+ /**
+ * Unique (for a given PowerEntityInfo) ID of this StateInfo
+ */
+ optional int32 state_id = 1;
+ /**
+ * Unique (for a given PowerEntityInfo) name of the state. Vendor/device specific.
+ * Opaque to framework
+ */
+ optional string state_name = 2;
+}
+
+/**
+ * A PowerEntity is defined as a platform subsystem, peripheral, or power domain
+ * that impacts the total device power consumption. PowerEntityInfo is
+ * information about a PowerEntity. It includes an array of information about
+ * each possible state of the PowerEntity.
+ */
+message PowerEntityInfoProto {
+ /**
+ * Unique ID of this PowerEntityInfo
+ */
+ optional int32 power_entity_id = 1;
+ /**
+ * Unique name of the PowerEntity. Vendor/device specific. Opaque to framework
+ */
+ optional string power_entity_name = 2;
+ /**
+ * List of states that the PowerEntity may reside in
+ */
+ repeated StateInfoProto states = 3;
+}
+
+/**
+ * StateResidency is defined as an accumulation of time that a PowerEntity
+ * resided in each of its possible states, the number of times that each state
+ * was entered, and a timestamp corresponding to the last time that state was
+ * entered. Data is accumulated starting at device boot.
+ */
+message StateResidencyProto {
+ /**
+ * ID of the state associated with this residency
+ */
+ optional int32 state_id = 1;
+ /**
+ * Total time in milliseconds that the corresponding PowerEntity resided
+ * in this state since boot
+ */
+ optional int64 total_time_in_state_ms = 2;
+ /**
+ * Total number of times that the state was entered since boot
+ */
+ optional int64 total_state_entry_count = 3;
+ /**
+ * Last time this state was entered. Time in milliseconds since boot
+ */
+ optional int64 last_entry_timestamp_ms = 4;
+}
+
+/**
+ * A StateResidencyResult is an array of StateResidencies for a particular
+ * PowerEntity. The StateResidencyResult can be matched to its corresponding
+ * PowerEntityInfo through the power_entity_id field.
+ */
+message StateResidencyResultProto {
+ /**
+ * ID of the PowerEntity associated with this result
+ */
+ optional int32 power_entity_id = 1;
+ /**
+ * Residency for each state in the PowerEntity's state space
+ */
+ repeated StateResidencyProto state_residency_data = 2;
+}
+
+/**
* Energy consumer ID:
* A list of default subsystems for which energy consumption estimates
* may be provided (hardware dependent).
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8682fea..3053518 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5317,6 +5317,12 @@
<permission android:name="android.permission.BIND_IMPRESSION_ATTESTATION_SERVICE"
android:protectionLevel="signature" />
+ <!-- @hide @TestApi Allows an application to enable/disable toast rate limiting.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/assets/images/progress_font.png b/core/res/assets/images/progress_font.png
new file mode 100644
index 0000000..78c3ed9
--- /dev/null
+++ b/core/res/assets/images/progress_font.png
Binary files differ
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 0721f8e..3adb318 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"নেটৱৰ্ক পীয়েৰে TTY ম\'ড HCOলৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"নেটৱৰ্ক পীয়েৰে TTY ম\'ড VCO লৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"নেটৱৰ্ক পীয়েৰে TTY ম\'ড OFFলৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"কণ্ঠস্বৰ"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
<string name="serviceClassData" msgid="4148080018967300248">"ডেটা"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"ফেক্স"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"এছএমএছ"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 967bb92..016e77b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1319,7 +1319,7 @@
<string name="usb_power_notification_message" msgid="7284765627437897702">"કનેક્ટ કરેલ ઉપકરણ ચાર્જ થઈ રહ્યું છે. વધુ વિકલ્પો માટે ટૅપ કરો."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"એનાલોગ ઑડિઓ ઍક્સેસરી મળી"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"જોડેલ ઉપકરણ આ ફોન સાથે સુસંગત નથી. વધુ જાણવા માટે ટૅપ કરો."</string>
- <string name="adb_active_notification_title" msgid="408390247354560331">"USB ડીબગિંગ કનેક્ટ થયું."</string>
+ <string name="adb_active_notification_title" msgid="408390247354560331">"USB ડિબગીંગ કનેક્ટ થયું."</string>
<string name="adb_active_notification_message" msgid="5617264033476778211">"USB ડિબગીંગ બંધ કરવા માટે ટૅપ કરો"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB ડિબગીંગને અક્ષમ કરવા માટે પસંદ કરો."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"વાયરલેસ ડિબગીંગ કનેક્ટ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 0df69d9..a8afbbe 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"पीयर ने टेलीटाइपराइटर (TTY) मोड एचसीओ (HCO) का अनुरोध किया"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"पीयर ने टेलीटाइपराइटर (TTY) मोड वीसीओ (VCO) का अनुरोध किया"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"पीयर ने टेलीटाइपराइटर (TTY) मोड बंद का अनुरोध किया"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"आवाज़"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
<string name="serviceClassData" msgid="4148080018967300248">"डेटा"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"फ़ैक्स"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"मैसेज (एसएमएस)"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 937645e..b7356e1 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"ಪೀರ್ ವಿನಂತಿಸಿಕೊಂಡ TTY ಮೋಡ್ HCO"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"ಪೀರ್ ವಿನಂತಿಸಿಕೊಂಡ TTY ಮೋಡ್ VCO"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"ಪೀರ್ ವಿನಂತಿಸಿಕೊಂಡ TTY ಮೋಡ್ ಆಫ್ ಆಗಿದೆ"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"ಧ್ವನಿ"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
<string name="serviceClassData" msgid="4148080018967300248">"ಡೇಟಾ"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"ಫ್ಯಾಕ್ಸ್"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index e7a1628..30c0d03 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"പിയർ അഭ്യർത്ഥിച്ച TTY മോഡ് HCO"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"പിയർ അഭ്യർത്ഥിച്ച TTY മോഡ് VCO"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"പിയർ അഭ്യർത്ഥിച്ച TTY മോഡ് \'ഓഫ്\'"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"ശബ്ദം"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
<string name="serviceClassData" msgid="4148080018967300248">"ഡാറ്റ"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"ഫാക്സ്"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index b5935e9..2124d49 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"ପୀଅର୍ ଅନୁରୋଧ କରିଥିବା TTY ମୋଡ୍ HCO ଅଟେ"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"ପୀଅର୍ ଅନୁରୋଧ କରିଥିବା TTY ମୋଡ୍ VCO ଅଟେ"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"ପୀଅର୍ ଅନୁରୋଧ କରିଥିବା TTY ମୋଡ୍ OFF ଅଛି"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"ଭଏସ୍"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
<string name="serviceClassData" msgid="4148080018967300248">"ଡାଟା"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"ଫାକ୍ସ"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 8d5e974..4518a3b 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1319,7 +1319,7 @@
<string name="usb_power_notification_message" msgid="7284765627437897702">"ਕਨੈਕਟ ਕੀਤਾ ਡੀਵਾਈਸ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ। ਹੋਰ ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"ਐਨਾਲੌਗ ਆਡੀਓ ਉਪਸਾਧਨ ਦਾ ਪਤਾ ਲੱਗਿਆ"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"ਨੱਥੀ ਕੀਤਾ ਡੀਵਾਈਸ ਇਸ ਫ਼ੋਨ ਦੇ ਅਨੁਰੂਪ ਨਹੀਂ ਹੈ। ਹੋਰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
- <string name="adb_active_notification_title" msgid="408390247354560331">"USB ਡੀਬਗਿੰਗ ਕਨੈਕਟ ਕੀਤੀ"</string>
+ <string name="adb_active_notification_title" msgid="408390247354560331">"USB ਡੀਬੱਗਿੰਗ ਕਨੈਕਟ ਕੀਤੀ"</string>
<string name="adb_active_notification_message" msgid="5617264033476778211">"USB ਡੀਬੱਗਿੰਗ ਬੰਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB ਡੀਬੱਗਿੰਗ ਅਯੋਗ ਬਣਾਉਣ ਲਈ ਚੁਣੋ।"</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"ਵਾਇਰਲੈੱਸ ਡੀਬੱਗਿੰਗ ਨੂੰ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 0f5d000..6483db1 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"అవతలి వారు HCO TTY మోడ్ని అభ్యర్థించారు"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"అవతలి వారు VCO TTY మోడ్ని అభ్యర్థించారు"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"అవతలి వారు OFF TTY మోడ్ని అభ్యర్థించారు"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"వాయిస్"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
<string name="serviceClassData" msgid="4148080018967300248">"డేటా"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"ఫ్యాక్స్"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index fbcf2cb..3ae2131 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -66,6 +66,10 @@
<!-- The height of the navigation gesture area if the gesture is starting from the bottom. -->
<dimen name="navigation_bar_gesture_height">@dimen/navigation_bar_frame_height</dimen>
+ <!-- The height of the navigation larger gesture area if the gesture is starting from
+ the bottom. -->
+ <dimen name="navigation_bar_gesture_larger_height">80dp</dimen>
+
<!-- Height of the bottom navigation / system bar in car mode. -->
<dimen name="navigation_bar_height_car_mode">96dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9b06285..31b4edd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1702,6 +1702,7 @@
<java-symbol type="dimen" name="navigation_bar_frame_height" />
<java-symbol type="dimen" name="navigation_bar_frame_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_gesture_height" />
+ <java-symbol type="dimen" name="navigation_bar_gesture_larger_height" />
<java-symbol type="dimen" name="navigation_bar_height_car_mode" />
<java-symbol type="dimen" name="navigation_bar_height_landscape_car_mode" />
<java-symbol type="dimen" name="navigation_bar_width_car_mode" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index c0731c8..dff6e87 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -361,6 +361,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1119,6 +1121,8 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
<!-- Progress bar attributes -->
<item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
index 9978648..9968f79 100644
--- a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
@@ -87,7 +87,7 @@
}
@Test
- public void testGetEditorInfoMimeTypes_fallbackToCommitContent() throws Throwable {
+ public void testGetFallbackMimeTypesForAutofill() throws Throwable {
// Configure the EditText with an EditorInfo/InputConnection that supports some image MIME
// types.
String[] mimeTypes = {"image/gif", "image/png"};
@@ -99,11 +99,12 @@
onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
// Assert that the default listener returns the MIME types declared in the EditorInfo.
- assertThat(mDefaultReceiver.getEditorInfoMimeTypes(mEditText)).isEqualTo(mimeTypes);
+ assertThat(mDefaultReceiver.getFallbackMimeTypesForAutofill(mEditText)).isEqualTo(
+ mimeTypes);
}
@Test
- public void testGetEditorInfoMimeTypes_fallbackToCommitContent_noMimeTypesInEditorInfo()
+ public void testGetFallbackMimeTypesForAutofill_noMimeTypesInEditorInfo()
throws Throwable {
// Configure the EditText with an EditorInfo/InputConnection that doesn't declare any MIME
// types.
@@ -115,7 +116,7 @@
onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
// Assert that the default listener returns null as the MIME types.
- assertThat(mDefaultReceiver.getEditorInfoMimeTypes(mEditText)).isNull();
+ assertThat(mDefaultReceiver.getFallbackMimeTypesForAutofill(mEditText)).isNull();
}
@Test
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
index 6df1c3e..c01bb75 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -45,14 +45,14 @@
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;
+ private static final @RangingChangeReason int REASON = RangingChangeReason.UNKNOWN;
@Test
- public void testOpenSession_StartRangingInvoked() throws RemoteException {
+ public void testOpenSession_OpenRangingInvoked() 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));
+ verify(ADAPTER, times(1)).openRanging(eq(rangingManager), eq(PARAMS));
}
@Test
@@ -60,7 +60,7 @@
RangingManager rangingManager = new RangingManager(ADAPTER);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
SessionHandle handle = new SessionHandle(1);
- when(ADAPTER.startRanging(any(), any())).thenReturn(handle);
+ when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
rangingManager.openSession(PARAMS, EXECUTOR, callback);
@@ -73,34 +73,34 @@
}
@Test
- public void testOnRangingStarted_ValidSessionHandle() throws RemoteException {
+ public void testOnRangingOpened_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);
+ when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
rangingManager.openSession(PARAMS, EXECUTOR, callback);
- rangingManager.onRangingStarted(handle, PARAMS);
- verify(callback, times(1)).onOpenSuccess(any(), any());
+ rangingManager.onRangingOpened(handle);
+ verify(callback, times(1)).onOpened(any());
}
@Test
- public void testOnRangingStarted_InvalidSessionHandle() throws RemoteException {
+ public void testOnRangingOpened_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());
+ rangingManager.onRangingOpened(new SessionHandle(2));
+ verify(callback, times(0)).onOpened(any());
}
@Test
- public void testOnRangingStarted_MultipleSessionsRegistered() throws RemoteException {
+ public void testOnRangingOpened_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()))
+ when(ADAPTER.openRanging(any(), any()))
.thenReturn(sessionHandle1)
.thenReturn(sessionHandle2);
@@ -108,25 +108,50 @@
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.onRangingOpened(sessionHandle1);
+ verify(callback1, times(1)).onOpened(any());
+ verify(callback2, times(0)).onOpened(any());
- rangingManager.onRangingStarted(sessionHandle2, PARAMS);
- verify(callback1, times(1)).onOpenSuccess(any(), any());
- verify(callback2, times(1)).onOpenSuccess(any(), any());
+ rangingManager.onRangingOpened(sessionHandle2);
+ verify(callback1, times(1)).onOpened(any());
+ verify(callback2, times(1)).onOpened(any());
}
@Test
- public void testOnRangingClosed_OnRangingClosedCalled() throws RemoteException {
+ public void testCorrectCallbackInvoked() 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);
+ when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
- rangingManager.onRangingClosed(handle, CLOSE_REASON, PARAMS);
- verify(callback, times(1)).onClosed(anyInt(), any());
+ rangingManager.openSession(PARAMS, EXECUTOR, callback);
+ rangingManager.onRangingOpened(handle);
+ verify(callback, times(1)).onOpened(any());
+
+ rangingManager.onRangingStarted(handle, PARAMS);
+ verify(callback, times(1)).onStarted(eq(PARAMS));
+
+ rangingManager.onRangingStartFailed(handle, REASON, PARAMS);
+ verify(callback, times(1)).onStartFailed(eq(REASON), eq(PARAMS));
+
+ RangingReport report = UwbTestUtils.getRangingReports(1);
+ rangingManager.onRangingResult(handle, report);
+ verify(callback, times(1)).onReportReceived(eq(report));
+
+ rangingManager.onRangingReconfigured(handle, PARAMS);
+ verify(callback, times(1)).onReconfigured(eq(PARAMS));
+
+ rangingManager.onRangingReconfigureFailed(handle, REASON, PARAMS);
+ verify(callback, times(1)).onReconfigureFailed(eq(REASON), eq(PARAMS));
+
+ rangingManager.onRangingStopped(handle);
+ verify(callback, times(1)).onStopped();
+
+ rangingManager.onRangingStopFailed(handle, REASON, PARAMS);
+ verify(callback, times(1)).onStopFailed(eq(REASON), eq(PARAMS));
+
+ rangingManager.onRangingClosed(handle, REASON, PARAMS);
+ verify(callback, times(1)).onClosed(eq(REASON), eq(PARAMS));
}
@Test
@@ -138,7 +163,7 @@
RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
- when(ADAPTER.startRanging(any(), any()))
+ when(ADAPTER.openRanging(any(), any()))
.thenReturn(sessionHandle1)
.thenReturn(sessionHandle2);
@@ -146,37 +171,23 @@
rangingManager.openSession(PARAMS, EXECUTOR, callback1);
rangingManager.openSession(PARAMS, EXECUTOR, callback2);
- rangingManager.onRangingClosed(sessionHandle1, CLOSE_REASON, PARAMS);
+ rangingManager.onRangingClosed(sessionHandle1, REASON, PARAMS);
verify(callback1, times(1)).onClosed(anyInt(), any());
verify(callback2, times(0)).onClosed(anyInt(), any());
- rangingManager.onRangingClosed(sessionHandle2, CLOSE_REASON, PARAMS);
+ rangingManager.onRangingClosed(sessionHandle2, 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()))
+ when(ADAPTER.openRanging(any(), any()))
.thenReturn(sessionHandle1)
.thenReturn(sessionHandle2);
@@ -196,65 +207,54 @@
}
@Test
- public void testOnClose_Reasons() throws RemoteException {
- runOnClose_Reason(CloseReason.LOCAL_API,
- RangingSession.Callback.CLOSE_REASON_LOCAL_CLOSE_API);
+ public void testReasons() throws RemoteException {
+ runReason(RangingChangeReason.LOCAL_API,
+ RangingSession.Callback.REASON_LOCAL_REQUEST);
- runOnClose_Reason(CloseReason.MAX_SESSIONS_REACHED,
- RangingSession.Callback.CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED);
+ runReason(RangingChangeReason.MAX_SESSIONS_REACHED,
+ RangingSession.Callback.REASON_MAX_SESSIONS_REACHED);
- runOnClose_Reason(CloseReason.PROTOCOL_SPECIFIC,
- RangingSession.Callback.CLOSE_REASON_PROTOCOL_SPECIFIC);
+ runReason(RangingChangeReason.PROTOCOL_SPECIFIC,
+ RangingSession.Callback.REASON_PROTOCOL_SPECIFIC_ERROR);
- runOnClose_Reason(CloseReason.REMOTE_REQUEST,
- RangingSession.Callback.CLOSE_REASON_REMOTE_REQUEST);
+ runReason(RangingChangeReason.REMOTE_REQUEST,
+ RangingSession.Callback.REASON_REMOTE_REQUEST);
- runOnClose_Reason(CloseReason.SYSTEM_POLICY,
- RangingSession.Callback.CLOSE_REASON_LOCAL_SYSTEM_POLICY);
+ runReason(RangingChangeReason.SYSTEM_POLICY,
+ RangingSession.Callback.REASON_SYSTEM_POLICY);
- runOnClose_Reason(CloseReason.UNKNOWN,
- RangingSession.Callback.CLOSE_REASON_UNKNOWN);
+ runReason(RangingChangeReason.BAD_PARAMETERS,
+ RangingSession.Callback.REASON_BAD_PARAMETERS);
+
+ runReason(RangingChangeReason.UNKNOWN,
+ RangingSession.Callback.REASON_UNKNOWN);
}
- private void runOnClose_Reason(@CloseReason int reasonIn,
- @RangingSession.Callback.CloseReason int reasonOut) throws RemoteException {
+ private void runReason(@RangingChangeReason int reasonIn,
+ @RangingSession.Callback.Reason 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);
+ when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
rangingManager.openSession(PARAMS, EXECUTOR, callback);
- rangingManager.onRangingClosed(handle, reasonIn, PARAMS);
- verify(callback, times(1)).onClosed(eq(reasonOut), eq(PARAMS));
- }
+ rangingManager.onRangingOpenFailed(handle, reasonIn, PARAMS);
+ verify(callback, times(1)).onOpenFailed(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);
+ // Open a new session
rangingManager.openSession(PARAMS, EXECUTOR, callback);
+ rangingManager.onRangingOpened(handle);
rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS);
+ verify(callback, times(1)).onStartFailed(eq(reasonOut), eq(PARAMS));
+
+ rangingManager.onRangingReconfigureFailed(handle, reasonIn, PARAMS);
+ verify(callback, times(1)).onReconfigureFailed(eq(reasonOut), eq(PARAMS));
+
+ rangingManager.onRangingStopFailed(handle, reasonIn, PARAMS);
+ verify(callback, times(1)).onStopFailed(eq(reasonOut), eq(PARAMS));
+
+ rangingManager.onRangingClosed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onClosed(eq(reasonOut), eq(PARAMS));
}
}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java b/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java
index 702c68e..e5eea26 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java
@@ -19,9 +19,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -34,6 +36,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import java.util.concurrent.Executor;
@@ -43,47 +47,48 @@
@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;
+ private static final @RangingSession.Callback.Reason int REASON =
+ RangingSession.Callback.REASON_GENERIC_ERROR;
@Test
- public void testOnRangingStarted_OnOpenSuccessCalled() {
+ public void testOnRangingOpened_OnOpenSuccessCalled() {
SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
verifyOpenState(session, false);
- session.onRangingStarted(PARAMS);
+ session.onRangingOpened();
verifyOpenState(session, true);
// Verify that the onOpenSuccess callback was invoked
- verify(callback, times(1)).onOpenSuccess(eq(session), any());
+ verify(callback, times(1)).onOpened(eq(session));
verify(callback, times(0)).onClosed(anyInt(), any());
}
@Test
- public void testOnRangingStarted_CannotOpenClosedSession() {
+ public void testOnRangingOpened_CannotOpenClosedSession() {
SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- session.onRangingStarted(PARAMS);
+ session.onRangingOpened();
verifyOpenState(session, true);
- verify(callback, times(1)).onOpenSuccess(eq(session), any());
+ verify(callback, times(1)).onOpened(eq(session));
verify(callback, times(0)).onClosed(anyInt(), any());
- session.onRangingClosed(CLOSE_REASON, PARAMS);
+ session.onRangingClosed(REASON, PARAMS);
verifyOpenState(session, false);
- verify(callback, times(1)).onOpenSuccess(eq(session), any());
+ verify(callback, times(1)).onOpened(eq(session));
verify(callback, times(1)).onClosed(anyInt(), any());
// Now invoke the ranging started callback and ensure the session remains closed
- session.onRangingStarted(PARAMS);
+ session.onRangingOpened();
verifyOpenState(session, false);
- verify(callback, times(1)).onOpenSuccess(eq(session), any());
+ verify(callback, times(1)).onOpened(eq(session));
verify(callback, times(1)).onClosed(anyInt(), any());
}
@@ -91,27 +96,30 @@
public void testOnRangingClosed_OnClosedCalledWhenSessionNotOpen() {
SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
verifyOpenState(session, false);
- session.onRangingClosed(CLOSE_REASON, PARAMS);
+ session.onRangingClosed(REASON, PARAMS);
verifyOpenState(session, false);
// Verify that the onOpenSuccess callback was invoked
- verify(callback, times(0)).onOpenSuccess(eq(session), any());
+ verify(callback, times(0)).onOpened(eq(session));
verify(callback, times(1)).onClosed(anyInt(), any());
}
- @Test public void testOnRangingClosed_OnClosedCalled() {
+ @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);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
session.onRangingStarted(PARAMS);
- session.onRangingClosed(CLOSE_REASON, PARAMS);
+ session.onRangingClosed(REASON, PARAMS);
verify(callback, times(1)).onClosed(anyInt(), any());
verifyOpenState(session, false);
- session.onRangingClosed(CLOSE_REASON, PARAMS);
+ session.onRangingClosed(REASON, PARAMS);
verify(callback, times(2)).onClosed(anyInt(), any());
}
@@ -119,7 +127,8 @@
public void testOnRangingResult_OnReportReceivedCalled() {
SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
verifyOpenState(session, false);
session.onRangingStarted(PARAMS);
@@ -131,11 +140,83 @@
}
@Test
- public void testClose() throws RemoteException {
+ public void testStart_CannotStartIfAlreadyStarted() throws RemoteException {
SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
+ doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any());
+ session.onRangingOpened();
+
+ session.start(PARAMS);
+ verify(callback, times(1)).onStarted(any());
+
+ // Calling start again should throw an illegal state
+ verifyThrowIllegalState(() -> session.start(PARAMS));
+ verify(callback, times(1)).onStarted(any());
+ }
+
+ @Test
+ public void testStop_CannotStopIfAlreadyStopped() throws RemoteException {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
+ doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any());
+ doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any());
+ session.onRangingOpened();
+ session.start(PARAMS);
+
+ verifyNoThrowIllegalState(session::stop);
+ verify(callback, times(1)).onStopped();
+
+ // Calling stop again should throw an illegal state
+ verifyThrowIllegalState(session::stop);
+ verify(callback, times(1)).onStopped();
+ }
+
+ @Test
+ public void testReconfigure_OnlyWhenOpened() throws RemoteException {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
+ doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any());
+ doAnswer(new ReconfigureAnswer(session)).when(adapter).reconfigureRanging(any(), any());
+
+ verifyThrowIllegalState(() -> session.reconfigure(PARAMS));
+ verify(callback, times(0)).onReconfigured(any());
+ verifyOpenState(session, false);
+
+ session.onRangingOpened();
+ verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS));
+ verify(callback, times(1)).onReconfigured(any());
+ verifyOpenState(session, true);
+
session.onRangingStarted(PARAMS);
+ verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS));
+ verify(callback, times(2)).onReconfigured(any());
+ verifyOpenState(session, true);
+
+ session.onRangingStopped();
+ verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS));
+ verify(callback, times(3)).onReconfigured(any());
+ verifyOpenState(session, true);
+
+
+ session.onRangingClosed(REASON, PARAMS);
+ verifyThrowIllegalState(() -> session.reconfigure(PARAMS));
+ verify(callback, times(3)).onReconfigured(any());
+ verifyOpenState(session, false);
+ }
+
+ @Test
+ public void testClose_NoCallbackUntilInvoked() throws RemoteException {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
+ session.onRangingOpened();
// Calling close multiple times should invoke closeRanging until the session receives
// the onClosed callback.
@@ -143,7 +224,7 @@
for (int i = 1; i <= totalCallsBeforeOnRangingClosed; i++) {
session.close();
verifyOpenState(session, true);
- verify(ADAPTER, times(i)).closeRanging(handle);
+ verify(adapter, times(i)).closeRanging(handle);
verify(callback, times(0)).onClosed(anyInt(), any());
}
@@ -151,18 +232,47 @@
// the session's close.
final int totalCallsAfterOnRangingClosed = 2;
for (int i = 1; i <= totalCallsAfterOnRangingClosed; i++) {
- session.onRangingClosed(CLOSE_REASON, PARAMS);
+ session.onRangingClosed(REASON, PARAMS);
verifyOpenState(session, false);
- verify(ADAPTER, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle);
+ verify(adapter, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle);
verify(callback, times(i)).onClosed(anyInt(), any());
}
}
@Test
+ public void testClose_OnClosedCalled() throws RemoteException {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
+ doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any());
+ session.onRangingOpened();
+
+ session.close();
+ verify(callback, times(1)).onClosed(anyInt(), any());
+ }
+
+ @Test
+ public void testClose_CannotInteractFurther() throws RemoteException {
+ SessionHandle handle = new SessionHandle(123);
+ RangingSession.Callback callback = mock(RangingSession.Callback.class);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
+ doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any());
+ session.close();
+
+ verifyThrowIllegalState(() -> session.start(PARAMS));
+ verifyThrowIllegalState(() -> session.reconfigure(PARAMS));
+ verifyThrowIllegalState(() -> session.stop());
+ verifyNoThrowIllegalState(() -> session.close());
+ }
+
+ @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);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
assertFalse(session.isOpen());
session.onRangingStarted(PARAMS);
@@ -178,7 +288,8 @@
public void testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen() {
SessionHandle handle = new SessionHandle(123);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, ADAPTER, handle);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
assertFalse(session.isOpen());
@@ -191,4 +302,77 @@
private void verifyOpenState(RangingSession session, boolean expected) {
assertEquals(expected, session.isOpen());
}
+
+ private void verifyThrowIllegalState(Runnable runnable) {
+ try {
+ runnable.run();
+ fail();
+ } catch (IllegalStateException e) {
+ // Pass
+ }
+ }
+
+ private void verifyNoThrowIllegalState(Runnable runnable) {
+ try {
+ runnable.run();
+ } catch (IllegalStateException e) {
+ fail();
+ }
+ }
+
+ abstract class AdapterAnswer implements Answer {
+ protected RangingSession mSession;
+
+ protected AdapterAnswer(RangingSession session) {
+ mSession = session;
+ }
+ }
+
+ class StartAnswer extends AdapterAnswer {
+ StartAnswer(RangingSession session) {
+ super(session);
+ }
+
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ mSession.onRangingStarted(PARAMS);
+ return null;
+ }
+ }
+
+ class ReconfigureAnswer extends AdapterAnswer {
+ ReconfigureAnswer(RangingSession session) {
+ super(session);
+ }
+
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ mSession.onRangingReconfigured(PARAMS);
+ return null;
+ }
+ }
+
+ class StopAnswer extends AdapterAnswer {
+ StopAnswer(RangingSession session) {
+ super(session);
+ }
+
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ mSession.onRangingStopped();
+ return null;
+ }
+ }
+
+ class CloseAnswer extends AdapterAnswer {
+ CloseAnswer(RangingSession session) {
+ super(session);
+ }
+
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ mSession.onRangingClosed(REASON, PARAMS);
+ return null;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 807e5af..13f1fdd 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -42,4 +42,10 @@
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
when the PIP menu is shown in center. -->
<string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
+
+ <!-- one handed background panel default color RGB -->
+ <item name="config_one_handed_background_rgb" format="float" type="dimen">0.5</item>
+
+ <!-- one handed background panel default alpha -->
+ <item name="config_one_handed_background_alpha" format="float" type="dimen">0.5</item>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
index 2f2168f..aa82339 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
@@ -16,113 +16,32 @@
package com.android.wm.shell;
+import android.util.Slog;
+
import com.android.wm.shell.apppairs.AppPairs;
-import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import java.io.PrintWriter;
import java.util.Optional;
+import java.util.concurrent.TimeUnit;
/**
* An entry point into the shell for dumping shell internal state and running adb commands.
*
* Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}.
*/
-public final class ShellCommandHandler {
+public interface ShellCommandHandler {
+ /**
+ * Dumps the shell state.
+ */
+ void dump(PrintWriter pw);
- private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
- private final Optional<Pip> mPipOptional;
- private final Optional<OneHanded> mOneHandedOptional;
- private final Optional<HideDisplayCutout> mHideDisplayCutout;
- private final ShellTaskOrganizer mShellTaskOrganizer;
- private final Optional<AppPairs> mAppPairsOptional;
-
- public ShellCommandHandler(
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<Pip> pipOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutout,
- Optional<AppPairs> appPairsOptional) {
- mShellTaskOrganizer = shellTaskOrganizer;
- mLegacySplitScreenOptional = legacySplitScreenOptional;
- mPipOptional = pipOptional;
- mOneHandedOptional = oneHandedOptional;
- mHideDisplayCutout = hideDisplayCutout;
- mAppPairsOptional = appPairsOptional;
- }
-
- /** Dumps WM Shell internal state. */
- @ExternalThread
- public void dump(PrintWriter pw) {
- mShellTaskOrganizer.dump(pw, "");
- pw.println();
- pw.println();
- mPipOptional.ifPresent(pip -> pip.dump(pw));
- mLegacySplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
- mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
- mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
- pw.println();
- pw.println();
- mAppPairsOptional.ifPresent(appPairs -> appPairs.dump(pw, ""));
- }
-
-
- /** Returns {@code true} if command was found and executed. */
- @ExternalThread
- public boolean handleCommand(String[] args, PrintWriter pw) {
- if (args.length < 2) {
- // Argument at position 0 is "WMShell".
- return false;
- }
- switch (args[1]) {
- case "pair":
- return runPair(args, pw);
- case "unpair":
- return runUnpair(args, pw);
- case "help":
- return runHelp(pw);
- default:
- return false;
- }
- }
-
-
- private boolean runPair(String[] args, PrintWriter pw) {
- if (args.length < 4) {
- // First two arguments are "WMShell" and command name.
- pw.println("Error: two task ids should be provided as arguments");
- return false;
- }
- final int taskId1 = new Integer(args[2]);
- final int taskId2 = new Integer(args[3]);
- mAppPairsOptional.ifPresent(appPairs -> appPairs.pair(taskId1, taskId2));
- return true;
- }
-
- private boolean runUnpair(String[] args, PrintWriter pw) {
- if (args.length < 3) {
- // First two arguments are "WMShell" and command name.
- pw.println("Error: task id should be provided as an argument");
- return false;
- }
- final int taskId = new Integer(args[2]);
- mAppPairsOptional.ifPresent(appPairs -> appPairs.unpair(taskId));
- return true;
- }
-
- private boolean runHelp(PrintWriter pw) {
- pw.println("Window Manager Shell commands:");
- pw.println(" help");
- pw.println(" Print this help text.");
- pw.println(" <no arguments provided>");
- pw.println(" Dump Window Manager Shell internal state");
- pw.println(" pair <taskId1> <taskId2>");
- pw.println(" unpair <taskId>");
- pw.println(" Pairs/unpairs tasks with given ids.");
- return true;
- }
+ /**
+ * Handles a shell command.
+ */
+ boolean handleCommand(final String[] args, PrintWriter pw);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
new file mode 100644
index 0000000..f213af7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import android.util.Slog;
+
+import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+
+import java.io.PrintWriter;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An entry point into the shell for dumping shell internal state and running adb commands.
+ *
+ * Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}.
+ */
+public final class ShellCommandHandlerImpl {
+ private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName();
+
+ private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
+ private final Optional<Pip> mPipOptional;
+ private final Optional<OneHanded> mOneHandedOptional;
+ private final Optional<HideDisplayCutout> mHideDisplayCutout;
+ private final ShellTaskOrganizer mShellTaskOrganizer;
+ private final Optional<AppPairs> mAppPairsOptional;
+ private final ShellExecutor mMainExecutor;
+ private final HandlerImpl mImpl = new HandlerImpl();
+
+ public static ShellCommandHandler create(
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<Pip> pipOptional,
+ Optional<OneHanded> oneHandedOptional,
+ Optional<HideDisplayCutout> hideDisplayCutout,
+ Optional<AppPairs> appPairsOptional,
+ ShellExecutor mainExecutor) {
+ return new ShellCommandHandlerImpl(shellTaskOrganizer, legacySplitScreenOptional,
+ pipOptional, oneHandedOptional, hideDisplayCutout, appPairsOptional,
+ mainExecutor).mImpl;
+ }
+
+ private ShellCommandHandlerImpl(
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<Pip> pipOptional,
+ Optional<OneHanded> oneHandedOptional,
+ Optional<HideDisplayCutout> hideDisplayCutout,
+ Optional<AppPairs> appPairsOptional,
+ ShellExecutor mainExecutor) {
+ mShellTaskOrganizer = shellTaskOrganizer;
+ mLegacySplitScreenOptional = legacySplitScreenOptional;
+ mPipOptional = pipOptional;
+ mOneHandedOptional = oneHandedOptional;
+ mHideDisplayCutout = hideDisplayCutout;
+ mAppPairsOptional = appPairsOptional;
+ mMainExecutor = mainExecutor;
+ }
+
+ /** Dumps WM Shell internal state. */
+ private void dump(PrintWriter pw) {
+ mShellTaskOrganizer.dump(pw, "");
+ pw.println();
+ pw.println();
+ mPipOptional.ifPresent(pip -> pip.dump(pw));
+ mLegacySplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
+ mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
+ mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
+ pw.println();
+ pw.println();
+ mAppPairsOptional.ifPresent(appPairs -> appPairs.dump(pw, ""));
+ }
+
+
+ /** Returns {@code true} if command was found and executed. */
+ private boolean handleCommand(final String[] args, PrintWriter pw) {
+ if (args.length < 2) {
+ // Argument at position 0 is "WMShell".
+ return false;
+ }
+ switch (args[1]) {
+ case "pair":
+ return runPair(args, pw);
+ case "unpair":
+ return runUnpair(args, pw);
+ case "help":
+ return runHelp(pw);
+ default:
+ return false;
+ }
+ }
+
+
+ private boolean runPair(String[] args, PrintWriter pw) {
+ if (args.length < 4) {
+ // First two arguments are "WMShell" and command name.
+ pw.println("Error: two task ids should be provided as arguments");
+ return false;
+ }
+ final int taskId1 = new Integer(args[2]);
+ final int taskId2 = new Integer(args[3]);
+ mAppPairsOptional.ifPresent(appPairs -> appPairs.pair(taskId1, taskId2));
+ return true;
+ }
+
+ private boolean runUnpair(String[] args, PrintWriter pw) {
+ if (args.length < 3) {
+ // First two arguments are "WMShell" and command name.
+ pw.println("Error: task id should be provided as an argument");
+ return false;
+ }
+ final int taskId = new Integer(args[2]);
+ mAppPairsOptional.ifPresent(appPairs -> appPairs.unpair(taskId));
+ return true;
+ }
+
+ private boolean runHelp(PrintWriter pw) {
+ pw.println("Window Manager Shell commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" <no arguments provided>");
+ pw.println(" Dump Window Manager Shell internal state");
+ pw.println(" pair <taskId1> <taskId2>");
+ pw.println(" unpair <taskId>");
+ pw.println(" Pairs/unpairs tasks with given ids.");
+ return true;
+ }
+
+ private class HandlerImpl implements ShellCommandHandler {
+ @Override
+ public void dump(PrintWriter pw) {
+ try {
+ mMainExecutor.executeBlocking(() -> ShellCommandHandlerImpl.this.dump(pw));
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to dump the Shell in 2s", e);
+ }
+ }
+
+ @Override
+ public boolean handleCommand(String[] args, PrintWriter pw) {
+ try {
+ boolean[] result = new boolean[1];
+ mMainExecutor.executeBlocking(() -> {
+ result[0] = ShellCommandHandlerImpl.this.handleCommand(args, pw);
+ });
+ return result[0];
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to handle Shell command in 2s", e);
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
index f4c617e..d7010b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
@@ -16,61 +16,15 @@
package com.android.wm.shell;
-import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
-
-import com.android.wm.shell.apppairs.AppPairs;
-import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.draganddrop.DragAndDropController;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
-
-import java.util.Optional;
/**
* An entry point into the shell for initializing shell internal state.
*/
-public class ShellInit {
-
- private final DisplayImeController mDisplayImeController;
- private final DragAndDropController mDragAndDropController;
- private final ShellTaskOrganizer mShellTaskOrganizer;
- private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
- private final Optional<AppPairs> mAppPairsOptional;
- private final FullscreenTaskListener mFullscreenTaskListener;
- private final Transitions mTransitions;
-
- public ShellInit(DisplayImeController displayImeController,
- DragAndDropController dragAndDropController,
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<AppPairs> appPairsOptional,
- FullscreenTaskListener fullscreenTaskListener,
- Transitions transitions) {
- mDisplayImeController = displayImeController;
- mDragAndDropController = dragAndDropController;
- mShellTaskOrganizer = shellTaskOrganizer;
- mLegacySplitScreenOptional = legacySplitScreenOptional;
- mAppPairsOptional = appPairsOptional;
- mFullscreenTaskListener = fullscreenTaskListener;
- mTransitions = transitions;
- }
-
- @ExternalThread
- public void init() {
- // Start listening for display changes
- mDisplayImeController.startMonitorDisplays();
-
- mShellTaskOrganizer.addListenerForType(
- mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN);
- // Register the shell organizer
- mShellTaskOrganizer.registerOrganizer();
-
- mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered);
- // Bind the splitscreen impl to the drag drop controller
- mDragAndDropController.setSplitScreenController(mLegacySplitScreenOptional);
-
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mTransitions.register(mShellTaskOrganizer);
- }
- }
+@ExternalThread
+public interface ShellInit {
+ /**
+ * Initializes the shell state.
+ */
+ void init();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
new file mode 100644
index 0000000..0056761
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
+
+import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The entry point implementation into the shell for initializing shell internal state.
+ */
+public class ShellInitImpl {
+ private static final String TAG = ShellInitImpl.class.getSimpleName();
+
+ private final DisplayImeController mDisplayImeController;
+ private final DragAndDropController mDragAndDropController;
+ private final ShellTaskOrganizer mShellTaskOrganizer;
+ private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
+ private final Optional<AppPairs> mAppPairsOptional;
+ private final FullscreenTaskListener mFullscreenTaskListener;
+ private final ShellExecutor mMainExecutor;
+
+ private final InitImpl mImpl = new InitImpl();
+
+ public static ShellInit create(DisplayImeController displayImeController,
+ DragAndDropController dragAndDropController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<AppPairs> appPairsOptional,
+ FullscreenTaskListener fullscreenTaskListener,
+ ShellExecutor mainExecutor) {
+ return new ShellInitImpl(displayImeController,
+ dragAndDropController,
+ shellTaskOrganizer,
+ legacySplitScreenOptional,
+ appPairsOptional,
+ fullscreenTaskListener,
+ mainExecutor).mImpl;
+ }
+
+ private ShellInitImpl(DisplayImeController displayImeController,
+ DragAndDropController dragAndDropController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<AppPairs> appPairsOptional,
+ FullscreenTaskListener fullscreenTaskListener,
+ ShellExecutor mainExecutor) {
+ mDisplayImeController = displayImeController;
+ mDragAndDropController = dragAndDropController;
+ mShellTaskOrganizer = shellTaskOrganizer;
+ mLegacySplitScreenOptional = legacySplitScreenOptional;
+ mAppPairsOptional = appPairsOptional;
+ mFullscreenTaskListener = fullscreenTaskListener;
+ mMainExecutor = mainExecutor;
+ }
+
+ private void init() {
+ // Start listening for display changes
+ mDisplayImeController.startMonitorDisplays();
+
+ mShellTaskOrganizer.addListenerForType(
+ mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN);
+ // Register the shell organizer
+ mShellTaskOrganizer.registerOrganizer();
+
+ mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered);
+
+ // Bind the splitscreen impl to the drag drop controller
+ mDragAndDropController.initialize(mLegacySplitScreenOptional);
+ }
+
+ @ExternalThread
+ private class InitImpl implements ShellInit {
+ @Override
+ public void init() {
+ try {
+ mMainExecutor.executeBlocking(() -> ShellInitImpl.this.init());
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to initialize the Shell in 2s", e);
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index d588419..8d0e965 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -29,11 +29,14 @@
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Binder;
import android.util.CloseGuard;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewTreeObserver;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -44,7 +47,7 @@
* View that can display a task.
*/
public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
- ShellTaskOrganizer.TaskListener {
+ ShellTaskOrganizer.TaskListener, ViewTreeObserver.OnComputeInternalInsetsListener {
/** Callback for listening task state. */
public interface Listener {
@@ -70,6 +73,7 @@
private final CloseGuard mGuard = new CloseGuard();
private final ShellTaskOrganizer mTaskOrganizer;
+ private final Executor mShellExecutor;
private ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mTaskToken;
@@ -78,16 +82,17 @@
private boolean mSurfaceCreated;
private boolean mIsInitialized;
private Listener mListener;
- private Executor mExecutor;
+ private Executor mListenerExecutor;
private final Rect mTmpRect = new Rect();
private final Rect mTmpRootRect = new Rect();
+ private final int[] mTmpLocation = new int[2];
public TaskView(Context context, ShellTaskOrganizer organizer) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
mTaskOrganizer = organizer;
- mExecutor = organizer.getExecutor();
+ mShellExecutor = organizer.getExecutor();
setUseAlpha();
getHolder().addCallback(this);
mGuard.open("release");
@@ -96,12 +101,13 @@
/**
* Only one listener may be set on the view, throws an exception otherwise.
*/
- public void setListener(Listener listener) {
+ public void setListener(@NonNull Executor executor, Listener listener) {
if (mListener != null) {
throw new IllegalStateException(
"Trying to set a listener when one has already been set");
}
mListener = listener;
+ mListenerExecutor = executor;
}
/**
@@ -146,7 +152,9 @@
private void prepareActivityOptions(ActivityOptions options) {
final Binder launchCookie = new Binder();
- mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this);
+ mShellExecutor.execute(() -> {
+ mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this);
+ });
options.setLaunchCookie(launchCookie);
options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
options.setTaskAlwaysOnTop(true);
@@ -193,11 +201,15 @@
private void performRelease() {
getHolder().removeCallback(this);
- mTaskOrganizer.removeListener(this);
- resetTaskInfo();
+ mShellExecutor.execute(() -> {
+ mTaskOrganizer.removeListener(this);
+ resetTaskInfo();
+ });
mGuard.close();
if (mListener != null && mIsInitialized) {
- mListener.onReleased();
+ mListenerExecutor.execute(() -> {
+ mListener.onReleased();
+ });
mIsInitialized = false;
}
}
@@ -214,75 +226,71 @@
mTaskOrganizer.applyTransaction(wct);
// TODO(b/151449487): Only call callback once we enable synchronization
if (mListener != null) {
- mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated);
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated);
+ });
}
}
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl leash) {
- if (mExecutor == null) return;
- mExecutor.execute(() -> {
- mTaskInfo = taskInfo;
- mTaskToken = taskInfo.token;
- mTaskLeash = leash;
+ mTaskInfo = taskInfo;
+ mTaskToken = taskInfo.token;
+ mTaskLeash = leash;
- if (mSurfaceCreated) {
- // Surface is ready, so just reparent the task to this surface control
- mTransaction.reparent(mTaskLeash, getSurfaceControl())
- .show(mTaskLeash)
- .apply();
- } else {
- // The surface has already been destroyed before the task has appeared,
- // so go ahead and hide the task entirely
- updateTaskVisibility();
- }
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
- // TODO: Synchronize show with the resize
- onLocationChanged();
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ if (mSurfaceCreated) {
+ // Surface is ready, so just reparent the task to this surface control
+ mTransaction.reparent(mTaskLeash, getSurfaceControl())
+ .show(mTaskLeash)
+ .apply();
+ } else {
+ // The surface has already been destroyed before the task has appeared,
+ // so go ahead and hide the task entirely
+ updateTaskVisibility();
+ }
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
+ // TODO: Synchronize show with the resize
+ onLocationChanged();
+ setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
- if (mListener != null) {
+ if (mListener != null) {
+ mListenerExecutor.execute(() -> {
mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity);
- }
- });
+ });
+ }
}
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- if (mExecutor == null) return;
- mExecutor.execute(() -> {
- if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
+ if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
- if (mListener != null) {
+ if (mListener != null) {
+ mListenerExecutor.execute(() -> {
mListener.onTaskRemovalStarted(taskInfo.taskId);
- }
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+ });
+ }
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
- // Unparent the task when this surface is destroyed
- mTransaction.reparent(mTaskLeash, null).apply();
- resetTaskInfo();
- });
+ // Unparent the task when this surface is destroyed
+ mTransaction.reparent(mTaskLeash, null).apply();
+ resetTaskInfo();
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- if (mExecutor == null) return;
- mExecutor.execute(() -> {
- mTaskInfo.taskDescription = taskInfo.taskDescription;
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
- });
+ mTaskInfo.taskDescription = taskInfo.taskDescription;
+ setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
}
@Override
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
- if (mExecutor == null) return;
- mExecutor.execute(() -> {
- if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
- if (mListener != null) {
+ if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
+ if (mListener != null) {
+ mListenerExecutor.execute(() -> {
mListener.onBackPressedOnTaskRoot(taskInfo.taskId);
- }
- });
+ });
+ }
}
@Override
@@ -302,17 +310,21 @@
mSurfaceCreated = true;
if (mListener != null && !mIsInitialized) {
mIsInitialized = true;
- mListener.onInitialized();
+ mListenerExecutor.execute(() -> {
+ mListener.onInitialized();
+ });
}
- if (mTaskToken == null) {
- // Nothing to update, task is not yet available
- return;
- }
- // Reparent the task when this surface is created
- mTransaction.reparent(mTaskLeash, getSurfaceControl())
- .show(mTaskLeash)
- .apply();
- updateTaskVisibility();
+ mShellExecutor.execute(() -> {
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+ // Reparent the task when this surface is created
+ mTransaction.reparent(mTaskLeash, getSurfaceControl())
+ .show(mTaskLeash)
+ .apply();
+ updateTaskVisibility();
+ });
}
@Override
@@ -326,13 +338,47 @@
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mSurfaceCreated = false;
- if (mTaskToken == null) {
- // Nothing to update, task is not yet available
- return;
- }
+ mShellExecutor.execute(() -> {
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
- // Unparent the task when this surface is destroyed
- mTransaction.reparent(mTaskLeash, null).apply();
- updateTaskVisibility();
+ // Unparent the task when this surface is destroyed
+ mTransaction.reparent(mTaskLeash, null).apply();
+ updateTaskVisibility();
+ });
+ }
+
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ // TODO(b/176854108): Consider to move the logic into gatherTransparentRegions since this
+ // is dependent on the order of listener.
+ // If there are multiple TaskViews, we'll set the touchable area as the root-view, then
+ // subtract each TaskView from it.
+ if (inoutInfo.touchableRegion.isEmpty()) {
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ View root = getRootView();
+ root.getLocationInWindow(mTmpLocation);
+ mTmpRootRect.set(mTmpLocation[0], mTmpLocation[1], root.getWidth(), root.getHeight());
+ inoutInfo.touchableRegion.set(mTmpRootRect);
+ }
+ getLocationInWindow(mTmpLocation);
+ mTmpRect.set(mTmpLocation[0], mTmpLocation[1],
+ mTmpLocation[0] + getWidth(), mTmpLocation[1] + getHeight());
+ inoutInfo.touchableRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactory.java
new file mode 100644
index 0000000..a29e7a0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactory.java
@@ -0,0 +1,32 @@
+/*
+ * 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.wm.shell;
+
+import android.annotation.UiContext;
+import android.content.Context;
+
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/** Interface to create TaskView. */
+@ExternalThread
+public interface TaskViewFactory {
+ /** Creates an {@link TaskView} */
+ void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
new file mode 100644
index 0000000..a5dd79b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -0,0 +1,62 @@
+/*
+ * 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.wm.shell;
+
+import android.annotation.UiContext;
+import android.content.Context;
+
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/** Factory controller which can create {@link TaskView} */
+public class TaskViewFactoryController {
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final ShellExecutor mShellExecutor;
+
+ public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
+ ShellExecutor shellExecutor) {
+ mTaskOrganizer = taskOrganizer;
+ mShellExecutor = shellExecutor;
+ }
+
+ /** Creates an {@link TaskView} */
+ @ShellMainThread
+ public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
+ TaskView taskView = new TaskView(context, mTaskOrganizer);
+ executor.execute(() -> {
+ onCreate.accept(taskView);
+ });
+ }
+
+ public TaskViewFactory getTaskViewFactory() {
+ return new TaskViewFactoryImpl();
+ }
+
+ private class TaskViewFactoryImpl implements TaskViewFactory {
+ @ExternalThread
+ public void create(@UiContext Context context,
+ Executor executor, Consumer<TaskView> onCreate) {
+ mShellExecutor.execute(() -> {
+ TaskViewFactoryController.this.create(context, executor, onCreate);
+ });
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
index 834de3f..abd9257 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
@@ -37,8 +37,8 @@
*/
private final PinnedStackListenerForwarder mPinnedStackListenerForwarder;
- public WindowManagerShellWrapper(ShellExecutor shellMainExecutor) {
- mPinnedStackListenerForwarder = new PinnedStackListenerForwarder(shellMainExecutor);
+ public WindowManagerShellWrapper(ShellExecutor mainExecutor) {
+ mPinnedStackListenerForwarder = new PinnedStackListenerForwarder(mainExecutor);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 40b41e1..9419b9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -45,7 +45,6 @@
import android.graphics.PointF;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -68,6 +67,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import java.io.FileDescriptor;
@@ -106,7 +106,6 @@
private final FloatingContentCoordinator mFloatingContentCoordinator;
private final BubbleDataRepository mDataRepository;
private BubbleLogger mLogger;
- private final Handler mMainHandler;
private BubbleData mBubbleData;
private View mBubbleScrim;
@Nullable private BubbleStackView mStackView;
@@ -138,7 +137,7 @@
private WindowManager mWindowManager;
// Used to post to main UI thread
- private Handler mHandler = new Handler();
+ private final ShellExecutor mMainExecutor;
/** LayoutParams used to add the BubbleStackView to the window manager. */
private WindowManager.LayoutParams mWmLayoutParams;
@@ -186,15 +185,15 @@
WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps,
UiEventLogger uiEventLogger,
- Handler mainHandler,
- ShellTaskOrganizer organizer) {
+ ShellTaskOrganizer organizer,
+ ShellExecutor mainExecutor) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
BubblePositioner positioner = new BubblePositioner(context, windowManager);
BubbleData data = new BubbleData(context, logger, positioner);
return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
new BubbleDataRepository(context, launcherApps),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- logger, mainHandler, organizer, positioner);
+ logger, organizer, positioner, mainExecutor);
}
/**
@@ -211,14 +210,14 @@
WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps,
BubbleLogger bubbleLogger,
- Handler mainHandler,
ShellTaskOrganizer organizer,
- BubblePositioner positioner) {
+ BubblePositioner positioner,
+ ShellExecutor mainExecutor) {
mContext = context;
mFloatingContentCoordinator = floatingContentCoordinator;
mDataRepository = dataRepository;
mLogger = bubbleLogger;
- mMainHandler = mainHandler;
+ mMainExecutor = mainExecutor;
mBubblePositioner = positioner;
mBubbleData = data;
@@ -241,7 +240,7 @@
bubble.setPendingIntentCanceled();
return;
}
- mHandler.post(() -> removeBubble(bubble.getKey(), DISMISS_INVALID_INTENT));
+ mMainExecutor.execute(() -> removeBubble(bubble.getKey(), DISMISS_INVALID_INTENT));
});
try {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 86da2b5..cb6b543 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -314,7 +314,7 @@
mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
- mTaskView.setListener(mTaskViewListener);
+ mTaskView.setListener(mContext.getMainExecutor(), mTaskViewListener);
mPositioner = mController.getPositioner();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 1e5e43a..0f81d7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1201,15 +1201,7 @@
mTempRect.setEmpty();
getTouchableRegion(mTempRect);
- if (mIsExpanded && mExpandedBubble != null
- && mExpandedBubble.getExpandedView() != null
- && mExpandedBubble.getExpandedView().getTaskView() != null) {
- inoutInfo.touchableRegion.set(mTempRect);
- mExpandedBubble.getExpandedView().getTaskView().getBoundsOnScreen(mTempRect);
- inoutInfo.touchableRegion.op(mTempRect, Region.Op.DIFFERENCE);
- } else {
- inoutInfo.touchableRegion.set(mTempRect);
- }
+ inoutInfo.touchableRegion.set(mTempRect);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index cb4584c..3a7b534 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.common;
-import android.os.Handler;
import android.os.RemoteException;
import android.view.IDisplayWindowRotationCallback;
import android.view.IDisplayWindowRotationController;
@@ -37,7 +36,7 @@
*/
public class DisplayChangeController {
- private final Handler mHandler;
+ private final ShellExecutor mMainExecutor;
private final IWindowManager mWmService;
private final IDisplayWindowRotationController mControllerImpl;
@@ -45,8 +44,8 @@
new ArrayList<>();
private final ArrayList<OnDisplayChangingListener> mTmpListeners = new ArrayList<>();
- public DisplayChangeController(Handler mainHandler, IWindowManager wmService) {
- mHandler = mainHandler;
+ public DisplayChangeController(IWindowManager wmService, ShellExecutor mainExecutor) {
+ mMainExecutor = mainExecutor;
mWmService = wmService;
mControllerImpl = new DisplayWindowRotationControllerImpl();
try {
@@ -97,7 +96,7 @@
@Override
public void onRotateDisplay(int displayId, final int fromRotation,
final int toRotation, IDisplayWindowRotationCallback callback) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
DisplayChangeController.this.onRotateDisplay(displayId, fromRotation, toRotation,
callback);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index a413c05..ba9ba5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
-import android.os.Handler;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
@@ -44,7 +43,7 @@
public class DisplayController {
private static final String TAG = "DisplayController";
- private final Handler mHandler;
+ private final ShellExecutor mMainExecutor;
private final Context mContext;
private final IWindowManager mWmService;
private final DisplayChangeController mChangeController;
@@ -61,12 +60,12 @@
return displayManager.getDisplay(displayId);
}
- public DisplayController(Context context, Handler handler,
- IWindowManager wmService) {
- mHandler = handler;
+ public DisplayController(Context context, IWindowManager wmService,
+ ShellExecutor mainExecutor) {
+ mMainExecutor = mainExecutor;
mContext = context;
mWmService = wmService;
- mChangeController = new DisplayChangeController(mHandler, mWmService);
+ mChangeController = new DisplayChangeController(mWmService, mainExecutor);
mDisplayContainerListener = new DisplayWindowListenerImpl();
try {
mWmService.registerDisplayWindowListener(mDisplayContainerListener);
@@ -229,35 +228,35 @@
private class DisplayWindowListenerImpl extends IDisplayWindowListener.Stub {
@Override
public void onDisplayAdded(int displayId) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
DisplayController.this.onDisplayAdded(displayId);
});
}
@Override
public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
DisplayController.this.onDisplayConfigurationChanged(displayId, newConfig);
});
}
@Override
public void onDisplayRemoved(int displayId) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
DisplayController.this.onDisplayRemoved(displayId);
});
}
@Override
public void onFixedRotationStarted(int displayId, int newRotation) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
DisplayController.this.onFixedRotationStarted(displayId, newRotation);
});
}
@Override
public void onFixedRotationFinished(int displayId) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
DisplayController.this.onFixedRotationFinished(displayId);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 3fbd7ed..3944128 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -40,10 +40,12 @@
import android.view.animation.PathInterpolator;
import androidx.annotation.BinderThread;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.view.IInputMethodManager;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -64,7 +66,7 @@
private static final int FLOATING_IME_BOTTOM_INSET = -80;
protected final IWindowManager mWmService;
- protected final Executor mExecutor;
+ protected final Executor mMainExecutor;
private final TransactionPool mTransactionPool;
private final DisplayController mDisplayController;
private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
@@ -73,10 +75,10 @@
public DisplayImeController(IWindowManager wmService, DisplayController displayController,
Executor mainExecutor, TransactionPool transactionPool) {
- mExecutor = mainExecutor;
mWmService = wmService;
- mTransactionPool = transactionPool;
mDisplayController = displayController;
+ mMainExecutor = mainExecutor;
+ mTransactionPool = transactionPool;
}
/** Starts monitor displays changes and set insets controller for each displays. */
@@ -90,11 +92,7 @@
// WM will defer IME inset handling to it in multi-window scenarious.
PerDisplay pd = new PerDisplay(displayId,
mDisplayController.getDisplayLayout(displayId).rotation());
- try {
- mWmService.setDisplayWindowInsetsController(displayId, pd);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to set insets controller on display " + displayId);
- }
+ pd.register();
mImePerDisplay.put(displayId, pd);
}
@@ -182,9 +180,11 @@
}
/** An implementation of {@link IDisplayWindowInsetsController} for a given display id. */
- public class PerDisplay extends IDisplayWindowInsetsController.Stub {
+ public class PerDisplay {
final int mDisplayId;
final InsetsState mInsetsState = new InsetsState();
+ protected final DisplayWindowInsetsControllerImpl mInsetsControllerImpl =
+ new DisplayWindowInsetsControllerImpl();
InsetsSourceControl mImeSourceControl = null;
int mAnimationDirection = DIRECTION_NONE;
ValueAnimator mAnimation = null;
@@ -198,10 +198,16 @@
mRotation = initialRotation;
}
- @BinderThread
- @Override
- public void insetsChanged(InsetsState insetsState) {
- mExecutor.execute(() -> {
+ public void register() {
+ try {
+ mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId);
+ }
+ }
+
+ protected void insetsChanged(InsetsState insetsState) {
+ mMainExecutor.execute(() -> {
if (mInsetsState.equals(insetsState)) {
return;
}
@@ -220,9 +226,8 @@
});
}
- @BinderThread
- @Override
- public void insetsControlChanged(InsetsState insetsState,
+ @VisibleForTesting
+ protected void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
insetsChanged(insetsState);
if (activeControls != null) {
@@ -231,7 +236,7 @@
continue;
}
if (activeControl.getType() == InsetsState.ITYPE_IME) {
- mExecutor.execute(() -> {
+ mMainExecutor.execute(() -> {
final Point lastSurfacePosition = mImeSourceControl != null
? mImeSourceControl.getSurfacePosition() : null;
final boolean positionChanged =
@@ -271,30 +276,25 @@
}
}
- @BinderThread
- @Override
- public void showInsets(int types, boolean fromIme) {
+ protected void showInsets(int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
- mExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */));
+ mMainExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */));
}
- @BinderThread
- @Override
- public void hideInsets(int types, boolean fromIme) {
+
+ protected void hideInsets(int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
- mExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */));
+ mMainExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */));
}
- @BinderThread
- @Override
public void topFocusedWindowChanged(String packageName) {
- // no-op
+ // Do nothing
}
/**
@@ -457,6 +457,47 @@
setVisibleDirectly(true /* visible */);
}
}
+
+ @VisibleForTesting
+ @BinderThread
+ public class DisplayWindowInsetsControllerImpl
+ extends IDisplayWindowInsetsController.Stub {
+ @Override
+ public void topFocusedWindowChanged(String packageName) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ PerDisplay.this.topFocusedWindowChanged(packageName);
+ });
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ PerDisplay.this.insetsChanged(insetsState);
+ });
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ PerDisplay.this.insetsControlChanged(insetsState, activeControls);
+ });
+ }
+
+ @Override
+ public void showInsets(int types, boolean fromIme) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ PerDisplay.this.showInsets(types, fromIme);
+ });
+ }
+
+ @Override
+ public void hideInsets(int types, boolean fromIme) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ PerDisplay.this.hideInsets(types, fromIme);
+ });
+ }
+ }
}
void removeImeSurface() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index 22b831b..f2c57f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -51,6 +51,16 @@
}
/**
+ * Convenience method to execute the blocking call with a default timeout.
+ *
+ * @throws InterruptedException if runnable does not return in the time specified by
+ * {@param waitTimeout}
+ */
+ default void executeBlocking(Runnable runnable) throws InterruptedException {
+ executeBlocking(runnable, 2, TimeUnit.SECONDS);
+ }
+
+ /**
* See {@link android.os.Handler#postDelayed(Runnable, long)}.
*/
void executeDelayed(Runnable r, long delayMillis);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index 7321dc8..33beab5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -16,16 +16,14 @@
package com.android.wm.shell.common;
+import android.annotation.BinderThread;
import android.annotation.NonNull;
-import android.os.Handler;
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;
import android.window.WindowOrganizer;
-import androidx.annotation.BinderThread;
-
import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.ArrayList;
@@ -41,7 +39,7 @@
private static final int REPLY_TIMEOUT = 5300;
private final TransactionPool mTransactionPool;
- private final Handler mHandler;
+ private final ShellExecutor mMainExecutor;
// Sync Transactions currently don't support nesting or interleaving properly, so
// queue up transactions to run them serially.
@@ -59,9 +57,9 @@
}
};
- public SyncTransactionQueue(TransactionPool pool, Handler handler) {
+ public SyncTransactionQueue(TransactionPool pool, ShellExecutor mainExecutor) {
mTransactionPool = pool;
- mHandler = handler;
+ mMainExecutor = mainExecutor;
}
/**
@@ -152,14 +150,14 @@
if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
- mHandler.postDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
+ mMainExecutor.executeDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
}
@BinderThread
@Override
public void onTransactionReady(int id,
@NonNull SurfaceControl.Transaction t) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
synchronized (mQueue) {
if (mId != id) {
Slog.e(TAG, "Got an unexpected onTransactionReady. Expected "
@@ -167,7 +165,7 @@
return;
}
mInFlight = null;
- mHandler.removeCallbacks(mOnReplyTimeout);
+ mMainExecutor.removeCallbacks(mOnReplyTimeout);
if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
mQueue.remove(this);
onTransactionReceived(t);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
index 718f7a0..db34248 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
@@ -71,11 +71,11 @@
private final IActivityTaskManager mActivityTaskManager;
// NOTE: In this case we do want to use a handler since we rely on the message system to
// efficiently dedupe sequential calls
- private Handler mHandler;
+ private Handler mMainHandler;
- public TaskStackListenerImpl(Handler handler) {
+ public TaskStackListenerImpl(Handler mainHandler) {
mActivityTaskManager = ActivityTaskManager.getService();
- mHandler = new Handler(handler.getLooper(), this);
+ mMainHandler = new Handler(mainHandler.getLooper(), this);
}
@VisibleForTesting
@@ -84,8 +84,8 @@
}
@VisibleForTesting
- void setHandler(Handler handler) {
- mHandler = handler;
+ void setHandler(Handler mainHandler) {
+ mMainHandler = mainHandler;
}
public void addListener(TaskStackListenerCallback listener) {
@@ -124,13 +124,13 @@
@Override
public void onRecentTaskListUpdated() {
- mHandler.obtainMessage(ON_TASK_LIST_UPDATED).sendToTarget();
+ mMainHandler.obtainMessage(ON_TASK_LIST_UPDATED).sendToTarget();
}
@Override
public void onRecentTaskListFrozenChanged(boolean frozen) {
- mHandler.obtainMessage(ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */)
- .sendToTarget();
+ mMainHandler.obtainMessage(ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0,
+ 0 /* unused */).sendToTarget();
}
@Override
@@ -147,48 +147,50 @@
}
mTmpListeners.clear();
- mHandler.removeMessages(ON_TASK_STACK_CHANGED);
- mHandler.sendEmptyMessage(ON_TASK_STACK_CHANGED);
+ mMainHandler.removeMessages(ON_TASK_STACK_CHANGED);
+ mMainHandler.sendEmptyMessage(ON_TASK_STACK_CHANGED);
}
@Override
public void onTaskProfileLocked(int taskId, int userId) {
- mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
+ mMainHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
}
@Override
public void onTaskDisplayChanged(int taskId, int newDisplayId) {
- mHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
+ mMainHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId,
+ newDisplayId).sendToTarget();
}
@Override
public void onTaskCreated(int taskId, ComponentName componentName) {
- mHandler.obtainMessage(ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
+ mMainHandler.obtainMessage(ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
}
@Override
public void onTaskRemoved(int taskId) {
- mHandler.obtainMessage(ON_TASK_REMOVED, taskId, 0).sendToTarget();
+ mMainHandler.obtainMessage(ON_TASK_REMOVED, taskId, 0).sendToTarget();
}
@Override
public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
- mHandler.obtainMessage(ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
+ mMainHandler.obtainMessage(ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
}
@Override
public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo) {
- mHandler.obtainMessage(ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
+ mMainHandler.obtainMessage(ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
}
@Override
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
- mHandler.obtainMessage(ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
+ mMainHandler.obtainMessage(ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot)
+ .sendToTarget();
}
@Override
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
- mHandler.obtainMessage(ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
+ mMainHandler.obtainMessage(ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
}
@Override
@@ -198,44 +200,44 @@
args.argi1 = userId;
args.argi2 = taskId;
args.argi3 = stackId;
- mHandler.removeMessages(ON_ACTIVITY_PINNED);
- mHandler.obtainMessage(ON_ACTIVITY_PINNED, args).sendToTarget();
+ mMainHandler.removeMessages(ON_ACTIVITY_PINNED);
+ mMainHandler.obtainMessage(ON_ACTIVITY_PINNED, args).sendToTarget();
}
@Override
public void onActivityUnpinned() {
- mHandler.removeMessages(ON_ACTIVITY_UNPINNED);
- mHandler.sendEmptyMessage(ON_ACTIVITY_UNPINNED);
+ mMainHandler.removeMessages(ON_ACTIVITY_UNPINNED);
+ mMainHandler.sendEmptyMessage(ON_ACTIVITY_UNPINNED);
}
@Override
- public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask, boolean wasVisible) {
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = task;
args.argi1 = homeTaskVisible ? 1 : 0;
args.argi2 = clearedTask ? 1 : 0;
args.argi3 = wasVisible ? 1 : 0;
- mHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT);
- mHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
+ mMainHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT);
+ mMainHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
}
@Override
public void onActivityForcedResizable(String packageName, int taskId, int reason) {
- mHandler.obtainMessage(ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
+ mMainHandler.obtainMessage(ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
.sendToTarget();
}
@Override
public void onActivityDismissingDockedStack() {
- mHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK);
+ mMainHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK);
}
@Override
public void onActivityLaunchOnSecondaryDisplayFailed(
ActivityManager.RunningTaskInfo taskInfo,
int requestedDisplayId) {
- mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED,
+ mMainHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED,
requestedDisplayId,
0 /* unused */,
taskInfo).sendToTarget();
@@ -245,25 +247,25 @@
public void onActivityLaunchOnSecondaryDisplayRerouted(
ActivityManager.RunningTaskInfo taskInfo,
int requestedDisplayId) {
- mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
+ mMainHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
}
@Override
public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
- mHandler.obtainMessage(ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
+ mMainHandler.obtainMessage(ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
requestedOrientation).sendToTarget();
}
@Override
public void onActivityRotation(int displayId) {
- mHandler.obtainMessage(ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
+ mMainHandler.obtainMessage(ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
.sendToTarget();
}
@Override
public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
- mHandler.obtainMessage(ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId,
+ mMainHandler.obtainMessage(ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId,
0 /* unused */,
activityToken).sendToTarget();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 8ae6679..c8b4e10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -74,11 +74,11 @@
public DragAndDropController(Context context, DisplayController displayController) {
mContext = context;
mDisplayController = displayController;
- mDisplayController.addDisplayWindowListener(this);
}
- public void setSplitScreenController(Optional<LegacySplitScreen> splitscreen) {
+ public void initialize(Optional<LegacySplitScreen> splitscreen) {
mLegacySplitScreen = splitscreen.orElse(null);
+ mDisplayController.addDisplayWindowListener(this);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
index b92846f..a891005 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
@@ -25,6 +25,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -37,12 +38,15 @@
private final Context mContext;
private final HideDisplayCutoutOrganizer mOrganizer;
+ private final ShellExecutor mMainExecutor;
@VisibleForTesting
boolean mEnabled;
- HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer) {
+ HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer,
+ ShellExecutor mainExecutor) {
mContext = context;
mOrganizer = organizer;
+ mMainExecutor = mainExecutor;
updateStatus();
}
@@ -52,7 +56,7 @@
*/
@Nullable
public static HideDisplayCutoutController create(
- Context context, DisplayController displayController, Executor executor) {
+ Context context, DisplayController displayController, ShellExecutor mainExecutor) {
// The SystemProperty is set for devices that support this feature and is used to control
// whether to create the HideDisplayCutout instance.
// It's defined in the device.mk (e.g. device/google/crosshatch/device.mk).
@@ -61,8 +65,8 @@
}
HideDisplayCutoutOrganizer organizer =
- new HideDisplayCutoutOrganizer(context, displayController, executor);
- return new HideDisplayCutoutController(context, organizer);
+ new HideDisplayCutoutOrganizer(context, displayController, mainExecutor);
+ return new HideDisplayCutoutController(context, organizer, mainExecutor);
}
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
index 51a35d8..53dd391 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
@@ -42,6 +42,7 @@
import com.android.internal.R;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
import java.util.List;
@@ -90,8 +91,8 @@
};
HideDisplayCutoutOrganizer(Context context, DisplayController displayController,
- Executor executor) {
- super(executor);
+ ShellExecutor mainExecutor) {
+ super(mainExecutor);
mContext = context;
mDisplayController = displayController;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
index 2c0cf59..7ce9014 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
@@ -24,7 +24,6 @@
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.graphics.Rect;
-import android.os.Handler;
import android.util.Slog;
import android.view.Choreographer;
import android.view.SurfaceControl;
@@ -33,6 +32,7 @@
import android.window.WindowContainerTransaction;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
class DividerImeController implements DisplayImeController.ImePositionProcessor {
@@ -43,7 +43,7 @@
private final LegacySplitScreenTaskListener mSplits;
private final TransactionPool mTransactionPool;
- private final Handler mHandler;
+ private final ShellExecutor mMainExecutor;
private final TaskOrganizer mTaskOrganizer;
/**
@@ -94,10 +94,10 @@
private boolean mAdjustedWhileHidden = false;
DividerImeController(LegacySplitScreenTaskListener splits, TransactionPool pool,
- Handler handler, TaskOrganizer taskOrganizer) {
+ ShellExecutor mainExecutor, TaskOrganizer taskOrganizer) {
mSplits = splits;
mTransactionPool = pool;
- mHandler = handler;
+ mMainExecutor = mainExecutor;
mTaskOrganizer = taskOrganizer;
}
@@ -377,7 +377,7 @@
/** Completely aborts/resets adjustment state */
public void pause(int displayId) {
if (DEBUG) Slog.d(TAG, "ime pause posting " + dumpState());
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
if (DEBUG) Slog.d(TAG, "ime pause run posted " + dumpState());
if (mPaused) {
return;
@@ -396,7 +396,7 @@
public void resume(int displayId) {
if (DEBUG) Slog.d(TAG, "ime resume posting " + dumpState());
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
if (DEBUG) Slog.d(TAG, "ime resume run posted " + dumpState());
if (!mPaused) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java
index ee5c9bc..139544f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java
@@ -22,12 +22,12 @@
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
-import android.os.Handler;
import android.os.UserHandle;
import android.util.ArraySet;
import android.widget.Toast;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.ShellExecutor;
import java.util.function.Consumer;
@@ -40,7 +40,7 @@
private static final int TIMEOUT = 1000;
private final Context mContext;
- private final Handler mHandler = new Handler();
+ private final ShellExecutor mMainExecutor;
private final ArraySet<PendingTaskRecord> mPendingTasks = new ArraySet<>();
private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
private boolean mDividerDragging;
@@ -69,15 +69,17 @@
}
ForcedResizableInfoActivityController(Context context,
- LegacySplitScreenController splitScreenController) {
+ LegacySplitScreenController splitScreenController,
+ ShellExecutor mainExecutor) {
mContext = context;
+ mMainExecutor = mainExecutor;
splitScreenController.registerInSplitScreenListener(mDockedStackExistsListener);
}
@Override
public void onDraggingStart() {
mDividerDragging = true;
- mHandler.removeCallbacks(mTimeoutRunnable);
+ mMainExecutor.removeCallbacks(mTimeoutRunnable);
}
@Override
@@ -111,7 +113,7 @@
}
private void showPending() {
- mHandler.removeCallbacks(mTimeoutRunnable);
+ mMainExecutor.removeCallbacks(mTimeoutRunnable);
for (int i = mPendingTasks.size() - 1; i >= 0; i--) {
PendingTaskRecord pendingRecord = mPendingTasks.valueAt(i);
Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class);
@@ -127,8 +129,8 @@
}
private void postTimeout() {
- mHandler.removeCallbacks(mTimeoutRunnable);
- mHandler.postDelayed(mTimeoutRunnable, TIMEOUT);
+ mMainExecutor.removeCallbacks(mTimeoutRunnable);
+ mMainExecutor.executeDelayed(mTimeoutRunnable, TIMEOUT);
}
private boolean debounce(String packageName) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
index 789bd1a..9e1f0a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
@@ -30,7 +30,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.Handler;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Slog;
@@ -49,6 +48,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerCallback;
@@ -80,7 +80,7 @@
private final DividerImeController mImePositionProcessor;
private final DividerState mDividerState = new DividerState();
private final ForcedResizableInfoActivityController mForcedResizableController;
- private final Handler mHandler;
+ private final ShellExecutor mMainExecutor;
private final LegacySplitScreenTaskListener mSplits;
private final SystemWindows mSystemWindows;
final TransactionPool mTransactionPool;
@@ -112,21 +112,23 @@
public LegacySplitScreenController(Context context,
DisplayController displayController, SystemWindows systemWindows,
- DisplayImeController imeController, Handler handler, TransactionPool transactionPool,
+ DisplayImeController imeController, TransactionPool transactionPool,
ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
- TaskStackListenerImpl taskStackListener, Transitions transitions) {
+ TaskStackListenerImpl taskStackListener, Transitions transitions,
+ ShellExecutor mainExecutor) {
mContext = context;
mDisplayController = displayController;
mSystemWindows = systemWindows;
mImeController = imeController;
- mHandler = handler;
- mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
+ mMainExecutor = mainExecutor;
+ mForcedResizableController = new ForcedResizableInfoActivityController(context, this,
+ mainExecutor);
mTransactionPool = transactionPool;
mWindowManagerProxy = new WindowManagerProxy(syncQueue, shellTaskOrganizer);
mTaskOrganizer = shellTaskOrganizer;
mSplits = new LegacySplitScreenTaskListener(this, shellTaskOrganizer, transitions,
syncQueue);
- mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler,
+ mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mMainExecutor,
shellTaskOrganizer);
mRotationController =
(display, fromRotation, toRotation, wct) -> {
@@ -271,11 +273,6 @@
}
}
- /** Posts task to handler dealing with divider. */
- void post(Runnable task) {
- mHandler.post(task);
- }
-
@Override
public DividerView getDividerView() {
return mView;
@@ -345,7 +342,7 @@
}
void onTaskVanished() {
- mHandler.post(this::removeDivider);
+ removeDivider();
}
private void updateVisibility(final boolean visible) {
@@ -379,7 +376,7 @@
@Override
public void setMinimized(final boolean minimized) {
if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible);
if (!mVisible) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
index 8c624cdb..3ed070b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
@@ -204,7 +204,7 @@
return;
}
- mSplitScreenController.post(() -> handleTaskInfoChanged(taskInfo));
+ handleTaskInfoChanged(taskInfo);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index 9639096..146f231 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -203,7 +203,7 @@
mSurfaceTransactionHelper = helper;
}
- OneHandedTransitionAnimator<T> setOneHandedAnimationCallbacks(
+ OneHandedTransitionAnimator<T> addOneHandedAnimationCallback(
OneHandedAnimationCallback callback) {
mOneHandedAnimationCallbacks.add(callback);
return this;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
new file mode 100644
index 0000000..a74f476
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
@@ -0,0 +1,201 @@
+/*
+ * 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.wm.shell.onehanded;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.util.Log;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.window.DisplayAreaAppearedInfo;
+import android.window.DisplayAreaInfo;
+import android.window.DisplayAreaOrganizer;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayController;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages OneHanded color background layer areas.
+ * To avoid when turning the Dark theme on, users can not clearly identify
+ * the screen has entered one handed mode.
+ */
+public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer
+ implements OneHandedTransitionCallback {
+ private static final String TAG = "OneHandedBackgroundPanelOrganizer";
+
+ private final Object mLock = new Object();
+ private final SurfaceSession mSurfaceSession = new SurfaceSession();
+ private final float[] mColor;
+ private final float mAlpha;
+ private final Rect mRect;
+ private final Handler mHandler;
+ private final Point mDisplaySize = new Point();
+ private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ mSurfaceControlTransactionFactory;
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ boolean mIsShowing;
+ @Nullable
+ @GuardedBy("mLock")
+ private SurfaceControl mBackgroundSurface;
+ @Nullable
+ @GuardedBy("mLock")
+ private SurfaceControl mParentLeash;
+
+ private final OneHandedAnimationCallback mOneHandedAnimationCallback =
+ new OneHandedAnimationCallback() {
+ @Override
+ public void onOneHandedAnimationStart(
+ OneHandedAnimationController.OneHandedTransitionAnimator animator) {
+ mHandler.post(() -> showBackgroundPanelLayer());
+ }
+ };
+
+ @Override
+ public void onStopFinished(Rect bounds) {
+ mHandler.post(() -> removeBackgroundPanelLayer());
+ }
+
+ public OneHandedBackgroundPanelOrganizer(Context context, DisplayController displayController,
+ Executor executor) {
+ super(executor);
+ displayController.getDisplay(DEFAULT_DISPLAY).getRealSize(mDisplaySize);
+ final Resources res = context.getResources();
+ final float defaultRGB = res.getFloat(R.dimen.config_one_handed_background_rgb);
+ mColor = new float[]{defaultRGB, defaultRGB, defaultRGB};
+ mAlpha = res.getFloat(R.dimen.config_one_handed_background_alpha);
+ mRect = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
+ mHandler = new Handler();
+ mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ }
+
+ @Override
+ public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
+ @NonNull SurfaceControl leash) {
+ synchronized (mLock) {
+ if (mParentLeash == null) {
+ mParentLeash = leash;
+ } else {
+ throw new RuntimeException("There should be only one DisplayArea for "
+ + "the one-handed mode background panel");
+ }
+ }
+ }
+
+ OneHandedAnimationCallback getOneHandedAnimationCallback() {
+ return mOneHandedAnimationCallback;
+ }
+
+ @Override
+ public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) {
+ synchronized (mLock) {
+ final List<DisplayAreaAppearedInfo> displayAreaInfos;
+ displayAreaInfos = super.registerOrganizer(displayAreaFeature);
+ for (int i = 0; i < displayAreaInfos.size(); i++) {
+ final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
+ onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
+ }
+ return displayAreaInfos;
+ }
+ }
+
+ @Override
+ public void unregisterOrganizer() {
+ synchronized (mLock) {
+ super.unregisterOrganizer();
+ mParentLeash = null;
+ }
+ }
+
+ @Nullable
+ @VisibleForTesting
+ SurfaceControl getBackgroundSurface() {
+ synchronized (mLock) {
+ if (mParentLeash == null) {
+ return null;
+ }
+
+ if (mBackgroundSurface == null) {
+ mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession)
+ .setParent(mParentLeash)
+ .setColorLayer()
+ .setFormat(PixelFormat.RGBA_8888)
+ .setOpaque(false)
+ .setName("one-handed-background-panel")
+ .setCallsite("OneHandedBackgroundPanelOrganizer")
+ .build();
+ }
+ return mBackgroundSurface;
+ }
+ }
+
+ @VisibleForTesting
+ void showBackgroundPanelLayer() {
+ synchronized (mLock) {
+ if (mIsShowing) {
+ return;
+ }
+
+ if (getBackgroundSurface() == null) {
+ Log.w(TAG, "mBackgroundSurface is null !");
+ return;
+ }
+
+ SurfaceControl.Transaction transaction =
+ mSurfaceControlTransactionFactory.getTransaction();
+ transaction.setLayer(mBackgroundSurface, -1 /* at bottom-most layer */)
+ .setColor(mBackgroundSurface, mColor)
+ .setAlpha(mBackgroundSurface, mAlpha)
+ .show(mBackgroundSurface)
+ .apply();
+ transaction.close();
+ mIsShowing = true;
+ }
+ }
+
+ @VisibleForTesting
+ void removeBackgroundPanelLayer() {
+ synchronized (mLock) {
+ if (mBackgroundSurface == null) {
+ return;
+ }
+
+ SurfaceControl.Transaction transaction =
+ mSurfaceControlTransactionFactory.getTransaction();
+ transaction.remove(mBackgroundSurface);
+ transaction.apply();
+ transaction.close();
+ mBackgroundSurface = null;
+ mIsShowing = false;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 69d8db2..00605d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -77,6 +77,7 @@
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
private final AccessibilityManager mAccessibilityManager;
+ private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
/**
* Handle rotation based on OnDisplayChangingListener callback
@@ -204,17 +205,22 @@
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler();
OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler(
context, displayController);
+ OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
+ new OneHandedBackgroundPanelOrganizer(context, displayController, executor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
- context, displayController, animationController, tutorialHandler, executor);
+ context, displayController, animationController, tutorialHandler, executor,
+ oneHandedBackgroundPanelOrganizer);
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
- return new OneHandedController(context, displayController, organizer, touchHandler,
- tutorialHandler, gestureHandler, overlayManager, taskStackListener);
+ return new OneHandedController(context, displayController,
+ oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
+ gestureHandler, overlayManager, taskStackListener);
}
@VisibleForTesting
OneHandedController(Context context,
DisplayController displayController,
+ OneHandedBackgroundPanelOrganizer backgroundPanelOrganizer,
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
OneHandedTouchHandler touchHandler,
OneHandedTutorialHandler tutorialHandler,
@@ -222,6 +228,7 @@
IOverlayManager overlayManager,
TaskStackListenerImpl taskStackListener) {
mContext = context;
+ mBackgroundPanelOrganizer = backgroundPanelOrganizer;
mDisplayAreaOrganizer = displayAreaOrganizer;
mDisplayController = displayController;
mTouchHandler = touchHandler;
@@ -355,6 +362,7 @@
mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler);
mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
+ mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer);
}
private void setupSettingObservers() {
@@ -405,9 +413,12 @@
}
// TODO Be aware to unregisterOrganizer() after animation finished
mDisplayAreaOrganizer.unregisterOrganizer();
+ mBackgroundPanelOrganizer.unregisterOrganizer();
if (mIsOneHandedEnabled) {
mDisplayAreaOrganizer.registerOrganizer(
OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
+ mBackgroundPanelOrganizer.registerOrganizer(
+ OneHandedBackgroundPanelOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL);
}
mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 7fb1faa..7873318 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -86,6 +86,7 @@
mSurfaceControlTransactionFactory;
private OneHandedTutorialHandler mTutorialHandler;
private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
+ private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
@VisibleForTesting
OneHandedAnimationCallback mOneHandedAnimationCallback =
@@ -152,7 +153,8 @@
public OneHandedDisplayAreaOrganizer(Context context,
DisplayController displayController,
OneHandedAnimationController animationController,
- OneHandedTutorialHandler tutorialHandler, Executor executor) {
+ OneHandedTutorialHandler tutorialHandler, Executor executor,
+ OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer) {
super(executor);
mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
mAnimationController = animationController;
@@ -166,6 +168,7 @@
animationDurationConfig);
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mTutorialHandler = tutorialHandler;
+ mBackgroundPanelOrganizer = oneHandedBackgroundGradientOrganizer;
}
@Override
@@ -300,8 +303,10 @@
mAnimationController.getAnimator(leash, fromBounds, toBounds);
if (animator != null) {
animator.setTransitionDirection(direction)
- .setOneHandedAnimationCallbacks(mOneHandedAnimationCallback)
- .setOneHandedAnimationCallbacks(mTutorialHandler.getAnimationCallback())
+ .addOneHandedAnimationCallback(mOneHandedAnimationCallback)
+ .addOneHandedAnimationCallback(mTutorialHandler.getAnimationCallback())
+ .addOneHandedAnimationCallback(
+ mBackgroundPanelOrganizer.getOneHandedAnimationCallback())
.setDuration(durationMs)
.start();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
index 951a688..aa4ec17 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
@@ -88,7 +88,7 @@
mDisplayController = displayController;
displayController.addDisplayChangingController(this);
mNavGestureHeight = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_gesture_height);
+ com.android.internal.R.dimen.navigation_bar_gesture_larger_height);
mDragDistThreshold = context.getResources().getDimensionPixelSize(
R.dimen.gestures_onehanded_drag_threshold);
final float slop = ViewConfiguration.get(context).getScaledTouchSlop();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
index d59aec2..8f8ec47 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
@@ -38,11 +38,11 @@
public class PinnedStackListenerForwarder {
private final IPinnedStackListener mListenerImpl = new PinnedStackListenerImpl();
- private final ShellExecutor mShellMainExecutor;
+ private final ShellExecutor mMainExecutor;
private final ArrayList<PinnedStackListener> mListeners = new ArrayList<>();
- public PinnedStackListenerForwarder(ShellExecutor shellMainExecutor) {
- mShellMainExecutor = shellMainExecutor;
+ public PinnedStackListenerForwarder(ShellExecutor mainExecutor) {
+ mMainExecutor = mainExecutor;
}
/** Adds a listener to receive updates from the WindowManagerService. */
@@ -94,35 +94,35 @@
private class PinnedStackListenerImpl extends IPinnedStackListener.Stub {
@Override
public void onMovementBoundsChanged(boolean fromImeAdjustment) {
- mShellMainExecutor.execute(() -> {
+ mMainExecutor.execute(() -> {
PinnedStackListenerForwarder.this.onMovementBoundsChanged(fromImeAdjustment);
});
}
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
- mShellMainExecutor.execute(() -> {
+ mMainExecutor.execute(() -> {
PinnedStackListenerForwarder.this.onImeVisibilityChanged(imeVisible, imeHeight);
});
}
@Override
public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
- mShellMainExecutor.execute(() -> {
+ mMainExecutor.execute(() -> {
PinnedStackListenerForwarder.this.onActionsChanged(actions);
});
}
@Override
public void onActivityHidden(ComponentName componentName) {
- mShellMainExecutor.execute(() -> {
+ mMainExecutor.execute(() -> {
PinnedStackListenerForwarder.this.onActivityHidden(componentName);
});
}
@Override
public void onAspectRatioChanged(float aspectRatio) {
- mShellMainExecutor.execute(() -> {
+ mMainExecutor.execute(() -> {
PinnedStackListenerForwarder.this.onAspectRatioChanged(aspectRatio);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index e32d3b9..df7c753 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -83,8 +83,18 @@
private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
ThreadLocal.withInitial(() -> {
- FrameCallbackScheduler scheduler = runnable ->
+ final Looper initialLooper = Looper.myLooper();
+ final FrameCallbackScheduler scheduler = new FrameCallbackScheduler() {
+ @Override
+ public void postFrameCallback(@androidx.annotation.NonNull Runnable runnable) {
Choreographer.getSfInstance().postFrameCallback(t -> runnable.run());
+ }
+
+ @Override
+ public boolean isCurrentThread() {
+ return Looper.myLooper() == initialLooper;
+ }
+ };
AnimationHandler handler = new AnimationHandler(scheduler);
return handler;
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 33439a4..6b6bf5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -155,7 +155,7 @@
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
- ShellExecutor shellMainExecutor) {
+ ShellExecutor mainExecutor) {
// Initialize the Pip input consumer
mContext = context;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
@@ -186,7 +186,7 @@
mFloatingContentCoordinator = floatingContentCoordinator;
mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
- this::onAccessibilityShowMenu, this::updateMovementBounds, shellMainExecutor);
+ this::onAccessibilityShowMenu, this::updateMovementBounds, mainExecutor);
mPipUiEventLogger = pipUiEventLogger;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 786a1fd..8e24e0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -222,7 +222,11 @@
}
Context context = mContext;
- final int theme = activityInfo.getThemeResource();
+ int theme = activityInfo.getThemeResource();
+ if (theme == 0) {
+ // replace with the default theme if the application didn't set
+ theme = com.android.internal.R.style.Theme_DeviceDefault_DayNight;
+ }
if (DEBUG_SPLASH_SCREEN) {
Slog.d(TAG, "addSplashScreen " + activityInfo.packageName
+ ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index 01b5204..eb03ab0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -99,7 +99,7 @@
when(mOrganizer.getExecutor()).thenReturn(mExecutor);
mTaskView = new TaskView(mContext, mOrganizer);
- mTaskView.setListener(mViewListener);
+ mTaskView.setListener(mExecutor, mViewListener);
}
@After
@@ -112,9 +112,9 @@
@Test
public void testSetPendingListener_throwsException() {
TaskView taskView = new TaskView(mContext, mOrganizer);
- taskView.setListener(mViewListener);
+ taskView.setListener(mExecutor, mViewListener);
try {
- taskView.setListener(mViewListener);
+ taskView.setListener(mExecutor, mViewListener);
} catch (IllegalStateException e) {
// pass
return;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
index 595440f..f10dc16 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
@@ -27,6 +27,8 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.common.ShellExecutor;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,12 +46,14 @@
private HideDisplayCutoutController mHideDisplayCutoutController;
@Mock
private HideDisplayCutoutOrganizer mMockDisplayAreaOrganizer;
+ @Mock
+ private ShellExecutor mMockMainExecutor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mHideDisplayCutoutController = new HideDisplayCutoutController(
- mContext, mMockDisplayAreaOrganizer);
+ mContext, mMockDisplayAreaOrganizer, mMockMainExecutor);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
index e0c835b..9637570 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
@@ -50,6 +50,7 @@
import com.android.internal.R;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Test;
@@ -72,6 +73,9 @@
private DisplayController mMockDisplayController;
private HideDisplayCutoutOrganizer mOrganizer;
+ @Mock
+ private ShellExecutor mMockMainExecutor;
+
private DisplayAreaInfo mDisplayAreaInfo;
private SurfaceControl mLeash;
@@ -93,7 +97,7 @@
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
HideDisplayCutoutOrganizer organizer = new HideDisplayCutoutOrganizer(
- mContext, mMockDisplayController, Runnable::run);
+ mContext, mMockDisplayController, mMockMainExecutor);
mOrganizer = Mockito.spy(organizer);
doNothing().when(mOrganizer).unregisterOrganizer();
doNothing().when(mOrganizer).applyBoundsAndOffsets(any(), any(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
new file mode 100644
index 0000000..e9c4af1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.wm.shell.onehanded;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.Display;
+import android.view.SurfaceControl;
+import android.window.DisplayAreaInfo;
+import android.window.IWindowContainerToken;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.common.DisplayController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase {
+ private DisplayAreaInfo mDisplayAreaInfo;
+ private Display mDisplay;
+ private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
+ private WindowContainerToken mToken;
+ private SurfaceControl mLeash;
+ private TestableLooper mTestableLooper;
+
+ @Mock
+ IWindowContainerToken mMockRealToken;
+ @Mock
+ DisplayController mMockDisplayController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mTestableLooper = TestableLooper.get(this);
+ mToken = new WindowContainerToken(mMockRealToken);
+ mLeash = new SurfaceControl();
+ mDisplay = mContext.getDisplay();
+ when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
+ mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
+ FEATURE_ONE_HANDED_BACKGROUND_PANEL);
+
+ mBackgroundPanelOrganizer = new OneHandedBackgroundPanelOrganizer(mContext,
+ mMockDisplayController, Runnable::run);
+ }
+
+ @Test
+ public void testOnDisplayAreaAppeared() {
+ mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mBackgroundPanelOrganizer.getBackgroundSurface()).isNotNull();
+ }
+
+ @Test
+ public void testUnregisterOrganizer() {
+ mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
+ mTestableLooper.processAllMessages();
+ mBackgroundPanelOrganizer.unregisterOrganizer();
+
+ assertThat(mBackgroundPanelOrganizer.getBackgroundSurface()).isNull();
+ }
+
+ @Test
+ public void testShowBackgroundLayer() {
+ mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
+ mBackgroundPanelOrganizer.showBackgroundPanelLayer();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mBackgroundPanelOrganizer.mIsShowing).isTrue();
+ }
+
+ @Test
+ public void testRemoveBackgroundLayer() {
+ mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
+ mBackgroundPanelOrganizer.removeBackgroundPanelLayer();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mBackgroundPanelOrganizer.mIsShowing).isFalse();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index e37e154..20184bf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -54,6 +54,8 @@
@Mock
DisplayController mMockDisplayController;
@Mock
+ OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
+ @Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
OneHandedTouchHandler mMockTouchHandler;
@@ -75,6 +77,7 @@
OneHandedController oneHandedController = new OneHandedController(
mContext,
mMockDisplayController,
+ mMockBackgroundOrganizer,
mMockDisplayAreaOrganizer,
mMockTouchHandler,
mMockTutorialHandler,
@@ -94,7 +97,7 @@
mContext);
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
mContext, mMockDisplayController, animationController, mMockTutorialHandler,
- Runnable::run);
+ Runnable::run, mMockBackgroundOrganizer);
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 5d742b3..3d9fad9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -80,6 +80,8 @@
SurfaceControl mMockLeash;
@Mock
WindowContainerTransaction mMockWindowContainerTransaction;
+ @Mock
+ OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
Handler mSpyUpdateHandler;
Handler.Callback mUpdateCallback = (msg) -> false;
@@ -103,7 +105,7 @@
mMockSurfaceTransactionHelper);
when(mMockAnimator.isRunning()).thenReturn(true);
when(mMockAnimator.setDuration(anyInt())).thenReturn(mFakeAnimator);
- when(mMockAnimator.setOneHandedAnimationCallbacks(any())).thenReturn(mFakeAnimator);
+ when(mMockAnimator.addOneHandedAnimationCallback(any())).thenReturn(mFakeAnimator);
when(mMockAnimator.setTransitionDirection(anyInt())).thenReturn(mFakeAnimator);
when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
@@ -112,7 +114,7 @@
mMockDisplayController,
mMockAnimationController,
mTutorialHandler,
- Runnable::run);
+ Runnable::run, mMockBackgroundOrganizer);
mSpyUpdateHandler = spy(new Handler(OneHandedThread.get().getLooper(), mUpdateCallback));
mDisplayAreaOrganizer.setUpdateHandler(mSpyUpdateHandler);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index ba8c737..b187dc9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -45,6 +45,8 @@
@Mock
DisplayController mMockDisplayController;
@Mock
+ OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
+ @Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
IOverlayManager mMockOverlayManager;
@@ -59,6 +61,7 @@
mOneHandedController = new OneHandedController(
getContext(),
mMockDisplayController,
+ mMockBackgroundOrganizer,
mMockDisplayAreaOrganizer,
mTouchHandler,
mTutorialHandler,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 4efaebf..b4cfbc2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -73,7 +73,7 @@
private PipUiEventLogger mPipUiEventLogger;
@Mock
- private ShellExecutor mShellMainExecutor;
+ private ShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -98,7 +98,7 @@
mPipSnapAlgorithm = new PipSnapAlgorithm();
mPipTouchHandler = new PipTouchHandler(mContext, mPhonePipMenuController,
mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer,
- mFloatingContentCoordinator, mPipUiEventLogger, mShellMainExecutor);
+ mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
mPipTouchHandler.setPipMotionHelper(mMotionHelper);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 07115c2..c9537af 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -15,14 +15,13 @@
*/
package unittest.src.com.android.wm.shell.startingsurface;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -72,6 +71,7 @@
static final class TestStartingSurfaceDrawer extends StartingSurfaceDrawer{
int mAddWindowForTask = 0;
+ int mViewThemeResId;
TestStartingSurfaceDrawer(Context context, ShellExecutor executor) {
super(context, executor);
@@ -82,6 +82,7 @@
View view, WindowManager wm, WindowManager.LayoutParams params) {
// listen for addView
mAddWindowForTask = taskId;
+ mViewThemeResId = view.getContext().getThemeResId();
}
@Override
@@ -121,7 +122,7 @@
final int taskId = 1;
final Handler mainLoop = new Handler(Looper.getMainLooper());
final StartingWindowInfo windowInfo =
- createWindowInfo(taskId, WINDOWING_MODE_FULLSCREEN);
+ createWindowInfo(taskId, android.R.style.Theme);
mStartingSurfaceDrawer.addStartingWindow(windowInfo, mBinder);
waitHandlerIdle(mainLoop);
verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any());
@@ -133,12 +134,24 @@
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
}
- private StartingWindowInfo createWindowInfo(int taskId, int windowingMode) {
+ @Test
+ public void testFallbackDefaultTheme() {
+ final int taskId = 1;
+ final Handler mainLoop = new Handler(Looper.getMainLooper());
+ final StartingWindowInfo windowInfo =
+ createWindowInfo(taskId, 0);
+ mStartingSurfaceDrawer.addStartingWindow(windowInfo, mBinder);
+ waitHandlerIdle(mainLoop);
+ verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any());
+ assertNotEquals(mStartingSurfaceDrawer.mViewThemeResId, 0);
+ }
+
+ private StartingWindowInfo createWindowInfo(int taskId, int themeResId) {
StartingWindowInfo windowInfo = new StartingWindowInfo();
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
info.packageName = "test";
- info.theme = android.R.style.Theme;
+ info.theme = themeResId;
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.topActivityInfo = info;
taskInfo.taskId = taskId;
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 8330363..b54f7d8 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -100,6 +100,11 @@
"libz",
],
},
+ linux_glibc: {
+ srcs: [
+ "CursorWindow.cpp",
+ ],
+ },
windows: {
enabled: true,
},
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index cb56a51..011a0de 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -385,7 +385,7 @@
return {};
}
- auto overlay_path = loaded_idmap->OverlayApkPath();
+ auto overlay_path = std::string(loaded_idmap->OverlayApkPath());
auto assets = ZipAssetsProvider::Create(overlay_path);
return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY,
nullptr /* override_asset */, std::move(idmap_asset),
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index bec80a7..3f06000 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -157,7 +157,8 @@
// The target package must precede the overlay package in the apk assets paths in order
// to take effect.
const auto& loaded_idmap = apk_assets->GetLoadedIdmap();
- auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath());
+ auto target_package_iter = apk_assets_package_ids.find(
+ std::string(loaded_idmap->TargetApkPath()));
if (target_package_iter == apk_assets_package_ids.end()) {
LOG(INFO) << "failed to find target package for overlay "
<< loaded_idmap->OverlayApkPath();
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index a613095..73e040c 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -36,13 +36,51 @@
namespace android {
-uint32_t round_to_4_bytes(uint32_t size) {
- return size + (4U - (size % 4U)) % 4U;
-}
+// See frameworks/base/cmds/idmap2/include/idmap2/Idmap.h for full idmap file format specification.
+struct Idmap_header {
+ // Always 0x504D4449 ('IDMP')
+ uint32_t magic;
+ uint32_t version;
-size_t Idmap_header::Size() const {
- return sizeof(Idmap_header) + sizeof(uint8_t) * round_to_4_bytes(dtohl(debug_info_size));
-}
+ uint32_t target_crc32;
+ uint32_t overlay_crc32;
+
+ uint32_t fulfilled_policies;
+ uint32_t enforce_overlayable;
+
+ // overlay_path, target_path, and other string values encoded in the idmap header and read and
+ // stored in separate structures. This allows the idmap header data to be casted to this struct
+ // without having to read/store each header entry separately.
+};
+
+struct Idmap_data_header {
+ uint8_t target_package_id;
+ uint8_t overlay_package_id;
+
+ // Padding to ensure 4 byte alignment for target_entry_count
+ uint16_t p0;
+
+ uint32_t target_entry_count;
+ uint32_t target_inline_entry_count;
+ uint32_t overlay_entry_count;
+
+ uint32_t string_pool_index_offset;
+};
+
+struct Idmap_target_entry {
+ uint32_t target_id;
+ uint32_t overlay_id;
+};
+
+struct Idmap_target_entry_inline {
+ uint32_t target_id;
+ Res_value value;
+};
+
+struct Idmap_overlay_entry {
+ uint32_t overlay_id;
+ uint32_t target_id;
+};
OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
: data_header_(loaded_idmap->data_header_),
@@ -155,140 +193,133 @@
return {};
}
-static bool is_word_aligned(const void* data) {
- return (reinterpret_cast<uintptr_t>(data) & 0x03U) == 0U;
+namespace {
+template <typename T>
+const T* ReadType(const uint8_t** in_out_data_ptr, size_t* in_out_size, const std::string& label,
+ size_t count = 1) {
+ if (!util::IsFourByteAligned(*in_out_data_ptr)) {
+ LOG(ERROR) << "Idmap " << label << " is not word aligned.";
+ return {};
+ }
+ if ((*in_out_size / sizeof(T)) < count) {
+ LOG(ERROR) << "Idmap too small for the number of " << label << " entries ("
+ << count << ").";
+ return nullptr;
+ }
+ auto data_ptr = *in_out_data_ptr;
+ const size_t read_size = sizeof(T) * count;
+ *in_out_data_ptr += read_size;
+ *in_out_size -= read_size;
+ return reinterpret_cast<const T*>(data_ptr);
}
-static bool IsValidIdmapHeader(const StringPiece& data) {
- if (!is_word_aligned(data.data())) {
- LOG(ERROR) << "Idmap header is not word aligned.";
- return false;
+std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size_t* in_out_size,
+ const std::string& label) {
+ const auto* len = ReadType<uint32_t>(in_out_data_ptr, in_out_size, label + " length");
+ if (len == nullptr) {
+ return {};
}
-
- if (data.size() < sizeof(Idmap_header)) {
- LOG(ERROR) << "Idmap header is too small.";
- return false;
+ const auto* data = ReadType<char>(in_out_data_ptr, in_out_size, label, *len);
+ if (data == nullptr) {
+ return {};
}
-
- auto header = reinterpret_cast<const Idmap_header*>(data.data());
- if (dtohl(header->magic) != kIdmapMagic) {
- LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
- dtohl(header->magic), kIdmapMagic);
- return false;
+ // Strings are padded to the next 4 byte boundary.
+ const uint32_t padding_size = (4U - ((size_t)*in_out_data_ptr & 0x3U)) % 4U;
+ for (uint32_t i = 0; i < padding_size; i++) {
+ if (**in_out_data_ptr != 0) {
+ LOG(ERROR) << " Idmap padding of " << label << " is non-zero.";
+ return {};
+ }
+ *in_out_data_ptr += sizeof(uint8_t);
+ *in_out_size -= sizeof(uint8_t);
}
-
- if (dtohl(header->version) != kIdmapCurrentVersion) {
- // We are strict about versions because files with this format are auto-generated and don't need
- // backwards compatibility.
- LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
- dtohl(header->version), kIdmapCurrentVersion);
- return false;
- }
-
- return true;
+ return std::string_view(data, *len);
+}
}
LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
- const time_t last_mod_time,
const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
const Idmap_overlay_entry* overlay_entries,
- ResStringPool* string_pool)
+ std::unique_ptr<ResStringPool>&& string_pool,
+ std::string_view overlay_apk_path,
+ std::string_view target_apk_path)
: header_(header),
data_header_(data_header),
target_entries_(target_entries),
target_inline_entries_(target_inline_entries),
overlay_entries_(overlay_entries),
- string_pool_(string_pool),
+ string_pool_(std::move(string_pool)),
idmap_path_(std::move(idmap_path)),
- idmap_last_mod_time_(last_mod_time) {
-
- size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
- arraysize(header_->overlay_path));
- overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
-
- length = strnlen(reinterpret_cast<const char*>(header_->target_path),
- arraysize(header_->target_path));
- target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
-}
+ overlay_apk_path_(overlay_apk_path),
+ target_apk_path_(target_apk_path),
+ idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
const StringPiece& idmap_data) {
ATRACE_CALL();
- if (!IsValidIdmapHeader(idmap_data)) {
+ size_t data_size = idmap_data.size();
+ auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
+
+ // Parse the idmap header
+ auto header = ReadType<Idmap_header>(&data_ptr, &data_size, "header");
+ if (header == nullptr) {
+ return {};
+ }
+ if (dtohl(header->magic) != kIdmapMagic) {
+ LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
+ dtohl(header->magic), kIdmapMagic);
+ return {};
+ }
+ if (dtohl(header->version) != kIdmapCurrentVersion) {
+ // We are strict about versions because files with this format are generated at runtime and
+ // don't need backwards compatibility.
+ LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
+ dtohl(header->version), kIdmapCurrentVersion);
+ return {};
+ }
+ std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path");
+ if (!overlay_path) {
+ return {};
+ }
+ std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path");
+ if (!target_path) {
+ return {};
+ }
+ if (!ReadString(&data_ptr, &data_size, "target name") ||
+ !ReadString(&data_ptr, &data_size, "debug info")) {
return {};
}
- auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
- const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + header->Size();
- size_t data_size = idmap_data.size() - header->Size();
-
- // Currently idmap2 can only generate one data block.
- auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
- data_ptr += sizeof(*data_header);
- data_size -= sizeof(*data_header);
-
- // Make sure there is enough space for the target entries declared in the header
- const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
- if (data_size / sizeof(Idmap_target_entry) <
- static_cast<size_t>(dtohl(data_header->target_entry_count))) {
- LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
- (int)dtohl(data_header->target_entry_count));
+ // Parse the idmap data blocks. Currently idmap2 can only generate one data block.
+ auto data_header = ReadType<Idmap_data_header>(&data_ptr, &data_size, "data header");
+ if (data_header == nullptr) {
return {};
}
-
- // Advance the data pointer past the target entries.
- const size_t target_entry_size_bytes =
- (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
- data_ptr += target_entry_size_bytes;
- data_size -= target_entry_size_bytes;
-
- // Make sure there is enough space for the target entries declared in the header.
- const auto target_inline_entries = reinterpret_cast<const Idmap_target_entry_inline*>(data_ptr);
- if (data_size / sizeof(Idmap_target_entry_inline) <
- static_cast<size_t>(dtohl(data_header->target_inline_entry_count))) {
- LOG(ERROR) << StringPrintf("Idmap too small for the number of target inline entries (%d)",
- (int)dtohl(data_header->target_inline_entry_count));
+ auto target_entries = ReadType<Idmap_target_entry>(&data_ptr, &data_size, "target",
+ dtohl(data_header->target_entry_count));
+ if (target_entries == nullptr) {
return {};
}
-
- // Advance the data pointer past the target entries.
- const size_t target_inline_entry_size_bytes =
- (dtohl(data_header->target_inline_entry_count) * sizeof(Idmap_target_entry_inline));
- data_ptr += target_inline_entry_size_bytes;
- data_size -= target_inline_entry_size_bytes;
-
- // Make sure there is enough space for the overlay entries declared in the header.
- const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
- if (data_size / sizeof(Idmap_overlay_entry) <
- static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
- LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
- (int)dtohl(data_header->overlay_entry_count));
+ auto target_inline_entries = ReadType<Idmap_target_entry_inline>(
+ &data_ptr, &data_size, "target inline", dtohl(data_header->target_inline_entry_count));
+ if (target_inline_entries == nullptr) {
return {};
}
-
- // Advance the data pointer past the overlay entries.
- const size_t overlay_entry_size_bytes =
- (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
- data_ptr += overlay_entry_size_bytes;
- data_size -= overlay_entry_size_bytes;
-
- // Read the idmap string pool that holds the value of inline string entries.
- uint32_t string_pool_size = dtohl(*reinterpret_cast<const uint32_t*>(data_ptr));
- data_ptr += sizeof(uint32_t);
- data_size -= sizeof(uint32_t);
-
- if (data_size < string_pool_size) {
- LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
- (int)string_pool_size);
+ auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline",
+ dtohl(data_header->overlay_entry_count));
+ if (overlay_entries == nullptr) {
return {};
}
-
+ std::optional<std::string_view> string_pool = ReadString(&data_ptr, &data_size, "string pool");
+ if (!string_pool) {
+ return {};
+ }
auto idmap_string_pool = util::make_unique<ResStringPool>();
- if (string_pool_size > 0) {
- status_t err = idmap_string_pool->setTo(data_ptr, string_pool_size);
+ if (!string_pool->empty()) {
+ const status_t err = idmap_string_pool->setTo(string_pool->data(), string_pool->size());
if (err != NO_ERROR) {
LOG(ERROR) << "idmap string pool corrupt.";
return {};
@@ -296,12 +327,10 @@
}
// Can't use make_unique because LoadedIdmap constructor is private.
- auto loaded_idmap = std::unique_ptr<LoadedIdmap>(
- new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header,
- data_header, target_entries, target_inline_entries, overlay_entries,
- idmap_string_pool.release()));
-
- return std::move(loaded_idmap);
+ return std::unique_ptr<LoadedIdmap>(
+ new LoadedIdmap(idmap_path.to_string(), header, data_header, target_entries,
+ target_inline_entries, overlay_entries, std::move(idmap_string_pool),
+ *target_path, *overlay_path));
}
bool LoadedIdmap::IsUpToDate() const {
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index fdab03b..fd9a8d13 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -31,6 +31,11 @@
class LoadedIdmap;
class IdmapResMap;
+struct Idmap_header;
+struct Idmap_data_header;
+struct Idmap_target_entry;
+struct Idmap_target_entry_inline;
+struct Idmap_overlay_entry;
// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
// table and additionally allows for loading strings from the idmap string pool. The idmap string
@@ -148,29 +153,29 @@
const StringPiece& idmap_data);
// Returns the path to the IDMAP.
- inline const std::string& IdmapPath() const {
+ std::string_view IdmapPath() const {
return idmap_path_;
}
// Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
- inline const std::string& OverlayApkPath() const {
+ std::string_view OverlayApkPath() const {
return overlay_apk_path_;
}
// Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
- inline const std::string& TargetApkPath() const {
+ std::string_view TargetApkPath() const {
return target_apk_path_;
}
// Returns a mapping from target resource ids to overlay values.
- inline const IdmapResMap GetTargetResourcesMap(
+ const IdmapResMap GetTargetResourcesMap(
uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
target_assigned_package_id, overlay_ref_table);
}
// Returns a dynamic reference table for a loaded overlay package.
- inline const OverlayDynamicRefTable GetOverlayDynamicRefTable(
+ const OverlayDynamicRefTable GetOverlayDynamicRefTable(
uint8_t target_assigned_package_id) const {
return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id);
}
@@ -190,22 +195,23 @@
const Idmap_overlay_entry* overlay_entries_;
const std::unique_ptr<ResStringPool> string_pool_;
- const std::string idmap_path_;
- std::string overlay_apk_path_;
- std::string target_apk_path_;
- const time_t idmap_last_mod_time_;
+ std::string idmap_path_;
+ std::string_view overlay_apk_path_;
+ std::string_view target_apk_path_;
+ time_t idmap_last_mod_time_;
private:
DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
explicit LoadedIdmap(std::string&& idmap_path,
- time_t last_mod_time,
const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
const Idmap_overlay_entry* overlay_entries,
- ResStringPool* string_pool);
+ std::unique_ptr<ResStringPool>&& string_pool,
+ std::string_view overlay_apk_path,
+ std::string_view target_apk_path);
friend OverlayStringPool;
};
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index fb5f864..bfd564c 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -44,7 +44,7 @@
namespace android {
constexpr const static uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const static uint32_t kIdmapCurrentVersion = 0x00000005u;
+constexpr const static uint32_t kIdmapCurrentVersion = 0x00000007u;
/**
* In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
@@ -1700,56 +1700,6 @@
return first;
}
-struct Idmap_header {
- // Always 0x504D4449 ('IDMP')
- uint32_t magic;
-
- uint32_t version;
-
- uint32_t target_crc32;
- uint32_t overlay_crc32;
-
- uint32_t fulfilled_policies;
- uint32_t enforce_overlayable;
-
- uint8_t target_path[256];
- uint8_t overlay_path[256];
-
- uint32_t debug_info_size;
- uint8_t debug_info[0];
-
- size_t Size() const;
-};
-
-struct Idmap_data_header {
- uint8_t target_package_id;
- uint8_t overlay_package_id;
-
- // Padding to ensure 4 byte alignment for target_entry_count
- uint16_t p0;
-
- uint32_t target_entry_count;
- uint32_t target_inline_entry_count;
- uint32_t overlay_entry_count;
-
- uint32_t string_pool_index_offset;
-};
-
-struct Idmap_target_entry {
- uint32_t target_id;
- uint32_t overlay_id;
-};
-
-struct Idmap_target_entry_inline {
- uint32_t target_id;
- Res_value value;
-};
-
-struct Idmap_overlay_entry {
- uint32_t overlay_id;
- uint32_t target_id;
-};
-
class AssetManager2;
/**
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index aceeecc..c59b5b6 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -128,10 +128,14 @@
std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
template <typename T>
-bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
+inline bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
return ((size_t)data.unsafe_ptr() & 0x3U) == 0;
}
+inline bool IsFourByteAligned(const void* data) {
+ return ((size_t)data & 0x3U) == 0;
+}
+
} // namespace util
} // namespace android
diff --git a/libs/androidfw/tests/data/app/app.apk b/libs/androidfw/tests/data/app/app.apk
index c8ad86d..6703695 100644
--- a/libs/androidfw/tests/data/app/app.apk
+++ b/libs/androidfw/tests/data/app/app.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 3ab244e..723413c 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index c50904c..afae502 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -36,7 +36,7 @@
import android.location.Location;
import android.location.LocationRequest;
import android.location.LocationTime;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
import android.os.Bundle;
import android.os.ICancellationSignal;
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 1d3e8eb..2dc9eb4 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -49,6 +49,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.location.provider.ProviderProperties;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -1803,7 +1804,6 @@
}
try {
-
ProviderProperties properties = mService.getProviderProperties(provider);
return new LocationProvider(provider, properties);
} catch (IllegalArgumentException e) {
@@ -1931,11 +1931,35 @@
boolean supportsSpeed, boolean supportsBearing,
@ProviderProperties.PowerUsage int powerUsage,
@ProviderProperties.Accuracy int accuracy) {
- Preconditions.checkArgument(provider != null, "invalid null provider");
+ addTestProvider(provider, new ProviderProperties.Builder()
+ .setHasNetworkRequirement(requiresNetwork)
+ .setHasSatelliteRequirement(requiresSatellite)
+ .setHasCellRequirement(requiresCell)
+ .setHasMonetaryCost(hasMonetaryCost)
+ .setHasAltitudeSupport(supportsAltitude)
+ .setHasSpeedSupport(supportsSpeed)
+ .setHasBearingSupport(supportsBearing)
+ .setPowerUsage(powerUsage)
+ .setAccuracy(accuracy)
+ .build());
+ }
- ProviderProperties properties = new ProviderProperties(requiresNetwork,
- requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
- supportsBearing, powerUsage, accuracy);
+ /**
+ * Creates a test location provider and adds it to the set of active providers. This provider
+ * will replace any provider with the same name that exists prior to this call.
+ *
+ * @param provider the provider name
+ *
+ * @throws IllegalArgumentException if provider is null
+ * @throws IllegalArgumentException if properties is null
+ * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+ * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+ * allowed} for your app.
+ */
+ public void addTestProvider(@NonNull String provider, @NonNull ProviderProperties properties) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(properties != null, "invalid null properties");
+
try {
mService.addTestProvider(provider, properties, mContext.getOpPackageName(),
mContext.getFeatureId());
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index 6d2bfed..60b251e 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -17,6 +17,7 @@
package android.location;
import android.annotation.Nullable;
+import android.location.provider.ProviderProperties;
/**
* Information about the properties of a location provider.
@@ -200,10 +201,8 @@
}
/**
- * Returns the power requirement for this provider.
- *
- * @return the power requirement for this provider, as one of the
- * constants ProviderProperties.POWER_USAGE_*.
+ * Returns the power requirement for this provider, one of the ProviderProperties.POWER_USAGE_*
+ * constants.
*/
public int getPowerRequirement() {
if (mProperties == null) {
@@ -214,11 +213,8 @@
}
/**
- * Returns a constant describing horizontal accuracy of this provider.
- * If the provider returns finer grain or exact location,
- * {@link ProviderProperties#ACCURACY_FINE} is returned, otherwise if the
- * location is only approximate then {@link ProviderProperties#ACCURACY_COARSE}
- * is returned.
+ * Returns the rough accuracy of this provider, one of the ProviderProperties.ACCURACY_*
+ * constants.
*/
public int getAccuracy() {
if (mProperties == null) {
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/android/location/provider/ILocationProvider.aidl
similarity index 66%
rename from location/java/com/android/internal/location/ILocationProvider.aidl
rename to location/java/android/location/provider/ILocationProvider.aidl
index dac08ff..f9995d5 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/android/location/provider/ILocationProvider.aidl
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package com.android.internal.location;
+package android.location.provider;
import android.os.Bundle;
-import android.os.WorkSource;
-import com.android.internal.location.ILocationProviderManager;
-import com.android.internal.location.ProviderRequest;
+import android.location.provider.ILocationProviderManager;
+import android.location.provider.ProviderRequest;
/**
* Binder interface for services that implement location providers. Do not implement this directly,
@@ -29,14 +28,8 @@
*/
interface ILocationProvider {
- @UnsupportedAppUsage(maxTargetSdk = 30, publicAlternatives = "{@code Do not use}")
oneway void setLocationProviderManager(in ILocationProviderManager manager);
-
- @UnsupportedAppUsage(maxTargetSdk = 30, publicAlternatives = "{@code Do not use}")
- oneway void setRequest(in ProviderRequest request, in WorkSource ws);
-
+ oneway void setRequest(in ProviderRequest request);
oneway void flush();
-
- @UnsupportedAppUsage(maxTargetSdk = 30, publicAlternatives = "{@code Do not use}")
oneway void sendExtraCommand(String command, in Bundle extras);
}
diff --git a/location/java/com/android/internal/location/ILocationProviderManager.aidl b/location/java/android/location/provider/ILocationProviderManager.aidl
similarity index 92%
rename from location/java/com/android/internal/location/ILocationProviderManager.aidl
rename to location/java/android/location/provider/ILocationProviderManager.aidl
index a5b22b2..e3f51d9 100644
--- a/location/java/com/android/internal/location/ILocationProviderManager.aidl
+++ b/location/java/android/location/provider/ILocationProviderManager.aidl
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.internal.location;
+package android.location.provider;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
/**
* Binder interface for manager of all location providers.
diff --git a/location/java/android/location/provider/LocationProviderBase.java b/location/java/android/location/provider/LocationProviderBase.java
new file mode 100644
index 0000000..1306ea2
--- /dev/null
+++ b/location/java/android/location/provider/LocationProviderBase.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2010 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.location.provider;
+
+import static android.location.Location.EXTRA_NO_GPS_LOCATION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Location;
+import android.location.LocationResult;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Base class for location providers outside the system server.
+ *
+ * Location providers should be wrapped in a non-exported service which returns the result of
+ * {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The
+ * service should not be exported so that components other than the system server cannot bind to it.
+ * Alternatively, the service may be guarded by a permission that only system server can obtain. The
+ * service may specify metadata on its capabilities:
+ *
+ * <ul>
+ * <li>
+ * "serviceVersion": An integer version code to help tie break if multiple services are
+ * capable of implementing the same location provider. All else equal, the service with the
+ * highest version code will be chosen. Assumed to be 0 if not specified.
+ * </li>
+ * <li>
+ * "serviceIsMultiuser": A boolean property, indicating if the service wishes to take
+ * responsibility for handling changes to the current user on the device. If true, the
+ * service will always be bound from the system user. If false, the service will always be
+ * bound from the current user. If the current user changes, the old binding will be
+ * released, and a new binding established under the new user. Assumed to be false if not
+ * specified.
+ * </li>
+ * </ul>
+ *
+ * <p>The service should have an intent filter in place for the location provider it wishes to
+ * implements. Defaults for some providers are specified as constants in this class.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class LocationProviderBase {
+
+ /**
+ * Callback to be invoked when a flush operation is complete and all flushed locations have been
+ * reported.
+ */
+ public interface OnFlushCompleteCallback {
+
+ /**
+ * Should be invoked once the flush is complete.
+ */
+ void onFlushComplete();
+ }
+
+ /**
+ * The action the wrapping service should have in its intent filter to implement the
+ * {@link android.location.LocationManager#NETWORK_PROVIDER}.
+ */
+ @SuppressLint("ActionValue")
+ public static final String ACTION_NETWORK_PROVIDER =
+ "com.android.location.service.v3.NetworkLocationProvider";
+
+ /**
+ * The action the wrapping service should have in its intent filter to implement the
+ * {@link android.location.LocationManager#FUSED_PROVIDER}.
+ */
+ @SuppressLint("ActionValue")
+ public static final String ACTION_FUSED_PROVIDER =
+ "com.android.location.service.FusedLocationProvider";
+
+ private final String mTag;
+ private final @Nullable String mPackageName;
+ private final @Nullable String mAttributionTag;
+ private final IBinder mBinder;
+
+ // write locked on mBinder, read lock is optional depending on atomicity requirements
+ private @Nullable volatile ILocationProviderManager mManager;
+ private volatile ProviderProperties mProperties;
+ private volatile boolean mAllowed;
+
+ public LocationProviderBase(@NonNull Context context, @NonNull String tag,
+ @NonNull ProviderProperties properties) {
+ mTag = tag;
+ mPackageName = context.getPackageName();
+ mAttributionTag = context.getAttributionTag();
+ mBinder = new Service();
+
+ mManager = null;
+ mProperties = Objects.requireNonNull(properties);
+ mAllowed = true;
+ }
+
+ /**
+ * Returns the IBinder instance that should be returned from the
+ * {@link android.app.Service#onBind(Intent)} method of the wrapping service.
+ */
+ public final @Nullable IBinder getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Sets whether this provider is currently allowed or not. Note that this is specific to the
+ * provider only, and is unrelated to global location settings. This is a hint to the location
+ * manager that this provider will be unable to fulfill incoming requests. Setting a provider
+ * as not allowed will result in the provider being disabled. Setting a provider as allowed
+ * means that the provider may be in either the enabled or disabled state.
+ *
+ * <p>Some guidelines: providers should set their own allowed/disallowed status based only on
+ * state "owned" by that provider. For instance, providers should not take into account the
+ * state of the location master setting when setting themselves allowed or disallowed, as this
+ * state is not owned by a particular provider. If a provider requires some additional user
+ * consent that is particular to the provider, this should be use to set the allowed/disallowed
+ * state. If the provider proxies to another provider, the child provider's allowed/disallowed
+ * state should be taken into account in the parent's allowed state. For most providers, it is
+ * expected that they will be always allowed.
+ */
+ public void setAllowed(boolean allowed) {
+ synchronized (mBinder) {
+ if (mAllowed == allowed) {
+ return;
+ }
+
+ mAllowed = allowed;
+ }
+
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onSetAllowed(mAllowed);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
+ }
+
+ /**
+ * Returns true if this provider is allowed. Providers start as allowed on construction.
+ */
+ public boolean isAllowed() {
+ return mAllowed;
+ }
+
+ /**
+ * Sets the provider properties that may be queried by clients. Generally speaking, providers
+ * should try to avoid changing their properties after construction.
+ */
+ public void setProperties(@NonNull ProviderProperties properties) {
+ synchronized (mBinder) {
+ mProperties = Objects.requireNonNull(properties);
+ }
+
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onSetProperties(mProperties);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
+ }
+
+ /**
+ * Returns the currently set properties of the provider.
+ */
+ public @NonNull ProviderProperties getProperties() {
+ return mProperties;
+ }
+
+ /**
+ * Reports a new location from this provider.
+ */
+ public void reportLocation(@NonNull Location location) {
+ reportLocation(LocationResult.create(location));
+ }
+
+ /**
+ * Reports a new location result from this provider.
+ *
+ * <p>May only be used from Android S onwards.
+ */
+ public void reportLocation(@NonNull LocationResult locationResult) {
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ locationResult = locationResult.map(location -> {
+ // remove deprecated extras to save on serialization costs
+ Bundle extras = location.getExtras();
+ if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
+ || extras.containsKey("coarseLocation"))) {
+ location = new Location(location);
+ extras = location.getExtras();
+ extras.remove(EXTRA_NO_GPS_LOCATION);
+ extras.remove("coarseLocation");
+ if (extras.isEmpty()) {
+ location.setExtras(null);
+ }
+ }
+ return location;
+ });
+
+ try {
+ manager.onReportLocation(locationResult);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
+ }
+
+ /**
+ * Set the current {@link ProviderRequest} for this provider. Each call to this method overrides
+ * any prior ProviderRequests. The provider should immediately attempt to provide locations (or
+ * not provide locations) according to the parameters of the provider request.
+ */
+ public abstract void onSetRequest(@NonNull ProviderRequest request);
+
+ /**
+ * Requests a flush of any pending batched locations. The callback must always be invoked once
+ * per invocation, and should be invoked after {@link #reportLocation(LocationResult)} has been
+ * invoked with any flushed locations. The callback may be invoked immediately if no locations
+ * are flushed.
+ */
+ public abstract void onFlush(@NonNull OnFlushCompleteCallback callback);
+
+ /**
+ * Implements optional custom commands.
+ */
+ public abstract void onSendExtraCommand(@NonNull String command, @Nullable Bundle extras);
+
+ private final class Service extends ILocationProvider.Stub {
+
+ Service() {}
+
+ @Override
+ public void setLocationProviderManager(ILocationProviderManager manager) {
+ synchronized (mBinder) {
+ try {
+ manager.onInitialize(mAllowed, mProperties, mPackageName, mAttributionTag);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
+ Log.w(mTag, e);
+ }
+
+ mManager = manager;
+ }
+ }
+
+ @Override
+ public void setRequest(ProviderRequest request) {
+ onSetRequest(request);
+ }
+
+ @Override
+ public void flush() {
+ onFlush(this::onFlushComplete);
+ }
+
+ private void onFlushComplete() {
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onFlushComplete();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
+ }
+
+ @Override
+ public void sendExtraCommand(String command, Bundle extras) {
+ onSendExtraCommand(command, extras);
+ }
+ }
+}
diff --git a/location/java/android/location/ProviderProperties.aidl b/location/java/android/location/provider/ProviderProperties.aidl
similarity index 94%
rename from location/java/android/location/ProviderProperties.aidl
rename to location/java/android/location/provider/ProviderProperties.aidl
index 8b1d79f..fd5a614 100644
--- a/location/java/android/location/ProviderProperties.aidl
+++ b/location/java/android/location/provider/ProviderProperties.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.location;
+package android.location.provider;
parcelable ProviderProperties;
diff --git a/location/java/android/location/ProviderProperties.java b/location/java/android/location/provider/ProviderProperties.java
similarity index 65%
rename from location/java/android/location/ProviderProperties.java
rename to location/java/android/location/provider/ProviderProperties.java
index 8fa8c98..7934012 100644
--- a/location/java/android/location/ProviderProperties.java
+++ b/location/java/android/location/provider/ProviderProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.location;
+package android.location.provider;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -78,10 +78,7 @@
private final @PowerUsage int mPowerUsage;
private final @Accuracy int mAccuracy;
- /**
- * @hide
- */
- public ProviderProperties(boolean hasNetworkRequirement, boolean hasSatelliteRequirement,
+ private ProviderProperties(boolean hasNetworkRequirement, boolean hasSatelliteRequirement,
boolean hasCellRequirement, boolean hasMonetaryCost, boolean hasAltitudeSupport,
boolean hasSpeedSupport, boolean hasBearingSupport,
@PowerUsage int powerUsage, @Accuracy int accuracy) {
@@ -92,10 +89,8 @@
mHasAltitudeSupport = hasAltitudeSupport;
mHasSpeedSupport = hasSpeedSupport;
mHasBearingSupport = hasBearingSupport;
- mPowerUsage = Preconditions.checkArgumentInRange(powerUsage, POWER_USAGE_LOW,
- POWER_USAGE_HIGH, "powerUsage");
- mAccuracy = Preconditions.checkArgumentInRange(accuracy, ACCURACY_FINE,
- ACCURACY_COARSE, "locationAccuracy");
+ mPowerUsage = powerUsage;
+ mAccuracy = accuracy;
}
/**
@@ -233,7 +228,7 @@
@Override
public String toString() {
StringBuilder b = new StringBuilder("ProviderProperties[");
- b.append("power=").append(powerToString(mPowerUsage)).append(", ");
+ b.append("powerUsage=").append(powerToString(mPowerUsage)).append(", ");
b.append("accuracy=").append(accuracyToString(mAccuracy));
if (mHasNetworkRequirement || mHasSatelliteRequirement || mHasCellRequirement) {
b.append(", requires=");
@@ -292,4 +287,128 @@
throw new AssertionError();
}
}
+
+ /**
+ * Builder for ProviderProperties.
+ */
+ public static final class Builder {
+
+ private boolean mHasNetworkRequirement;
+ private boolean mHasSatelliteRequirement;
+ private boolean mHasCellRequirement;
+ private boolean mHasMonetaryCost;
+ private boolean mHasAltitudeSupport;
+ private boolean mHasSpeedSupport;
+ private boolean mHasBearingSupport;
+ private @PowerUsage int mPowerUsage;
+ private @Accuracy int mAccuracy;
+
+ public Builder() {
+ mHasNetworkRequirement = false;
+ mHasSatelliteRequirement = false;
+ mHasCellRequirement = false;
+ mHasMonetaryCost = false;
+ mHasAltitudeSupport = false;
+ mHasSpeedSupport = false;
+ mHasBearingSupport = false;
+ mPowerUsage = POWER_USAGE_HIGH;
+ mAccuracy = ACCURACY_COARSE;
+ }
+
+ public Builder(@NonNull ProviderProperties providerProperties) {
+ mHasNetworkRequirement = providerProperties.mHasNetworkRequirement;
+ mHasSatelliteRequirement = providerProperties.mHasSatelliteRequirement;
+ mHasCellRequirement = providerProperties.mHasCellRequirement;
+ mHasMonetaryCost = providerProperties.mHasMonetaryCost;
+ mHasAltitudeSupport = providerProperties.mHasAltitudeSupport;
+ mHasSpeedSupport = providerProperties.mHasSpeedSupport;
+ mHasBearingSupport = providerProperties.mHasBearingSupport;
+ mPowerUsage = providerProperties.mPowerUsage;
+ mAccuracy = providerProperties.mAccuracy;
+ }
+
+ /**
+ * Sets whether a provider requires network access. False by default.
+ */
+ public @NonNull Builder setHasNetworkRequirement(boolean requiresNetwork) {
+ mHasNetworkRequirement = requiresNetwork;
+ return this;
+ }
+
+ /**
+ * Sets whether a provider requires satellite access. False by default.
+ */
+ public @NonNull Builder setHasSatelliteRequirement(boolean requiresSatellite) {
+ mHasSatelliteRequirement = requiresSatellite;
+ return this;
+ }
+
+ /**
+ * Sets whether a provider requires cell tower access. False by default.
+ */
+ public @NonNull Builder setHasCellRequirement(boolean requiresCell) {
+ mHasCellRequirement = requiresCell;
+ return this;
+ }
+
+ /**
+ * Sets whether a provider has a monetary cost. False by default.
+ */
+ public @NonNull Builder setHasMonetaryCost(boolean monetaryCost) {
+ mHasMonetaryCost = monetaryCost;
+ return this;
+ }
+
+ /**
+ * Sets whether a provider can provide altitude information. False by default.
+ */
+ public @NonNull Builder setHasAltitudeSupport(boolean supportsAltitude) {
+ mHasAltitudeSupport = supportsAltitude;
+ return this;
+ }
+
+ /**
+ * Sets whether a provider can provide speed information. False by default.
+ */
+ public @NonNull Builder setHasSpeedSupport(boolean supportsSpeed) {
+ mHasSpeedSupport = supportsSpeed;
+ return this;
+ }
+
+ /**
+ * Sets whether a provider can provide bearing information. False by default.
+ */
+ public @NonNull Builder setHasBearingSupport(boolean supportsBearing) {
+ mHasBearingSupport = supportsBearing;
+ return this;
+ }
+
+ /**
+ * Sets a very rough bucket of provider power usage. {@link #POWER_USAGE_HIGH} by default.
+ */
+ public @NonNull Builder setPowerUsage(@PowerUsage int powerUsage) {
+ mPowerUsage = Preconditions.checkArgumentInRange(powerUsage, POWER_USAGE_LOW,
+ POWER_USAGE_HIGH, "powerUsage");
+ return this;
+ }
+
+ /**
+ * Sets a very rough bucket of provider location accuracy. {@link #ACCURACY_COARSE} by
+ * default.
+ */
+ public @NonNull Builder setAccuracy(@Accuracy int accuracy) {
+ mAccuracy = Preconditions.checkArgumentInRange(accuracy, ACCURACY_FINE,
+ ACCURACY_COARSE, "accuracy");
+ return this;
+ }
+
+ /**
+ * Builds a new ProviderProperties.
+ */
+ public @NonNull ProviderProperties build() {
+ return new ProviderProperties(mHasNetworkRequirement, mHasSatelliteRequirement,
+ mHasCellRequirement, mHasMonetaryCost, mHasAltitudeSupport, mHasSpeedSupport,
+ mHasBearingSupport, mPowerUsage, mAccuracy);
+ }
+ }
}
diff --git a/location/java/com/android/internal/location/ProviderRequest.aidl b/location/java/android/location/provider/ProviderRequest.aidl
similarity index 93%
rename from location/java/com/android/internal/location/ProviderRequest.aidl
rename to location/java/android/location/provider/ProviderRequest.aidl
index 4e1ea95..b98f301 100644
--- a/location/java/com/android/internal/location/ProviderRequest.aidl
+++ b/location/java/android/location/provider/ProviderRequest.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.location;
+package android.location.provider;
parcelable ProviderRequest;
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
similarity index 75%
rename from location/java/com/android/internal/location/ProviderRequest.java
rename to location/java/android/location/provider/ProviderRequest.java
index 545ea52..e543b04 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.location;
+package android.location.provider;
import static android.location.LocationRequest.QUALITY_BALANCED_POWER_ACCURACY;
import static android.location.LocationRequest.QUALITY_HIGH_ACCURACY;
@@ -22,10 +22,9 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
import android.location.LocationRequest;
import android.location.LocationRequest.Quality;
-import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.WorkSource;
@@ -33,34 +32,25 @@
import com.android.internal.util.Preconditions;
-import java.util.Collections;
-import java.util.List;
import java.util.Objects;
/**
* Location provider request.
* @hide
*/
+@SystemApi
public final class ProviderRequest implements Parcelable {
public static final long INTERVAL_DISABLED = Long.MAX_VALUE;
- public static final ProviderRequest EMPTY_REQUEST = new ProviderRequest(
+ public static final @NonNull ProviderRequest EMPTY_REQUEST = new ProviderRequest(
INTERVAL_DISABLED, QUALITY_BALANCED_POWER_ACCURACY, 0, false, false, new WorkSource());
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
- + "ProviderRequest}")
- public final boolean reportLocation;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
- + "ProviderRequest}")
- public final long interval;
+ private final long mIntervalMillis;
private final @Quality int mQuality;
private final long mMaxUpdateDelayMillis;
private final boolean mLowPower;
private final boolean mLocationSettingsIgnored;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
- + "ProviderRequest}")
- public final List<LocationRequest> locationRequests;
private final WorkSource mWorkSource;
private ProviderRequest(
@@ -70,22 +60,11 @@
boolean lowPower,
boolean locationSettingsIgnored,
@NonNull WorkSource workSource) {
- reportLocation = intervalMillis != INTERVAL_DISABLED;
- interval = intervalMillis;
+ mIntervalMillis = intervalMillis;
mQuality = quality;
mMaxUpdateDelayMillis = maxUpdateDelayMillis;
mLowPower = lowPower;
mLocationSettingsIgnored = locationSettingsIgnored;
- if (intervalMillis != INTERVAL_DISABLED) {
- locationRequests = Collections.singletonList(new LocationRequest.Builder(intervalMillis)
- .setQuality(quality)
- .setLowPower(lowPower)
- .setLocationSettingsIgnored(locationSettingsIgnored)
- .setWorkSource(workSource)
- .build());
- } else {
- locationRequests = Collections.emptyList();
- }
mWorkSource = Objects.requireNonNull(workSource);
}
@@ -94,7 +73,7 @@
* request is inactive and does not require any locations to be reported.
*/
public boolean isActive() {
- return interval != INTERVAL_DISABLED;
+ return mIntervalMillis != INTERVAL_DISABLED;
}
/**
@@ -102,7 +81,7 @@
* {@link #INTERVAL_DISABLED} for an inactive request.
*/
public @IntRange(from = 0) long getIntervalMillis() {
- return interval;
+ return mIntervalMillis;
}
/**
@@ -148,29 +127,28 @@
return mWorkSource;
}
- public static final Parcelable.Creator<ProviderRequest> CREATOR =
- new Parcelable.Creator<ProviderRequest>() {
- @Override
- public ProviderRequest createFromParcel(Parcel in) {
- long intervalMillis = in.readLong();
- if (intervalMillis == INTERVAL_DISABLED) {
- return EMPTY_REQUEST;
- } else {
- return new ProviderRequest(
- intervalMillis,
- /* quality= */ in.readInt(),
- /* maxUpdateDelayMillis= */ in.readLong(),
- /* lowPower= */ in.readBoolean(),
- /* locationSettingsIgnored= */ in.readBoolean(),
- /* workSource= */ in.readTypedObject(WorkSource.CREATOR));
- }
- }
+ public static final @NonNull Creator<ProviderRequest> CREATOR = new Creator<ProviderRequest>() {
+ @Override
+ public ProviderRequest createFromParcel(Parcel in) {
+ long intervalMillis = in.readLong();
+ if (intervalMillis == INTERVAL_DISABLED) {
+ return EMPTY_REQUEST;
+ } else {
+ return new ProviderRequest(
+ intervalMillis,
+ /* quality= */ in.readInt(),
+ /* maxUpdateDelayMillis= */ in.readLong(),
+ /* lowPower= */ in.readBoolean(),
+ /* locationSettingsIgnored= */ in.readBoolean(),
+ /* workSource= */ in.readTypedObject(WorkSource.CREATOR));
+ }
+ }
- @Override
- public ProviderRequest[] newArray(int size) {
- return new ProviderRequest[size];
- }
- };
+ @Override
+ public ProviderRequest[] newArray(int size) {
+ return new ProviderRequest[size];
+ }
+ };
@Override
public int describeContents() {
@@ -178,9 +156,9 @@
}
@Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeLong(interval);
- if (interval != INTERVAL_DISABLED) {
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeLong(mIntervalMillis);
+ if (mIntervalMillis != INTERVAL_DISABLED) {
parcel.writeInt(mQuality);
parcel.writeLong(mMaxUpdateDelayMillis);
parcel.writeBoolean(mLowPower);
@@ -199,10 +177,10 @@
}
ProviderRequest that = (ProviderRequest) o;
- if (interval == INTERVAL_DISABLED) {
- return that.interval == INTERVAL_DISABLED;
+ if (mIntervalMillis == INTERVAL_DISABLED) {
+ return that.mIntervalMillis == INTERVAL_DISABLED;
} else {
- return interval == that.interval
+ return mIntervalMillis == that.mIntervalMillis
&& mQuality == that.mQuality
&& mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
&& mLowPower == that.mLowPower
@@ -213,16 +191,16 @@
@Override
public int hashCode() {
- return Objects.hash(interval, mQuality, mWorkSource);
+ return Objects.hash(mIntervalMillis, mQuality, mWorkSource);
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("ProviderRequest[");
- if (interval != INTERVAL_DISABLED) {
+ if (mIntervalMillis != INTERVAL_DISABLED) {
s.append("@");
- TimeUtils.formatDuration(interval, s);
+ TimeUtils.formatDuration(mIntervalMillis, s);
if (mQuality != QUALITY_BALANCED_POWER_ACCURACY) {
if (mQuality == QUALITY_HIGH_ACCURACY) {
s.append(", HIGH_ACCURACY");
@@ -230,7 +208,7 @@
s.append(", LOW_POWER");
}
}
- if (mMaxUpdateDelayMillis / 2 > interval) {
+ if (mMaxUpdateDelayMillis / 2 > mIntervalMillis) {
s.append(", maxUpdateDelay=");
TimeUtils.formatDuration(mMaxUpdateDelayMillis, s);
}
@@ -253,7 +231,7 @@
/**
* A Builder for {@link ProviderRequest}s.
*/
- public static class Builder {
+ public static final class Builder {
private long mIntervalMillis = INTERVAL_DISABLED;
private int mQuality = QUALITY_BALANCED_POWER_ACCURACY;
private long mMaxUpdateDelayMillis = 0;
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 4e13487..338d7cc 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -6,33 +6,33 @@
method @Deprecated public android.os.IBinder getBinder();
}
- public abstract class LocationProviderBase {
+ @Deprecated public abstract class LocationProviderBase {
ctor @Deprecated public LocationProviderBase(String, com.android.location.provider.ProviderPropertiesUnbundled);
- ctor @RequiresApi(android.os.Build.VERSION_CODES.R) public LocationProviderBase(android.content.Context, String, com.android.location.provider.ProviderPropertiesUnbundled);
- method public android.os.IBinder getBinder();
- method @RequiresApi(android.os.Build.VERSION_CODES.R) public boolean isAllowed();
+ ctor @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.R) public LocationProviderBase(android.content.Context, String, com.android.location.provider.ProviderPropertiesUnbundled);
+ method @Deprecated public android.os.IBinder getBinder();
+ method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.R) public boolean isAllowed();
method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public boolean isEnabled();
method @Deprecated protected void onDisable();
method @Deprecated protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
method @Deprecated protected void onEnable();
- method protected void onFlush(com.android.location.provider.LocationProviderBase.OnFlushCompleteCallback);
+ method @Deprecated protected void onFlush(com.android.location.provider.LocationProviderBase.OnFlushCompleteCallback);
method @Deprecated protected int onGetStatus(android.os.Bundle);
method @Deprecated protected long onGetStatusUpdateTime();
- method protected void onInit();
- method protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle);
- method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
- method public void reportLocation(android.location.Location);
- method public void reportLocation(android.location.LocationResult);
+ method @Deprecated protected void onInit();
+ method @Deprecated protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle);
+ method @Deprecated protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
+ method @Deprecated public void reportLocation(android.location.Location);
+ method @Deprecated public void reportLocation(android.location.LocationResult);
method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setAdditionalProviderPackages(java.util.List<java.lang.String>);
- method @RequiresApi(android.os.Build.VERSION_CODES.R) public void setAllowed(boolean);
+ method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.R) public void setAllowed(boolean);
method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setEnabled(boolean);
- method @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setProperties(com.android.location.provider.ProviderPropertiesUnbundled);
+ method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setProperties(com.android.location.provider.ProviderPropertiesUnbundled);
field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
- field public static final String FUSED_PROVIDER = "fused";
+ field @Deprecated public static final String FUSED_PROVIDER = "fused";
}
- protected static interface LocationProviderBase.OnFlushCompleteCallback {
- method public void onFlushComplete();
+ @Deprecated protected static interface LocationProviderBase.OnFlushCompleteCallback {
+ method @Deprecated public void onFlushComplete();
}
@Deprecated public final class LocationRequestUnbundled {
diff --git a/location/lib/api/system-current.txt b/location/lib/api/system-current.txt
index d802177..7046abd 100644
--- a/location/lib/api/system-current.txt
+++ b/location/lib/api/system-current.txt
@@ -1 +1,62 @@
// Signature format: 2.0
+package com.android.location.provider {
+
+ @Deprecated public final class FusedLocationHardware {
+ method @Deprecated public void flushBatchedLocations();
+ method @Deprecated public int getSupportedBatchSize();
+ method @Deprecated public int getVersion();
+ method @Deprecated public void injectDeviceContext(int);
+ method @Deprecated public void injectDiagnosticData(String);
+ method @Deprecated public void registerSink(com.android.location.provider.FusedLocationHardwareSink, android.os.Looper);
+ method @Deprecated public void requestBatchOfLocations(int);
+ method @Deprecated public void startBatching(int, com.android.location.provider.GmsFusedBatchOptions);
+ method @Deprecated public void stopBatching(int);
+ method @Deprecated public boolean supportsDeviceContextInjection();
+ method @Deprecated public boolean supportsDiagnosticDataInjection();
+ method @Deprecated public void unregisterSink(com.android.location.provider.FusedLocationHardwareSink);
+ method @Deprecated public void updateBatchingOptions(int, com.android.location.provider.GmsFusedBatchOptions);
+ }
+
+ @Deprecated public class FusedLocationHardwareSink {
+ ctor @Deprecated public FusedLocationHardwareSink();
+ method @Deprecated public void onCapabilities(int);
+ method @Deprecated public void onDiagnosticDataAvailable(String);
+ method @Deprecated public void onLocationAvailable(android.location.Location[]);
+ method @Deprecated public void onStatusChanged(int);
+ }
+
+ @Deprecated public class GmsFusedBatchOptions {
+ ctor @Deprecated public GmsFusedBatchOptions();
+ method @Deprecated public int getFlags();
+ method @Deprecated public double getMaxPowerAllocationInMW();
+ method @Deprecated public long getPeriodInNS();
+ method @Deprecated public float getSmallestDisplacementMeters();
+ method @Deprecated public int getSourcesToUse();
+ method @Deprecated public boolean isFlagSet(int);
+ method @Deprecated public boolean isSourceToUseSet(int);
+ method @Deprecated public void resetFlag(int);
+ method @Deprecated public void resetSourceToUse(int);
+ method @Deprecated public void setFlag(int);
+ method @Deprecated public void setMaxPowerAllocationInMW(double);
+ method @Deprecated public void setPeriodInNS(long);
+ method @Deprecated public void setSmallestDisplacementMeters(float);
+ method @Deprecated public void setSourceToUse(int);
+ }
+
+ @Deprecated public static final class GmsFusedBatchOptions.BatchFlags {
+ ctor @Deprecated public GmsFusedBatchOptions.BatchFlags();
+ field @Deprecated public static int CALLBACK_ON_LOCATION_FIX;
+ field @Deprecated public static int WAKEUP_ON_FIFO_FULL;
+ }
+
+ @Deprecated public static final class GmsFusedBatchOptions.SourceTechnologies {
+ ctor @Deprecated public GmsFusedBatchOptions.SourceTechnologies();
+ field @Deprecated public static int BLUETOOTH;
+ field @Deprecated public static int CELL;
+ field @Deprecated public static int GNSS;
+ field @Deprecated public static int SENSORS;
+ field @Deprecated public static int WIFI;
+ }
+
+}
+
diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardware.java b/location/lib/java/com/android/location/provider/FusedLocationHardware.java
new file mode 100644
index 0000000..3d32386
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/FusedLocationHardware.java
@@ -0,0 +1,80 @@
+/*
+ * 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.location.provider;
+
+import android.annotation.SystemApi;
+import android.os.Looper;
+
+/**
+ * Class that exposes IFusedLocationHardware functionality to unbundled services.
+ *
+ * @deprecated This class may no longer be used from Android P and onwards.
+ * @hide
+ */
+@Deprecated
+@SystemApi
+public final class FusedLocationHardware {
+
+ private FusedLocationHardware() {}
+
+ /*
+ * Methods to provide a Facade for IFusedLocationHardware
+ */
+ public void registerSink(FusedLocationHardwareSink sink, Looper looper) {}
+
+ public void unregisterSink(FusedLocationHardwareSink sink) {}
+
+ public int getSupportedBatchSize() {
+ return 0;
+ }
+
+ public void startBatching(int id, GmsFusedBatchOptions batchOptions) {}
+
+ public void stopBatching(int id) {}
+
+ public void updateBatchingOptions(int id, GmsFusedBatchOptions batchOptions) {}
+
+ public void requestBatchOfLocations(int batchSizeRequest) {}
+
+ public void flushBatchedLocations() {}
+
+ public boolean supportsDiagnosticDataInjection() {
+ return false;
+ }
+
+ public void injectDiagnosticData(String data) {}
+
+ public boolean supportsDeviceContextInjection() {
+ return false;
+ }
+
+ public void injectDeviceContext(int deviceEnabledContext) {}
+
+ /**
+ * Returns the version of the FLP HAL.
+ *
+ * <p>Version 1 is the initial release.
+ * <p>Version 2 adds the ability to use {@link #flushBatchedLocations},
+ * {@link FusedLocationHardwareSink#onCapabilities}, and
+ * {@link FusedLocationHardwareSink#onStatusChanged}.
+ *
+ * <p>This method is only available on API 23 or later. Older APIs have version 1.
+ */
+ public int getVersion() {
+ return 1;
+ }
+}
diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
new file mode 100644
index 0000000..30bb1b3
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.location.provider;
+
+import android.annotation.SystemApi;
+import android.location.Location;
+
+/**
+ * Base class for sinks to interact with FusedLocationHardware.
+ *
+ * <p>Default implementations allow new methods to be added without crashing
+ * clients compiled against an old library version.
+ *
+ * @deprecated This class may no longer be used from Android P and onwards.
+ * @hide
+ */
+@Deprecated
+@SystemApi
+public class FusedLocationHardwareSink {
+ /**
+ * Called when one or more locations are available from the FLP
+ * HAL.
+ */
+ public void onLocationAvailable(Location[] locations) {
+ // default do nothing
+ }
+
+ /**
+ * Called when diagnostic data is available from the FLP HAL.
+ */
+ public void onDiagnosticDataAvailable(String data) {
+ // default do nothing
+ }
+
+ /**
+ * Called when capabilities are available from the FLP HAL.
+ * Should be called once right after initialization.
+ *
+ * @param capabilities A bitmask of capabilities defined in
+ * fused_location.h.
+ */
+ public void onCapabilities(int capabilities) {
+ // default do nothing
+ }
+
+ /**
+ * Called when the status changes in the underlying FLP HAL
+ * implementation (the ability to compute location). This
+ * callback will only be made on version 2 or later
+ * (see {@link FusedLocationHardware#getVersion()}).
+ *
+ * @param status One of FLP_STATUS_LOCATION_AVAILABLE or
+ * FLP_STATUS_LOCATION_UNAVAILABLE as defined in
+ * fused_location.h.
+ */
+ public void onStatusChanged(int status) {
+ // default do nothing
+ }
+}
diff --git a/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java b/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java
new file mode 100644
index 0000000..3647377
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java
@@ -0,0 +1,89 @@
+/*
+ * 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.location.provider;
+
+import android.annotation.SystemApi;
+
+/**
+ * Class that exposes FusedBatchOptions to the GmsCore.
+ *
+ * @deprecated This class may no longer be used from Android P and onwards.
+ * @hide
+ */
+@Deprecated
+@SystemApi
+public class GmsFusedBatchOptions {
+
+ public void setMaxPowerAllocationInMW(double value) {}
+
+ public double getMaxPowerAllocationInMW() {
+ return 0;
+ }
+
+ public void setPeriodInNS(long value) {}
+
+ public long getPeriodInNS() {
+ return 0;
+ }
+
+ public void setSmallestDisplacementMeters(float value) {}
+
+ public float getSmallestDisplacementMeters() {
+ return 0;
+ }
+
+ public void setSourceToUse(int source) {}
+
+ public void resetSourceToUse(int source) {}
+
+ public boolean isSourceToUseSet(int source) {
+ return false;
+ }
+
+ public int getSourcesToUse() {
+ return 0;
+ }
+
+ public void setFlag(int flag) {}
+
+ public void resetFlag(int flag) {}
+
+ public boolean isFlagSet(int flag) {
+ return false;
+ }
+
+ public int getFlags() {
+ return 0;
+ }
+
+ /**
+ * Definition of enum flag sets needed by this class.
+ * Such values need to be kept in sync with the ones in fused_location.h
+ */
+ public static final class SourceTechnologies {
+ public static int GNSS = 1 << 0;
+ public static int WIFI = 1 << 1;
+ public static int SENSORS = 1 << 2;
+ public static int CELL = 1 << 3;
+ public static int BLUETOOTH = 1 << 4;
+ }
+
+ public static final class BatchFlags {
+ public static int WAKEUP_ON_FIFO_FULL = 1 << 0;
+ public static int CALLBACK_ON_LOCATION_FIX = 1 << 1;
+ }
+}
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 47e4256..b545a83 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -23,7 +23,10 @@
import android.location.LocationManager;
import android.location.LocationProvider;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ILocationProvider;
+import android.location.provider.ILocationProviderManager;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.IBinder;
@@ -34,10 +37,6 @@
import androidx.annotation.RequiresApi;
-import com.android.internal.location.ILocationProvider;
-import com.android.internal.location.ILocationProviderManager;
-import com.android.internal.location.ProviderRequest;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
@@ -58,7 +57,11 @@
* <p>IMPORTANT: This class is effectively a public API for unbundled
* applications, and must remain API stable. See README.txt in the root
* of this package for more information.
+ *
+ * @deprecated This class is not part of the standard API surface - use
+ * {@link android.location.provider.LocationProviderBase} instead.
*/
+@Deprecated
public abstract class LocationProviderBase {
/**
@@ -386,8 +389,8 @@
}
@Override
- public void setRequest(ProviderRequest request, WorkSource ws) {
- onSetRequest(new ProviderRequestUnbundled(request), ws);
+ public void setRequest(ProviderRequest request) {
+ onSetRequest(new ProviderRequestUnbundled(request), request.getWorkSource());
}
@Override
diff --git a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
index 9d8ccdf..89ca282 100644
--- a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
@@ -17,7 +17,7 @@
package com.android.location.provider;
import android.annotation.NonNull;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
import java.util.Objects;
@@ -35,10 +35,18 @@
public static @NonNull ProviderPropertiesUnbundled create(boolean requiresNetwork,
boolean requiresSatellite, boolean requiresCell, boolean hasMonetaryCost,
boolean supportsAltitude, boolean supportsSpeed, boolean supportsBearing,
- int powerRequirement, int accuracy) {
- return new ProviderPropertiesUnbundled(new ProviderProperties(requiresNetwork,
- requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
- supportsBearing, powerRequirement, accuracy));
+ int powerUsage, int accuracy) {
+ return new ProviderPropertiesUnbundled(new ProviderProperties.Builder()
+ .setHasNetworkRequirement(requiresNetwork)
+ .setHasSatelliteRequirement(requiresSatellite)
+ .setHasCellRequirement(requiresCell)
+ .setHasMonetaryCost(requiresCell)
+ .setHasAltitudeSupport(requiresCell)
+ .setHasSpeedSupport(requiresCell)
+ .setHasBearingSupport(requiresCell)
+ .setPowerUsage(powerUsage)
+ .setAccuracy(accuracy)
+ .build());
}
private final ProviderProperties mProperties;
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index b464fca..28317fe 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -18,13 +18,12 @@
import android.annotation.NonNull;
import android.location.LocationRequest;
+import android.location.provider.ProviderRequest;
import android.os.Build;
import android.os.WorkSource;
import androidx.annotation.RequiresApi;
-import com.android.internal.location.ProviderRequest;
-
import java.util.Collections;
import java.util.List;
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 36f7bed..6cf99e2 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -43,8 +43,8 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayAddress;
@@ -55,7 +55,6 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -99,6 +98,7 @@
RouteInfo mDefaultAudioVideo;
RouteInfo mBluetoothA2dpRoute;
+ boolean mIsBluetoothA2dpOn;
RouteInfo mSelectedRoute;
@@ -113,11 +113,16 @@
IMediaRouterClient mClient;
MediaRouterClientState mClientState;
- Map<Integer, Integer> mStreamVolume = new ArrayMap<>();
+ SparseIntArray mStreamVolume = new SparseIntArray();
final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
@Override
public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
+ try {
+ mIsBluetoothA2dpOn = mAudioService.isBluetoothA2dpOn();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying Bluetooth A2DP state", e);
+ }
mHandler.post(new Runnable() {
@Override public void run() {
updateAudioRoutes(newRoutes);
@@ -267,23 +272,23 @@
}
int getStreamVolume(int streamType) {
- if (!mStreamVolume.containsKey(streamType)) {
+ int idx = mStreamVolume.indexOfKey(streamType);
+ if (idx < 0) {
+ int volume = 0;
try {
- mStreamVolume.put(streamType, mAudioService.getStreamVolume(streamType));
+ volume = mAudioService.getStreamVolume(streamType);
+ mStreamVolume.put(streamType, volume);
} catch (RemoteException e) {
Log.e(TAG, "Error getting local stream volume", e);
+ } finally {
+ return volume;
}
}
- return mStreamVolume.get(streamType);
+ return mStreamVolume.valueAt(idx);
}
boolean isBluetoothA2dpOn() {
- try {
- return mBluetoothA2dpRoute != null && mAudioService.isBluetoothA2dpOn();
- } catch (RemoteException e) {
- Log.e(TAG, "Error querying Bluetooth A2DP state", e);
- return false;
- }
+ return mBluetoothA2dpRoute != null && mIsBluetoothA2dpOn;
}
void updateDiscoveryRequest() {
@@ -1444,12 +1449,8 @@
selectedRoute == sStatic.mDefaultAudioVideo) {
dispatchRouteVolumeChanged(selectedRoute);
} else if (sStatic.mBluetoothA2dpRoute != null) {
- try {
- dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
- sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
- } catch (RemoteException e) {
- Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
- }
+ dispatchRouteVolumeChanged(sStatic.mIsBluetoothA2dpOn
+ ? sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
} else {
dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
}
diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml
index bad0497..12dc170 100644
--- a/packages/FusedLocation/AndroidManifest.xml
+++ b/packages/FusedLocation/AndroidManifest.xml
@@ -40,7 +40,7 @@
LocationManagerService will bind to the service with the highest
version. -->
<service android:name="com.android.location.fused.FusedLocationService"
- android:exported="true"
+ android:exported="false"
android:permission="android.permission.WRITE_SECURE_SETTINGS">
<intent-filter>
<action android:name="com.android.location.service.FusedLocationProvider" />
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 6827d6e..cb55c72 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -20,6 +20,8 @@
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
import static android.location.LocationRequest.QUALITY_LOW_POWER;
+import static android.location.provider.ProviderProperties.ACCURACY_FINE;
+import static android.location.provider.ProviderProperties.POWER_USAGE_LOW;
import static com.android.location.provider.ProviderRequestUnbundled.INTERVAL_DISABLED;
@@ -28,113 +30,57 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationRequest;
-import android.os.WorkSource;
+import android.location.provider.LocationProviderBase;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
+import android.os.Bundle;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.location.ProviderRequest;
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.ProviderPropertiesUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
import java.io.PrintWriter;
+import java.util.Objects;
/** Basic fused location provider implementation. */
public class FusedLocationProvider extends LocationProviderBase {
private static final String TAG = "FusedLocationProvider";
- private static final ProviderPropertiesUnbundled PROPERTIES =
- ProviderPropertiesUnbundled.create(
- /* requiresNetwork = */ false,
- /* requiresSatellite = */ false,
- /* requiresCell = */ false,
- /* hasMonetaryCost = */ false,
- /* supportsAltitude = */ true,
- /* supportsSpeed = */ true,
- /* supportsBearing = */ true,
- Criteria.POWER_LOW,
- Criteria.ACCURACY_FINE
- );
+ private static final ProviderProperties PROPERTIES = new ProviderProperties.Builder()
+ .setHasAltitudeSupport(true)
+ .setHasSpeedSupport(true)
+ .setHasBearingSupport(true)
+ .setPowerUsage(POWER_USAGE_LOW)
+ .setAccuracy(ACCURACY_FINE)
+ .build();
private static final long MAX_LOCATION_COMPARISON_NS = 11 * 1000000000L; // 11 seconds
- final Object mLock = new Object();
+ private final Object mLock = new Object();
private final Context mContext;
private final LocationManager mLocationManager;
- private final LocationListener mGpsListener;
- private final LocationListener mNetworkListener;
+ private final ChildLocationListener mGpsListener;
+ private final ChildLocationListener mNetworkListener;
private final BroadcastReceiver mUserChangeReceiver;
@GuardedBy("mLock")
- ProviderRequestUnbundled mRequest;
- @GuardedBy("mLock")
- private WorkSource mWorkSource;
- @GuardedBy("mLock")
- private long mGpsInterval;
- @GuardedBy("mLock")
- private long mNetworkInterval;
+ private ProviderRequest mRequest;
@GuardedBy("mLock")
- @Nullable private Location mFusedLocation;
- @GuardedBy("mLock")
- @Nullable Location mGpsLocation;
- @GuardedBy("mLock")
- @Nullable Location mNetworkLocation;
+ private @Nullable Location mFusedLocation;
public FusedLocationProvider(Context context) {
super(context, TAG, PROPERTIES);
mContext = context;
- mLocationManager = context.getSystemService(LocationManager.class);
+ mLocationManager = Objects.requireNonNull(context.getSystemService(LocationManager.class));
- mGpsListener = new LocationListener() {
- @Override
- public void onLocationChanged(Location location) {
- synchronized (mLock) {
- mGpsLocation = location;
- reportBestLocationLocked();
- }
- }
-
- @Override
- public void onProviderDisabled(String provider) {
- synchronized (mLock) {
- // if satisfying a bypass request, don't clear anything
- if (mRequest.getReportLocation() && mRequest.isLocationSettingsIgnored()) {
- return;
- }
-
- mGpsLocation = null;
- }
- }
- };
-
- mNetworkListener = new LocationListener() {
- @Override
- public void onLocationChanged(Location location) {
- synchronized (mLock) {
- mNetworkLocation = location;
- reportBestLocationLocked();
- }
- }
-
- @Override
- public void onProviderDisabled(String provider) {
- synchronized (mLock) {
- // if satisfying a bypass request, don't clear anything
- if (mRequest.getReportLocation() && mRequest.isLocationSettingsIgnored()) {
- return;
- }
-
- mNetworkLocation = null;
- }
- }
- };
+ mGpsListener = new ChildLocationListener(GPS_PROVIDER);
+ mNetworkListener = new ChildLocationListener(NETWORK_PROVIDER);
mUserChangeReceiver = new BroadcastReceiver() {
@Override
@@ -147,10 +93,7 @@
}
};
- mRequest = new ProviderRequestUnbundled(ProviderRequest.EMPTY_REQUEST);
- mWorkSource = new WorkSource();
- mGpsInterval = INTERVAL_DISABLED;
- mNetworkInterval = INTERVAL_DISABLED;
+ mRequest = ProviderRequest.EMPTY_REQUEST;
}
void start() {
@@ -161,57 +104,53 @@
mContext.unregisterReceiver(mUserChangeReceiver);
synchronized (mLock) {
- mRequest = new ProviderRequestUnbundled(ProviderRequest.EMPTY_REQUEST);
+ mRequest = ProviderRequest.EMPTY_REQUEST;
updateRequirementsLocked();
}
}
@Override
- public void onSetRequest(ProviderRequestUnbundled request, WorkSource workSource) {
+ public void onSetRequest(ProviderRequest request) {
synchronized (mLock) {
mRequest = request;
- mWorkSource = workSource;
updateRequirementsLocked();
}
}
- @GuardedBy("mLock")
- private void updateRequirementsLocked() {
- long gpsInterval = mRequest.getQuality() < QUALITY_LOW_POWER ? mRequest.getInterval()
- : INTERVAL_DISABLED;
- long networkInterval = mRequest.getInterval();
+ @Override
+ public void onFlush(OnFlushCompleteCallback callback) {
+ OnFlushCompleteCallback wrapper = new OnFlushCompleteCallback() {
+ private int mFlushCount = 2;
- if (gpsInterval != mGpsInterval) {
- resetProviderRequestLocked(GPS_PROVIDER, mGpsInterval, gpsInterval, mGpsListener);
- mGpsInterval = gpsInterval;
- }
- if (networkInterval != mNetworkInterval) {
- resetProviderRequestLocked(NETWORK_PROVIDER, mNetworkInterval, networkInterval,
- mNetworkListener);
- mNetworkInterval = networkInterval;
- }
+ @Override
+ public void onFlushComplete() {
+ if (--mFlushCount == 0) {
+ callback.onFlushComplete();
+ }
+ }
+ };
+
+ mGpsListener.flush(wrapper);
+ mNetworkListener.flush(wrapper);
}
+ @Override
+ public void onSendExtraCommand(String command, @Nullable Bundle extras) {}
+
@GuardedBy("mLock")
- private void resetProviderRequestLocked(String provider, long oldInterval, long newInterval,
- LocationListener listener) {
- if (oldInterval != INTERVAL_DISABLED && newInterval == INTERVAL_DISABLED) {
- mLocationManager.removeUpdates(listener);
- }
- if (newInterval != INTERVAL_DISABLED) {
- LocationRequest request = new LocationRequest.Builder(newInterval)
- .setQuality(mRequest.getQuality())
- .setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored())
- .setWorkSource(mWorkSource)
- .build();
- mLocationManager.requestLocationUpdates(provider, request, mContext.getMainExecutor(),
- listener);
- }
+ private void updateRequirementsLocked() {
+ long gpsInterval = mRequest.getQuality() < QUALITY_LOW_POWER ? mRequest.getIntervalMillis()
+ : INTERVAL_DISABLED;
+ long networkInterval = mRequest.getIntervalMillis();
+
+ mGpsListener.resetProviderRequest(gpsInterval);
+ mNetworkListener.resetProviderRequest(networkInterval);
}
@GuardedBy("mLock")
void reportBestLocationLocked() {
- Location bestLocation = chooseBestLocation(mGpsLocation, mNetworkLocation);
+ Location bestLocation = chooseBestLocation(mGpsListener.getLocation(),
+ mNetworkListener.getLocation());
if (bestLocation == mFusedLocation) {
return;
}
@@ -228,25 +167,25 @@
// clear cached locations when the user changes to prevent leaking user information
synchronized (mLock) {
mFusedLocation = null;
- mGpsLocation = null;
- mNetworkLocation = null;
+ mGpsListener.clearLocation();
+ mNetworkListener.clearLocation();
}
}
void dump(PrintWriter writer) {
synchronized (mLock) {
writer.println("request: " + mRequest);
- if (mGpsInterval != INTERVAL_DISABLED) {
- writer.println(" gps interval: " + mGpsInterval);
+ if (mGpsListener.getInterval() != INTERVAL_DISABLED) {
+ writer.println(" gps interval: " + mGpsListener.getInterval());
}
- if (mNetworkInterval != INTERVAL_DISABLED) {
- writer.println(" network interval: " + mNetworkInterval);
+ if (mNetworkListener.getInterval() != INTERVAL_DISABLED) {
+ writer.println(" network interval: " + mNetworkListener.getInterval());
}
- if (mGpsLocation != null) {
- writer.println(" last gps location: " + mGpsLocation);
+ if (mGpsListener.getLocation() != null) {
+ writer.println(" last gps location: " + mGpsListener.getLocation());
}
- if (mNetworkLocation != null) {
- writer.println(" last network location: " + mNetworkLocation);
+ if (mNetworkListener.getLocation() != null) {
+ writer.println(" last network location: " + mNetworkListener.getLocation());
}
}
}
@@ -279,4 +218,104 @@
}
return locationA.getAccuracy() < locationB.getAccuracy() ? locationA : locationB;
}
+
+ private class ChildLocationListener implements LocationListener {
+
+ private final String mProvider;
+ private final SparseArray<OnFlushCompleteCallback> mPendingFlushes;
+
+ @GuardedBy("mLock")
+ private int mNextFlushCode = 0;
+ @GuardedBy("mLock")
+ private @Nullable Location mLocation = null;
+ @GuardedBy("mLock")
+ private long mInterval = INTERVAL_DISABLED;
+
+ ChildLocationListener(String provider) {
+ mProvider = provider;
+ mPendingFlushes = new SparseArray<>();
+ }
+
+ @Nullable Location getLocation() {
+ synchronized (mLock) {
+ return mLocation;
+ }
+ }
+
+ long getInterval() {
+ synchronized (mLock) {
+ return mInterval;
+ }
+ }
+
+ void clearLocation() {
+ synchronized (mLock) {
+ mLocation = null;
+ }
+ }
+
+ private void resetProviderRequest(long newInterval) {
+ synchronized (mLock) {
+ if (newInterval == mInterval) {
+ return;
+ }
+
+ if (mInterval != INTERVAL_DISABLED && newInterval == INTERVAL_DISABLED) {
+ mLocationManager.removeUpdates(this);
+ }
+
+ mInterval = newInterval;
+
+ if (mInterval != INTERVAL_DISABLED) {
+ LocationRequest request = new LocationRequest.Builder(mInterval)
+ .setMaxUpdateDelayMillis(mRequest.getMaxUpdateDelayMillis())
+ .setQuality(mRequest.getQuality())
+ .setLowPower(mRequest.isLowPower())
+ .setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored())
+ .setWorkSource(mRequest.getWorkSource())
+ .build();
+ mLocationManager.requestLocationUpdates(mProvider, request,
+ mContext.getMainExecutor(), this);
+ }
+ }
+ }
+
+ void flush(OnFlushCompleteCallback callback) {
+ synchronized (mLock) {
+ int requestCode = mNextFlushCode++;
+ mPendingFlushes.put(requestCode, callback);
+ mLocationManager.requestFlush(mProvider, this, requestCode);
+ }
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ synchronized (mLock) {
+ mLocation = location;
+ reportBestLocationLocked();
+ }
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ synchronized (mLock) {
+ // if satisfying a bypass request, don't clear anything
+ if (mRequest.isActive() && mRequest.isLocationSettingsIgnored()) {
+ return;
+ }
+
+ mLocation = null;
+ }
+ }
+
+ @Override
+ public void onFlushComplete(int requestCode) {
+ synchronized (mLock) {
+ OnFlushCompleteCallback callback = mPendingFlushes.removeReturnOld(requestCode);
+ if (callback != null) {
+ callback.onFlushComplete();
+ }
+ }
+ }
+ }
}
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index 2bda530..d472311 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,18 +27,18 @@
import android.location.LocationManager;
import android.location.LocationRequest;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ILocationProvider;
+import android.location.provider.ILocationProviderManager;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
-import android.os.WorkSource;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.location.ILocationProvider;
-import com.android.internal.location.ILocationProviderManager;
-import com.android.internal.location.ProviderRequest;
import com.android.location.fused.FusedLocationProvider;
import org.junit.After;
@@ -71,7 +71,7 @@
long seed = System.currentTimeMillis();
Log.i(TAG, "location seed: " + seed);
- Context context = InstrumentationRegistry.getTargetContext();
+ Context context = ApplicationProvider.getApplicationContext();
mRandom = new Random(seed);
mLocationManager = context.getSystemService(LocationManager.class);
@@ -120,8 +120,7 @@
mProvider.setRequest(
new ProviderRequest.Builder()
.setIntervalMillis(1000)
- .build(),
- new WorkSource());
+ .build());
Location location = createLocation(NETWORK_PROVIDER, mRandom);
mLocationManager.setTestProviderLocation(NETWORK_PROVIDER, location);
@@ -135,8 +134,7 @@
new ProviderRequest.Builder()
.setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
.setIntervalMillis(1000)
- .build(),
- new WorkSource());
+ .build());
Location location = createLocation(GPS_PROVIDER, mRandom);
mLocationManager.setTestProviderLocation(GPS_PROVIDER, location);
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
index a9e9f8c..b1553e9 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
@@ -15,10 +15,10 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:background="@android:color/transparent"
android:orientation="vertical">
<LinearLayout
@@ -26,7 +26,7 @@
android:minHeight="@dimen/min_switch_bar_height"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:background="@android:color/transparent"
+ android:background="?android:attr/colorBackground"
android:paddingLeft="@dimen/switchbar_margin_start"
android:paddingRight="@dimen/switchbar_margin_end">
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
index b401398..ac8ab14 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
@@ -17,7 +17,6 @@
<resources>
- <color name="background_color">@android:color/transparent</color>
<color name="title_text_color">@*android:color/primary_text_light</color>
<color name="thumb_off">#BFFFFFFF</color>
<color name="track_on">@*android:color/accent_device_default_dark</color>
diff --git a/packages/SettingsLib/res/drawable/ic_carrier_wifi.xml b/packages/SettingsLib/res/drawable/ic_carrier_wifi.xml
new file mode 100644
index 0000000..ed9d85e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_carrier_wifi.xml
@@ -0,0 +1,30 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="38dp"
+ android:height="24dp"
+ android:viewportWidth="38.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.45,14.48h1.8c-0.05,0.98 -0.24,1.82 -0.6,2.53c-0.35,0.7 -0.85,1.24 -1.51,1.62c-0.66,0.38 -1.48,0.57 -2.47,0.57c-0.71,0 -1.35,-0.14 -1.92,-0.42c-0.57,-0.28 -1.06,-0.68 -1.47,-1.2c-0.4,-0.53 -0.71,-1.16 -0.93,-1.89c-0.21,-0.74 -0.32,-1.56 -0.32,-2.48v-2.63c0,-0.91 0.11,-1.74 0.32,-2.47c0.22,-0.74 0.54,-1.36 0.95,-1.88C3.71,5.69 4.21,5.29 4.8,5.01c0.6,-0.28 1.28,-0.42 2.03,-0.42c0.92,0 1.71,0.19 2.34,0.56c0.64,0.36 1.14,0.9 1.48,1.61c0.35,0.7 0.55,1.57 0.59,2.59h-1.8C9.41,8.59 9.29,7.98 9.1,7.52C8.91,7.04 8.63,6.69 8.26,6.47C7.9,6.24 7.42,6.13 6.84,6.13c-0.52,0 -0.97,0.1 -1.36,0.31C5.1,6.65 4.79,6.95 4.54,7.34C4.3,7.72 4.12,8.19 3.99,8.74c-0.12,0.54 -0.18,1.15 -0.18,1.82v2.65c0,0.62 0.05,1.21 0.15,1.75c0.1,0.54 0.27,1.02 0.49,1.43c0.23,0.4 0.52,0.72 0.89,0.95c0.36,0.23 0.81,0.34 1.33,0.34c0.66,0 1.18,-0.11 1.56,-0.32c0.38,-0.21 0.67,-0.56 0.85,-1.03C9.27,15.85 9.39,15.23 9.45,14.48z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24.87,4.78l-1.74,9.72l-0.2,1.64l-0.29,-1.44l-2.21,-9.92l-0.2,0l-1.06,0l-0.22,0l-2.28,9.92l-0.28,1.4l-0.18,-1.59l-1.78,-9.73l-1.79,0l2.83,14.22l0.34,0l0.92,0l0.35,0l2.48,-10.36l0.14,-0.84l0.15,0.84l2.36,10.36l0.36,0l0.92,0l0.34,0l2.82,-14.22z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M35.72,6.32l0,-1.54l-5.61,0l-0.34,0l-1.45,0l0,14.22l1.79,0l0,-6.28l4.8,0l0,-1.54l-4.8,0l0,-4.86z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 0ce8dd8..792fb52 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1447,6 +1447,9 @@
<!-- Content description of the data connection type 5G+. [CHAR LIMIT=NONE] -->
<string name="data_connection_5g_plus" translatable="false">5G+</string>
+ <!-- Content description of the data connection type Carrier WiFi. [CHAR LIMIT=NONE] -->
+ <string name="data_connection_carrier_wifi">CWF</string>
+
<!-- Content description of the cell data being disabled. [CHAR LIMIT=NONE] -->
<string name="cell_data_off_content_description">Mobile data off</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
similarity index 97%
rename from packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
rename to packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
index 61af5a7..647fd2a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
@@ -19,7 +19,7 @@
/**
* Class to access MediaOutput constants.
*/
-public class MediaOutputSliceConstants {
+public class MediaOutputConstants {
/**
* Key for the Remote Media slice.
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
index c2613a5..0cb9906 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
@@ -43,6 +43,7 @@
public static final int ICON_1X = R.drawable.ic_1x_mobiledata;
public static final int ICON_5G = R.drawable.ic_5g_mobiledata;
public static final int ICON_5G_PLUS = R.drawable.ic_5g_plus_mobiledata;
+ public static final int ICON_CWF = R.drawable.ic_carrier_wifi;
public static final MobileIconGroup CARRIER_NETWORK_CHANGE = new MobileIconGroup(
"CARRIER_NETWORK_CHANGE",
@@ -276,6 +277,20 @@
0,
false);
+ public static final MobileIconGroup CARRIER_MERGED_WIFI = new MobileIconGroup(
+ "CWF",
+ /* sbIcons= */ null,
+ /* qsIcons= */ null,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ /* sbNullState= */ 0,
+ /* qsNullState= */ 0,
+ /* sbDiscState= */ 0,
+ /* qsDiscState= */ 0,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.data_connection_carrier_wifi,
+ TelephonyIcons.ICON_CWF,
+ /* isWide= */ true);
+
// When adding a new MobileIconGround, check if the dataContentDescription has to be filtered
// in QSCarrier#hasValidTypeContentDescription
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index cbb5105..4614694 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -33,6 +33,7 @@
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
+import android.util.FeatureFlagUtils;
import com.android.settingslib.R;
@@ -103,11 +104,14 @@
private Network mDefaultNetwork = null;
private NetworkCapabilities mDefaultNetworkCapabilities = null;
private final Runnable mCallback;
+ private final boolean mProviderModel;
private WifiInfo mWifiInfo;
public boolean enabled;
public boolean isCaptivePortal;
public boolean isDefaultNetwork;
+ public boolean isCarrierMerged;
+ public int subId;
public int state;
public boolean connected;
public String ssid;
@@ -124,6 +128,8 @@
mNetworkScoreManager = networkScoreManager;
mConnectivityManager = connectivityManager;
mCallback = callback;
+ mProviderModel = FeatureFlagUtils.isEnabled(
+ mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
public void setListening(boolean listening) {
@@ -193,6 +199,10 @@
} else {
ssid = getValidSsid(mWifiInfo);
}
+ if (mProviderModel) {
+ isCarrierMerged = mWifiInfo.isCarrierMerged();
+ subId = mWifiInfo.getSubscriptionId();
+ }
updateRssi(mWifiInfo.getRssi());
maybeRequestNetworkScore();
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0a43014..14151191 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -379,6 +379,9 @@
<uses-permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" />
<uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
+ <!-- Permission required for CTS tests to enable/disable rate limiting toasts. -->
+ <uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 19f8248..55bdebd 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -89,7 +89,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/keyguard_status_area"
- android:paddingTop="20dp"
android:visibility="gone">
<com.android.keyguard.AnimatableClockView
android:id="@+id/animatable_clock_view_large"
@@ -97,7 +96,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
- android:textSize="200dp"
+ android:textSize="@dimen/large_clock_text_size"
android:letterSpacing="0.02"
android:lineSpacingMultiplier=".8"
android:includeFontPadding="false"
diff --git a/packages/SystemUI/res/values-h700dp/dimens.xml b/packages/SystemUI/res/values-h700dp/dimens.xml
new file mode 100644
index 0000000..fbd985e
--- /dev/null
+++ b/packages/SystemUI/res/values-h700dp/dimens.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) -->
+ <dimen name="large_clock_text_size">170dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index 6a0e880..cfacbec 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -17,4 +17,7 @@
<resources>
<!-- Minimum margin between clock and top of screen or ambient indication -->
<dimen name="keyguard_clock_top_margin">76dp</dimen>
-</resources>
\ No newline at end of file
+
+ <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) -->
+ <dimen name="large_clock_text_size">200dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6f69483..8888ad6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1041,6 +1041,13 @@
burn-in on AOD. -->
<dimen name="burn_in_prevention_offset_y">50dp</dimen>
+ <!-- The maximum offset in either direction that elements are moved vertically to prevent
+ burn-in on AOD. -->
+ <dimen name="burn_in_prevention_offset_y_large_clock">42dp</dimen>
+
+ <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) -->
+ <dimen name="large_clock_text_size">150dp</dimen>
+
<!-- The maximum offset in either direction that icons move to prevent burn-in on AOD. -->
<dimen name="default_burn_in_prevention_offset">15dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 6cc863a4..2d972e0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -24,6 +24,7 @@
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextClock;
+import android.widget.TextView;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
@@ -147,6 +148,10 @@
setTextSize(TypedValue.COMPLEX_UNIT_PX, mContext.getResources()
.getDimensionPixelSize(R.dimen.widget_big_font_size));
+ ((TextView) mNewLockscreenLargeClockFrame.getChildAt(0))
+ .setTextSize(TypedValue.COMPLEX_UNIT_PX, mContext.getResources()
+ .getDimensionPixelSize(R.dimen.large_clock_text_size));
+
mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_clock_switch_y_shift);
}
@@ -391,7 +396,6 @@
if (hasVisibleNotifications == mHasVisibleNotifications) {
return;
}
-
animateClockChange(!hasVisibleNotifications);
mHasVisibleNotifications = hasVisibleNotifications;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 08262de..eefae5b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -111,7 +111,8 @@
.setBubbles(mWMComponent.getBubbles())
.setHideDisplayCutout(mWMComponent.getHideDisplayCutout())
.setShellCommandHandler(mWMComponent.getShellCommandHandler())
- .setAppPairs(mWMComponent.getAppPairs());
+ .setAppPairs(mWMComponent.getAppPairs())
+ .setTaskViewFactory(mWMComponent.getTaskViewFactory());
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
// is separating this logic into newly creating SystemUITestsFactory.
@@ -122,7 +123,8 @@
.setBubbles(Optional.ofNullable(null))
.setHideDisplayCutout(Optional.ofNullable(null))
.setShellCommandHandler(Optional.ofNullable(null))
- .setAppPairs(Optional.ofNullable(null));
+ .setAppPairs(Optional.ofNullable(null))
+ .setTaskViewFactory(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
if (initializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
index 6c28d11..ff55b76d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
@@ -26,7 +26,9 @@
import android.service.controls.actions.ModeAction
import android.text.InputType
import android.util.Log
+import android.view.LayoutInflater
import android.view.WindowManager
+import android.view.inputmethod.InputMethodManager
import android.widget.CheckBox
import android.widget.EditText
@@ -71,11 +73,21 @@
R.string.controls_pin_instructions
)
}
- val builder = AlertDialog.Builder(cvh.context, STYLE).apply {
+ return object : AlertDialog(cvh.context, STYLE) {
+ override fun dismiss() {
+ window?.decorView?.let {
+ // workaround for b/159309083
+ it.context.getSystemService(InputMethodManager::class.java)
+ ?.hideSoftInputFromWindow(it.windowToken, 0)
+ }
+ super.dismiss()
+ }
+ }.apply {
setTitle(title)
- setView(R.layout.controls_dialog_pin)
- setPositiveButton(
- android.R.string.ok,
+ setView(LayoutInflater.from(context).inflate(R.layout.controls_dialog_pin, null))
+ setButton(
+ DialogInterface.BUTTON_POSITIVE,
+ context.getText(android.R.string.ok),
DialogInterface.OnClickListener { dialog, _ ->
if (dialog is Dialog) {
dialog.requireViewById<EditText>(R.id.controls_pin_input)
@@ -85,15 +97,15 @@
dialog.dismiss()
}
})
- setNegativeButton(
- android.R.string.cancel,
+ setButton(
+ DialogInterface.BUTTON_NEGATIVE,
+ context.getText(android.R.string.cancel),
DialogInterface.OnClickListener { dialog, _ ->
onCancel.invoke()
dialog.cancel()
}
)
- }
- return builder.create().apply {
+
getWindow().apply {
setType(WINDOW_TYPE)
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index ab82225..7cd7e18 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -36,6 +36,8 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.wm.shell.TaskViewFactory
+import java.util.Optional
import javax.inject.Inject
@SysUISingleton
@@ -45,7 +47,8 @@
@Main private val uiExecutor: DelayableExecutor,
private val activityStarter: ActivityStarter,
private val keyguardStateController: KeyguardStateController,
- private val globalActionsComponent: GlobalActionsComponent
+ private val globalActionsComponent: GlobalActionsComponent,
+ private val taskViewFactory: Optional<TaskViewFactory>
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
@@ -163,11 +166,13 @@
uiExecutor.execute {
// make sure the intent is valid before attempting to open the dialog
- if (activities.isNotEmpty()) {
- dialog = DetailDialog(cvh, intent).also {
- it.setOnDismissListener { _ -> dialog = null }
- it.show()
- }
+ if (activities.isNotEmpty() && taskViewFactory.isPresent) {
+ taskViewFactory.get().create(cvh.context, uiExecutor, {
+ dialog = DetailDialog(cvh, it, intent).also {
+ it.setOnDismissListener { _ -> dialog = null }
+ it.show()
+ }
+ })
} else {
cvh.setErrorStatus()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 5b11627..e4f9064 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -16,8 +16,12 @@
package com.android.systemui.controls.ui
-import android.app.ActivityView
+import android.app.ActivityOptions
+import android.app.ActivityTaskManager
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.Dialog
+import android.app.PendingIntent
+import android.content.ComponentName
import android.content.Intent
import android.provider.Settings
import android.view.View
@@ -26,9 +30,9 @@
import android.view.WindowInsets.Type
import android.view.WindowManager
import android.widget.ImageView
-
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.R
+import com.android.wm.shell.TaskView
/**
* A dialog that provides an {@link ActivityView}, allowing the application to provide
@@ -37,6 +41,7 @@
*/
class DetailDialog(
val cvh: ControlViewHolder,
+ val activityView: TaskView,
val intent: Intent
) : Dialog(cvh.context, R.style.Theme_SystemUI_Dialog_Control_DetailPanel) {
@@ -49,10 +54,16 @@
private const val EXTRA_USE_PANEL = "controls.DISPLAY_IN_PANEL"
}
- var activityView = ActivityView(context)
+ var detailTaskId = INVALID_TASK_ID
- val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() {
- override fun onActivityViewReady(view: ActivityView) {
+ fun removeDetailTask() {
+ if (detailTaskId == INVALID_TASK_ID) return
+ ActivityTaskManager.getInstance().removeTask(detailTaskId)
+ detailTaskId = INVALID_TASK_ID
+ }
+
+ val stateCallback = object : TaskView.Listener {
+ override fun onInitialized() {
val launchIntent = Intent(intent)
launchIntent.putExtra(EXTRA_USE_PANEL, true)
@@ -60,18 +71,31 @@
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
- view.startActivity(launchIntent)
+ activityView.startActivity(
+ PendingIntent.getActivity(context, 0, launchIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT), null, ActivityOptions.makeBasic())
}
- override fun onActivityViewDestroyed(view: ActivityView) {}
-
override fun onTaskRemovalStarted(taskId: Int) {
+ detailTaskId = INVALID_TASK_ID
dismiss()
}
+
+ override fun onTaskCreated(taskId: Int, name: ComponentName?) {
+ detailTaskId = taskId
+ }
+
+ override fun onReleased() {
+ removeDetailTask()
+ }
}
init {
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+ // To pass touches to the task inside TaskView.
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
+ window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+
setContentView(R.layout.controls_detail_dialog)
requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
@@ -84,6 +108,9 @@
requireViewById<ImageView>(R.id.control_detail_open_in_app).apply {
setOnClickListener { v: View ->
+ // Remove the task explicitly, since onRelease() callback will be executed after
+ // startActivity() below is called.
+ removeDetailTask()
dismiss()
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
v.context.startActivity(intent)
@@ -126,7 +153,7 @@
}
override fun show() {
- activityView.setCallback(stateCallback)
+ activityView.setListener(cvh.uiExecutor, stateCallback)
super.show()
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index f9505de..612a559 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -25,6 +25,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.InjectionInflationController;
import com.android.wm.shell.ShellCommandHandler;
+import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
@@ -71,6 +72,9 @@
Builder setBubbles(Optional<Bubbles> b);
@BindsInstance
+ Builder setTaskViewFactory(Optional<TaskViewFactory> t);
+
+ @BindsInstance
Builder setHideDisplayCutout(Optional<HideDisplayCutout> h);
@BindsInstance
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 943a54e..c75dc84 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -19,6 +19,7 @@
import com.android.systemui.wmshell.WMShellModule;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.ShellInit;
+import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
@@ -79,4 +80,7 @@
@WMSingleton
Optional<HideDisplayCutout> getHideDisplayCutout();
+
+ @WMSingleton
+ Optional<TaskViewFactory> getTaskViewFactory();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 4f85192..5dd2f06 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -46,7 +46,7 @@
import com.android.settingslib.media.InfoMediaManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
-import com.android.settingslib.media.MediaOutputSliceConstants;
+import com.android.settingslib.media.MediaOutputConstants;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
@@ -449,8 +449,8 @@
mCallback.dismissDialog();
final ActivityStarter.OnDismissAction postKeyguardAction = () -> {
mContext.sendBroadcast(new Intent()
- .setAction(MediaOutputSliceConstants.ACTION_LAUNCH_BLUETOOTH_PAIRING)
- .setPackage(MediaOutputSliceConstants.SETTINGS_PACKAGE_NAME));
+ .setAction(MediaOutputConstants.ACTION_LAUNCH_BLUETOOTH_PAIRING)
+ .setPackage(MediaOutputConstants.SETTINGS_PACKAGE_NAME));
mShadeController.animateCollapsePanels();
return true;
};
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
index 0ce0c02..bd3f5a6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
@@ -20,7 +20,7 @@
import android.content.Context
import android.content.Intent
import android.text.TextUtils
-import com.android.settingslib.media.MediaOutputSliceConstants
+import com.android.settingslib.media.MediaOutputConstants
import javax.inject.Inject
/**
@@ -30,10 +30,10 @@
private val mediaOutputDialogFactory: MediaOutputDialogFactory
) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
- if (TextUtils.equals(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG,
+ if (TextUtils.equals(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG,
intent.action)) {
mediaOutputDialogFactory.create(
- intent.getStringExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME), false)
+ intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME), false)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index 4efe4d8..f0e4cce 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -461,14 +461,8 @@
@Override
public void draw(Canvas canvas) {
if (mHasOvalBg) {
- canvas.save();
- int cx = (getLeft() + getRight()) / 2;
- int cy = (getTop() + getBottom()) / 2;
- canvas.translate(cx, cy);
int d = Math.min(getWidth(), getHeight());
- int r = d / 2;
- canvas.drawOval(-r, -r, r, r, mOvalBgPaint);
- canvas.restore();
+ canvas.drawOval(0, 0, d, d, mOvalBgPaint);
}
super.draw(canvas);
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 2ad12b9..065920c 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -22,7 +22,6 @@
import android.app.Activity;
import android.app.INotificationManager;
import android.app.people.IPeopleManager;
-import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTile.java
similarity index 99%
rename from core/java/android/app/people/PeopleSpaceTile.java
rename to packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTile.java
index 95739f3..d7ee97b 100644
--- a/core/java/android/app/people/PeopleSpaceTile.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTile.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.people;
+package com.android.systemui.people;
import android.annotation.NonNull;
import android.app.Person;
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
index 9ae7847..25b91da 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
@@ -16,7 +16,6 @@
package com.android.systemui.people;
-import android.app.people.PeopleSpaceTile;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.graphics.drawable.Drawable;
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index f1a57bf..12d5bac 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -23,7 +23,7 @@
import android.app.PendingIntent;
import android.app.people.ConversationChannel;
import android.app.people.IPeopleManager;
-import android.app.people.PeopleSpaceTile;
+import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
index fb33aff..cd0a5df 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -18,7 +18,7 @@
import android.app.INotificationManager;
import android.app.people.IPeopleManager;
-import android.app.people.PeopleSpaceTile;
+import com.android.systemui.people.PeopleSpaceTile;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 6da5d1b9..6cd7a74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -112,6 +112,11 @@
private int mBurnInPreventionOffsetY;
/**
+ * Burn-in prevention y translation for large clock layouts.
+ */
+ private int mBurnInPreventionOffsetYLargeClock;
+
+ /**
* Doze/AOD transition amount.
*/
private float mDarkAmount;
@@ -156,6 +161,8 @@
R.dimen.burn_in_prevention_offset_x);
mBurnInPreventionOffsetY = res.getDimensionPixelSize(
R.dimen.burn_in_prevention_offset_y);
+ mBurnInPreventionOffsetYLargeClock = res.getDimensionPixelSize(
+ R.dimen.burn_in_prevention_offset_y_large_clock);
}
/**
@@ -287,8 +294,12 @@
}
private float burnInPreventionOffsetY() {
- return getBurnInOffset(mBurnInPreventionOffsetY * 2, false /* xAxis */)
- - mBurnInPreventionOffsetY;
+ int offset = mBurnInPreventionOffsetY;
+ if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+ offset = mBurnInPreventionOffsetYLargeClock;
+ }
+
+ return getBurnInOffset(offset * 2, false /* xAxis */) - offset;
}
private float burnInPreventionOffsetX() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 291aefe..e419966 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -359,7 +359,7 @@
// broadcasts
IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
index 9db109d..66e8082 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import com.android.settingslib.AccessibilityContentDescriptions;
+import com.android.settingslib.SignalIcon.IconGroup;
import com.android.systemui.R;
public class WifiIcons {
@@ -40,6 +42,7 @@
WIFI_NO_INTERNET_ICONS,
WIFI_FULL_ICONS
};
+
static final int[][] WIFI_SIGNAL_STRENGTH = QS_WIFI_SIGNAL_STRENGTH;
public static final int QS_WIFI_DISABLED = com.android.internal.R.drawable.ic_wifi_signal_0;
@@ -47,4 +50,16 @@
static final int WIFI_NO_NETWORK = QS_WIFI_NO_NETWORK;
static final int WIFI_LEVEL_COUNT = WIFI_SIGNAL_STRENGTH[0].length;
+
+ public static final IconGroup UNMERGED_WIFI = new IconGroup(
+ "Wi-Fi Icons",
+ WifiIcons.WIFI_SIGNAL_STRENGTH,
+ WifiIcons.QS_WIFI_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH,
+ WifiIcons.WIFI_NO_NETWORK,
+ WifiIcons.QS_WIFI_NO_NETWORK,
+ WifiIcons.WIFI_NO_NETWORK,
+ WifiIcons.QS_WIFI_NO_NETWORK,
+ AccessibilityContentDescriptions.WIFI_NO_CONNECTION
+ );
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 6d109ac..4954286 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -25,12 +25,15 @@
import android.net.NetworkCapabilities;
import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
+import android.text.Html;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.SignalIcon.IconGroup;
+import com.android.settingslib.SignalIcon.MobileIconGroup;
import com.android.settingslib.SignalIcon.State;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.wifi.WifiStatusTracker;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -42,6 +45,9 @@
SignalController<WifiSignalController.WifiState, IconGroup> {
private final boolean mHasMobileDataFeature;
private final WifiStatusTracker mWifiTracker;
+ private final IconGroup mUnmergedWifiIconGroup = WifiIcons.UNMERGED_WIFI;
+ private final MobileIconGroup mCarrierMergedWifiIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
+ private final WifiManager mWifiManager;
public WifiSignalController(Context context, boolean hasMobileDataFeature,
CallbackHandler callbackHandler, NetworkControllerImpl networkController,
@@ -49,6 +55,7 @@
NetworkScoreManager networkScoreManager) {
super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
callbackHandler, networkController);
+ mWifiManager = wifiManager;
mWifiTracker = new WifiStatusTracker(mContext, wifiManager, networkScoreManager,
connectivityManager, this::handleStatusUpdated);
mWifiTracker.setListening(true);
@@ -57,18 +64,7 @@
wifiManager.registerTrafficStateCallback(context.getMainExecutor(),
new WifiTrafficStateCallback());
}
- // WiFi only has one state.
- mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
- "Wi-Fi Icons",
- WifiIcons.WIFI_SIGNAL_STRENGTH,
- WifiIcons.QS_WIFI_SIGNAL_STRENGTH,
- AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH,
- WifiIcons.WIFI_NO_NETWORK,
- WifiIcons.QS_WIFI_NO_NETWORK,
- WifiIcons.WIFI_NO_NETWORK,
- WifiIcons.QS_WIFI_NO_NETWORK,
- AccessibilityContentDescriptions.WIFI_NO_CONNECTION
- );
+ mCurrentState.iconGroup = mLastState.iconGroup = mUnmergedWifiIconGroup;
}
@Override
@@ -82,6 +78,14 @@
@Override
public void notifyListeners(SignalCallback callback) {
+ if (mCurrentState.isCarrierMerged) {
+ notifyListenersForCarrierWifi(callback);
+ } else {
+ notifyListenersForNonCarrierWifi(callback);
+ }
+ }
+
+ private void notifyListenersForNonCarrierWifi(SignalCallback callback) {
// only show wifi in the cluster if connected or if wifi-only
boolean visibleWhenEnabled = mContext.getResources().getBoolean(
R.bool.config_showWifiIndicatorWhenEnabled);
@@ -104,14 +108,49 @@
wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
}
- private void copyWifiStates() {
- mCurrentState.enabled = mWifiTracker.enabled;
- mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
- mCurrentState.connected = mWifiTracker.connected;
- mCurrentState.ssid = mWifiTracker.ssid;
- mCurrentState.rssi = mWifiTracker.rssi;
- mCurrentState.level = mWifiTracker.level;
- mCurrentState.statusLabel = mWifiTracker.statusLabel;
+ private void notifyListenersForCarrierWifi(SignalCallback callback) {
+ MobileIconGroup icons = mCarrierMergedWifiIconGroup;
+ String contentDescription = getTextIfExists(getContentDescription()).toString();
+ CharSequence dataContentDescriptionHtml = getTextIfExists(icons.dataContentDescription);
+
+ CharSequence dataContentDescription = Html.fromHtml(
+ dataContentDescriptionHtml.toString(), 0).toString();
+ if (mCurrentState.inetCondition == 0) {
+ dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
+ }
+ boolean qsVisible = mCurrentState.enabled
+ && (mCurrentState.connected && mCurrentState.inetCondition == 1);
+
+ IconState statusIcon =
+ new IconState(qsVisible, getCurrentIconIdForCarrierWifi(), contentDescription);
+ int qsTypeIcon = mCurrentState.connected ? icons.qsDataType : 0;
+ int typeIcon = mCurrentState.connected ? icons.dataType : 0;
+ IconState qsIcon = new IconState(
+ mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), contentDescription);
+ CharSequence description = mNetworkController.getMobileDataNetworkName();
+ callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
+ mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
+ dataContentDescriptionHtml, description, icons.isWide,
+ mCurrentState.subId, /* roaming= */ false);
+ }
+
+ private int getCurrentIconIdForCarrierWifi() {
+ int level = mCurrentState.level;
+ // The WiFi signal level returned by WifiManager#calculateSignalLevel start from 0, so
+ // WifiManager#getMaxSignalLevel + 1 represents the total level buckets count.
+ int totalLevel = mWifiManager.getMaxSignalLevel() + 1;
+ boolean noInternet = mCurrentState.inetCondition == 0;
+ if (mCurrentState.connected) {
+ return SignalDrawable.getState(level, totalLevel, noInternet);
+ } else if (mCurrentState.enabled) {
+ return SignalDrawable.getEmptyState(totalLevel);
+ } else {
+ return 0;
+ }
+ }
+
+ private int getQsCurrentIconIdForCarrierWifi() {
+ return getCurrentIconIdForCarrierWifi();
}
/**
@@ -137,6 +176,21 @@
notifyListenersIfNecessary();
}
+ private void copyWifiStates() {
+ mCurrentState.enabled = mWifiTracker.enabled;
+ mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
+ mCurrentState.connected = mWifiTracker.connected;
+ mCurrentState.ssid = mWifiTracker.ssid;
+ mCurrentState.rssi = mWifiTracker.rssi;
+ mCurrentState.level = mWifiTracker.level;
+ mCurrentState.statusLabel = mWifiTracker.statusLabel;
+ mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
+ mCurrentState.subId = mWifiTracker.subId;
+ mCurrentState.iconGroup =
+ mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup
+ : mUnmergedWifiIconGroup;
+ }
+
@VisibleForTesting
void setActivity(int wifiActivity) {
mCurrentState.activityIn = wifiActivity == DATA_ACTIVITY_INOUT
@@ -157,10 +211,12 @@
}
static class WifiState extends State {
- String ssid;
- boolean isTransient;
- boolean isDefault;
- String statusLabel;
+ public String ssid;
+ public boolean isTransient;
+ public boolean isDefault;
+ public String statusLabel;
+ public boolean isCarrierMerged;
+ public int subId;
@Override
public void copyFrom(State s) {
@@ -170,6 +226,8 @@
isTransient = state.isTransient;
isDefault = state.isDefault;
statusLabel = state.statusLabel;
+ isCarrierMerged = state.isCarrierMerged;
+ subId = state.subId;
}
@Override
@@ -178,7 +236,9 @@
builder.append(",ssid=").append(ssid)
.append(",isTransient=").append(isTransient)
.append(",isDefault=").append(isDefault)
- .append(",statusLabel=").append(statusLabel);
+ .append(",statusLabel=").append(statusLabel)
+ .append(",isCarrierMerged=").append(isCarrierMerged)
+ .append(",subId=").append(subId);
}
@Override
@@ -190,7 +250,9 @@
return Objects.equals(other.ssid, ssid)
&& other.isTransient == isTransient
&& other.isDefault == isDefault
- && TextUtils.equals(other.statusLabel, statusLabel);
+ && TextUtils.equals(other.statusLabel, statusLabel)
+ && other.isCarrierMerged == isCarrierMerged
+ && other.subId == subId;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index 1049548..1d3f26e7 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -17,24 +17,22 @@
package com.android.systemui.wmshell;
import android.content.Context;
-import android.os.Handler;
import android.view.IWindowManager;
import com.android.systemui.dagger.WMSingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.Transitions;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
-import java.util.concurrent.Executor;
-
import dagger.Module;
import dagger.Provides;
@@ -47,9 +45,9 @@
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, @Main Executor mainExecutor,
+ DisplayController displayController, @ShellMainThread ShellExecutor shellMainExecutor,
TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, mainExecutor,
+ return new DisplayImeController(wmService, displayController, shellMainExecutor,
transactionPool);
}
@@ -57,12 +55,12 @@
@Provides
static LegacySplitScreen provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
- DisplayImeController displayImeController, @Main Handler handler,
- TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue, TaskStackListenerImpl taskStackListener,
- Transitions transitions) {
+ DisplayImeController displayImeController, TransactionPool transactionPool,
+ ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
+ TaskStackListenerImpl taskStackListener, Transitions transitions,
+ @ShellMainThread ShellExecutor mainExecutor) {
return new LegacySplitScreenController(context, displayController, systemWindows,
- displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue,
- taskStackListener, transitions);
+ displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
+ taskStackListener, transitions, mainExecutor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 88cbddd..b0bb3fd 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -36,8 +36,12 @@
import com.android.wm.shell.FullscreenTaskListener;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellCommandHandler;
+import com.android.wm.shell.ShellCommandHandlerImpl;
import com.android.wm.shell.ShellInit;
+import com.android.wm.shell.ShellInitImpl;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewFactory;
+import com.android.wm.shell.TaskViewFactoryController;
import com.android.wm.shell.Transitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairs;
@@ -58,6 +62,7 @@
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
@@ -66,10 +71,8 @@
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipTouchHandler;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import java.util.Optional;
-import java.util.concurrent.TimeUnit;
import dagger.BindsOptionalOf;
import dagger.Module;
@@ -158,7 +161,7 @@
// Choreographer.getSfInstance() which returns a thread-local Choreographer instance
// that uses the SF vsync
handler.setProvider(new SfVsyncFrameCallbackProvider());
- }, 1, TimeUnit.SECONDS);
+ });
return handler;
} catch (InterruptedException e) {
throw new RuntimeException("Failed to initialize SfVsync animation handler in 1s", e);
@@ -173,14 +176,14 @@
Optional<LegacySplitScreen> legacySplitScreenOptional,
Optional<AppPairs> appPairsOptional,
FullscreenTaskListener fullscreenTaskListener,
- Transitions transitions) {
- return new ShellInit(displayImeController,
+ @ShellMainThread ShellExecutor shellMainExecutor) {
+ return ShellInitImpl.create(displayImeController,
dragAndDropController,
shellTaskOrganizer,
legacySplitScreenOptional,
appPairsOptional,
fullscreenTaskListener,
- transitions);
+ shellMainExecutor);
}
/**
@@ -195,9 +198,11 @@
Optional<Pip> pipOptional,
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutout,
- Optional<AppPairs> appPairsOptional) {
- return Optional.of(new ShellCommandHandler(shellTaskOrganizer, legacySplitScreenOptional,
- pipOptional, oneHandedOptional, hideDisplayCutout, appPairsOptional));
+ Optional<AppPairs> appPairsOptional,
+ @ShellMainThread ShellExecutor shellMainExecutor) {
+ return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer,
+ legacySplitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout,
+ appPairsOptional, shellMainExecutor));
}
@WMSingleton
@@ -208,9 +213,9 @@
@WMSingleton
@Provides
- static DisplayController provideDisplayController(Context context, @Main Handler handler,
- IWindowManager wmService) {
- return new DisplayController(context, handler, wmService);
+ static DisplayController provideDisplayController(Context context,
+ IWindowManager wmService, @ShellMainThread ShellExecutor shellMainExecutor) {
+ return new DisplayController(context, wmService, shellMainExecutor);
}
@WMSingleton
@@ -269,9 +274,9 @@
@WMSingleton
@Provides
- static SyncTransactionQueue provideSyncTransactionQueue(@Main Handler handler,
- TransactionPool pool) {
- return new SyncTransactionQueue(pool, handler);
+ static SyncTransactionQueue provideSyncTransactionQueue(TransactionPool pool,
+ @ShellMainThread ShellExecutor shellMainExecutor) {
+ return new SyncTransactionQueue(pool, shellMainExecutor);
}
@WMSingleton
@@ -288,10 +293,12 @@
return new RootTaskDisplayAreaOrganizer(mainExecutor, context);
}
+ // We currently dedupe multiple messages, so we use the shell main handler directly
@WMSingleton
@Provides
- static TaskStackListenerImpl providerTaskStackListenerImpl(@Main Handler handler) {
- return new TaskStackListenerImpl(handler);
+ static TaskStackListenerImpl providerTaskStackListenerImpl(
+ @ShellMainThread Handler shellMainHandler) {
+ return new TaskStackListenerImpl(shellMainHandler);
}
@BindsOptionalOf
@@ -309,11 +316,12 @@
WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps,
UiEventLogger uiEventLogger,
- @Main Handler mainHandler,
- ShellTaskOrganizer organizer) {
+ ShellTaskOrganizer organizer,
+ @ShellMainThread ShellExecutor shellMainExecutor) {
return Optional.of(BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
- windowManagerShellWrapper, launcherApps, uiEventLogger, mainHandler, organizer));
+ windowManagerShellWrapper, launcherApps, uiEventLogger, organizer,
+ shellMainExecutor));
}
@WMSingleton
@@ -335,6 +343,14 @@
@WMSingleton
@Provides
+ static Optional<TaskViewFactory> provideTaskViewFactory(ShellTaskOrganizer shellTaskOrganizer,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return Optional.of(new TaskViewFactoryController(shellTaskOrganizer, mainExecutor)
+ .getTaskViewFactory());
+ }
+
+ @WMSingleton
+ @Provides
static FullscreenTaskListener provideFullscreenTaskListener(
SyncTransactionQueue syncQueue) {
return new FullscreenTaskListener(syncQueue);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 9ec7657..509419e 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -65,9 +65,9 @@
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, @Main Executor mainExecutor,
+ DisplayController displayController, @ShellMainThread ShellExecutor shellMainExecutor,
TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, mainExecutor,
+ return new DisplayImeController(wmService, displayController, shellMainExecutor,
transactionPool);
}
@@ -75,13 +75,13 @@
@Provides
static LegacySplitScreen provideLegacySplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
- DisplayImeController displayImeController, @Main Handler handler,
- TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue, TaskStackListenerImpl taskStackListener,
- Transitions transitions) {
+ DisplayImeController displayImeController, TransactionPool transactionPool,
+ ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
+ TaskStackListenerImpl taskStackListener, Transitions transitions,
+ @ShellMainThread ShellExecutor mainExecutor) {
return new LegacySplitScreenController(context, displayController, systemWindows,
- displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue,
- taskStackListener, transitions);
+ displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
+ taskStackListener, transitions, mainExecutor);
}
@WMSingleton
diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceTileTest.java
similarity index 97%
rename from core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceTileTest.java
index 1d83003..bd6167b 100644
--- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceTileTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.people;
+package com.android.systemui.people;
import static com.google.common.truth.Truth.assertThat;
@@ -39,6 +39,9 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.PeopleSpaceTile;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,9 +50,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class PeopleSpaceTileTest {
+public class PeopleSpaceTileTest extends SysuiTestCase {
- private Context mContext;
private final Drawable mDrawable = new ColorDrawable(Color.BLUE);
private final Icon mIcon = PeopleSpaceTile.convertDrawableToIcon(mDrawable);
@@ -58,7 +60,6 @@
@Before
public void setUp() {
- mContext = InstrumentationRegistry.getContext();
MockitoAnnotations.initMocks(this);
when(mLauncherApps.getShortcutIconDrawable(any(), eq(0))).thenReturn(mDrawable);
}
@@ -131,7 +132,7 @@
PeopleSpaceTile tile = new PeopleSpaceTile.Builder(
new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build();
// Automatically added by creating a ShortcutInfo.
- assertThat(tile.getPackageName()).isEqualTo("com.android.frameworks.coretests");
+ assertThat(tile.getPackageName()).isEqualTo("com.android.systemui.tests");
tile = new PeopleSpaceTile.Builder(
new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).setPackageName(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index 644373c..e6cc107 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -32,7 +32,6 @@
import android.app.Notification;
import android.app.Person;
-import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.Context;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index df07b12..63ea7dd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -35,7 +35,6 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.Person;
-import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -58,6 +57,7 @@
import com.android.internal.appwidget.IAppWidgetService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.PeopleSpaceTile;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.SbnBuilder;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 230aeab..ccc2eb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -89,6 +89,8 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
@@ -100,6 +102,7 @@
import com.android.wm.shell.bubbles.BubbleStackView;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
import com.google.common.collect.ImmutableList;
@@ -203,6 +206,8 @@
private WindowManagerShellWrapper mWindowManagerShellWrapper;
@Mock
private BubbleLogger mBubbleLogger;
+ @Mock
+ private ShellTaskOrganizer mShellTaskOrganizer;
private TestableBubblePositioner mPositioner;
@@ -268,6 +273,7 @@
);
when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
+ when(mShellTaskOrganizer.getExecutor()).thenReturn(new FakeExecutor(new FakeSystemClock()));
mBubbleController = new TestableBubbleController(
mContext,
mBubbleData,
@@ -278,9 +284,9 @@
mWindowManagerShellWrapper,
mLauncherApps,
mBubbleLogger,
- mock(Handler.class),
- mock(ShellTaskOrganizer.class),
- mPositioner);
+ mShellTaskOrganizer,
+ mPositioner,
+ mock(ShellExecutor.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
mBubblesManager = new BubblesManager(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index bbcc30a..00f4e3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -83,6 +83,8 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.BubbleData;
@@ -93,6 +95,7 @@
import com.android.wm.shell.bubbles.BubbleStackView;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Ignore;
@@ -185,6 +188,8 @@
private WindowManagerShellWrapper mWindowManagerShellWrapper;
@Mock
private BubbleLogger mBubbleLogger;
+ @Mock
+ private ShellTaskOrganizer mShellTaskOrganizer;
private TestableBubblePositioner mPositioner;
@@ -236,6 +241,7 @@
mock(Handler.class)
);
when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
+ when(mShellTaskOrganizer.getExecutor()).thenReturn(new FakeExecutor(new FakeSystemClock()));
mBubbleController = new TestableBubbleController(
mContext,
mBubbleData,
@@ -246,9 +252,9 @@
mWindowManagerShellWrapper,
mLauncherApps,
mBubbleLogger,
- mock(Handler.class),
- mock(ShellTaskOrganizer.class),
- mPositioner);
+ mShellTaskOrganizer,
+ mPositioner,
+ mock(ShellExecutor.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
mBubblesManager = new BubblesManager(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index fd39b6e..3f918e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -30,6 +30,7 @@
import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
/**
* Testable BubbleController subclass that immediately synchronizes surfaces.
@@ -46,12 +47,12 @@
WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps,
BubbleLogger bubbleLogger,
- Handler mainHandler,
ShellTaskOrganizer shellTaskOrganizer,
- BubblePositioner positioner) {
+ BubblePositioner positioner,
+ ShellExecutor shellMainExecutor) {
super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- bubbleLogger, mainHandler, shellTaskOrganizer, positioner);
+ bubbleLogger, shellTaskOrganizer, positioner, shellMainExecutor);
setInflateSynchronously(true);
}
}
diff --git a/packages/overlays/OneHandedModeGesturalOverlay/res/values/dimens.xml b/packages/overlays/OneHandedModeGesturalOverlay/res/values/dimens.xml
index 7382565..3986119 100644
--- a/packages/overlays/OneHandedModeGesturalOverlay/res/values/dimens.xml
+++ b/packages/overlays/OneHandedModeGesturalOverlay/res/values/dimens.xml
@@ -18,5 +18,5 @@
-->
<resources>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">80dp</dimen>
+ <dimen name="navigation_bar_gesture_larger_height">80dp</dimen>
</resources>
diff --git a/proto/src/OWNERS b/proto/src/OWNERS
index e7ddf86..b456ba6 100644
--- a/proto/src/OWNERS
+++ b/proto/src/OWNERS
@@ -1,2 +1,3 @@
per-file gnss.proto = file:/services/core/java/com/android/server/location/OWNERS
per-file wifi.proto = file:/wifi/OWNERS
+per-file camera.proto = file:/services/core/java/com/android/server/camera/OWNERS
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 8a27acc..9d02835 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -919,9 +919,10 @@
break;
case "associate": {
+ int userId = getNextArgInt();
String pkg = getNextArgRequired();
String address = getNextArgRequired();
- addAssociation(new Association(getNextArgInt(), address, pkg, null, false));
+ addAssociation(new Association(userId, address, pkg, null, false));
}
break;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6ce334b..019d8c5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -141,7 +141,7 @@
"capture_state_listener-aidl-java",
"dnsresolver_aidl_interface-java",
"icu4j_calendar_astronomer",
- "netd_aidl_interfaces-platform-java",
+ "netd-client",
"overlayable_policy_aidl-java",
"SurfaceFlingerProperties",
"com.android.sysprop.watchdog",
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 01021345..9ba71dc 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -20,12 +20,14 @@
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.UserHandle.USER_SYSTEM;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -49,12 +51,15 @@
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
+import android.os.UserHandle;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.service.SensorPrivacyServiceDumpProto;
+import android.service.SensorPrivacyUserProto;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Log;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -63,9 +68,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.pm.UserManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -84,9 +91,19 @@
private static final String TAG = "SensorPrivacyService";
+ /** Version number indicating compatibility parsing the persisted file */
+ private static final int CURRENT_PERSISTENCE_VERSION = 1;
+ /** Version number indicating the persisted data needs upgraded to match new internal data
+ * structures and features */
+ private static final int CURRENT_VERSION = 1;
+
private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy.xml";
private static final String XML_TAG_SENSOR_PRIVACY = "sensor-privacy";
+ private static final String XML_TAG_USER = "user";
private static final String XML_TAG_INDIVIDUAL_SENSOR_PRIVACY = "individual-sensor-privacy";
+ private static final String XML_ATTRIBUTE_ID = "id";
+ private static final String XML_ATTRIBUTE_PERSISTENCE_VERSION = "persistence-version";
+ private static final String XML_ATTRIBUTE_VERSION = "version";
private static final String XML_ATTRIBUTE_ENABLED = "enabled";
private static final String XML_ATTRIBUTE_SENSOR = "sensor";
@@ -96,10 +113,18 @@
private static final String EXTRA_SENSOR = SensorPrivacyService.class.getName()
+ ".extra.sensor";
+ // These are associated with fields that existed for older persisted versions of files
+ private static final int VER0_ENABLED = 0;
+ private static final int VER0_INDIVIDUAL_ENABLED = 1;
+ private static final int VER1_ENABLED = 0;
+ private static final int VER1_INDIVIDUAL_ENABLED = 1;
+
private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl;
+ private final UserManagerInternal mUserManagerInternal;
public SensorPrivacyService(Context context) {
super(context);
+ mUserManagerInternal = getLocalService(UserManagerInternal.class);
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl(context);
}
@@ -117,8 +142,9 @@
@GuardedBy("mLock")
private final AtomicFile mAtomicFile;
@GuardedBy("mLock")
- private boolean mEnabled;
- private SparseBooleanArray mIndividualEnabled = new SparseBooleanArray();
+ private SparseBooleanArray mEnabled = new SparseBooleanArray();
+ @GuardedBy("mLock")
+ private SparseArray<SparseBooleanArray> mIndividualEnabled = new SparseArray<>();
SensorPrivacyServiceImpl(Context context) {
mContext = context;
@@ -127,7 +153,9 @@
SENSOR_PRIVACY_XML_FILE);
mAtomicFile = new AtomicFile(sensorPrivacyFile);
synchronized (mLock) {
- readPersistedSensorPrivacyStateLocked();
+ if (readPersistedSensorPrivacyStateLocked()) {
+ persistSensorPrivacyStateLocked();
+ }
}
int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_CAMERA};
@@ -138,7 +166,8 @@
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- setIndividualSensorPrivacy(intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
+ setIndividualSensorPrivacy(intent.getIntExtra(Intent.EXTRA_USER_ID, -1),
+ intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
}
}, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY));
}
@@ -174,7 +203,8 @@
* @param sensor The sensor that is attempting to be used
*/
private void onSensorUseStarted(int uid, String packageName, int sensor) {
- if (!isIndividualSensorPrivacyEnabled(sensor)) {
+ int userId = UserHandle.getUserId(uid);
+ if (!isIndividualSensorPrivacyEnabled(userId, sensor)) {
return;
}
@@ -216,7 +246,8 @@
PendingIntent.getBroadcast(mContext, sensor,
new Intent(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY)
.setPackage(mContext.getPackageName())
- .putExtra(EXTRA_SENSOR, sensor),
+ .putExtra(EXTRA_SENSOR, sensor)
+ .putExtra(Intent.EXTRA_USER_ID, userId),
PendingIntent.FLAG_IMMUTABLE
| PendingIntent.FLAG_UPDATE_CURRENT))
.build())
@@ -229,18 +260,27 @@
*/
@Override
public void setSensorPrivacy(boolean enable) {
+ // Keep the state consistent between all users to make it a single global state
+ forAllUsers(userId -> setSensorPrivacy(userId, enable));
+ }
+
+ private void setSensorPrivacy(@UserIdInt int userId, boolean enable) {
enforceSensorPrivacyPermission();
synchronized (mLock) {
- mEnabled = enable;
+ mEnabled.put(userId, enable);
persistSensorPrivacyStateLocked();
}
mHandler.onSensorPrivacyChanged(enable);
}
- public void setIndividualSensorPrivacy(int sensor, boolean enable) {
+ @Override
+ public void setIndividualSensorPrivacy(@UserIdInt int userId, int sensor, boolean enable) {
enforceSensorPrivacyPermission();
synchronized (mLock) {
- mIndividualEnabled.put(sensor, enable);
+ SparseBooleanArray userIndividualEnabled = mIndividualEnabled.get(userId,
+ new SparseBooleanArray());
+ userIndividualEnabled.put(sensor, enable);
+ mIndividualEnabled.put(userId, userIndividualEnabled);
if (!enable) {
// Remove any notifications prompting the user to disable sensory privacy
@@ -249,9 +289,20 @@
notificationManager.cancel(sensor);
}
-
persistSensorPrivacyState();
}
+ mHandler.onSensorPrivacyChanged(userId, sensor, enable);
+ }
+
+ @Override
+ public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId, int sensor,
+ boolean enable) {
+ int parentId = mUserManagerInternal.getProfileParentId(userId);
+ forAllUsers(userId2 -> {
+ if (parentId == mUserManagerInternal.getProfileParentId(userId2)) {
+ setIndividualSensorPrivacy(userId2, sensor, enable);
+ }
+ });
}
/**
@@ -273,53 +324,179 @@
*/
@Override
public boolean isSensorPrivacyEnabled() {
+ return isSensorPrivacyEnabled(USER_SYSTEM);
+ }
+
+ private boolean isSensorPrivacyEnabled(@UserIdInt int userId) {
synchronized (mLock) {
- return mEnabled;
+ return mEnabled.get(userId, false);
}
}
@Override
- public boolean isIndividualSensorPrivacyEnabled(int sensor) {
+ public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
synchronized (mLock) {
- return mIndividualEnabled.get(sensor, false);
+ SparseBooleanArray states = mIndividualEnabled.get(userId);
+ if (states == null) {
+ return false;
+ }
+ return states.get(sensor, false);
}
}
/**
* Returns the state of sensor privacy from persistent storage.
*/
- private void readPersistedSensorPrivacyStateLocked() {
+ private boolean readPersistedSensorPrivacyStateLocked() {
// if the file does not exist then sensor privacy has not yet been enabled on
// the device.
- if (!mAtomicFile.exists()) {
- return;
- }
- try (FileInputStream inputStream = mAtomicFile.openRead()) {
- TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
- XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
- parser.next();
- mEnabled = parser.getAttributeBoolean(null, XML_ATTRIBUTE_ENABLED, false);
- XmlUtils.nextElement(parser);
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- String tagName = parser.getName();
- if (XML_TAG_INDIVIDUAL_SENSOR_PRIVACY.equals(tagName)) {
- int sensor = XmlUtils.readIntAttribute(parser, XML_ATTRIBUTE_SENSOR);
- boolean enabled = XmlUtils.readBooleanAttribute(parser,
- XML_ATTRIBUTE_ENABLED);
- mIndividualEnabled.put(sensor, enabled);
- XmlUtils.skipCurrentTag(parser);
- } else {
+ SparseArray<Object> map = new SparseArray<>();
+ int version = -1;
+
+ if (mAtomicFile.exists()) {
+ try (FileInputStream inputStream = mAtomicFile.openRead()) {
+ TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
+ XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
+ final int persistenceVersion = parser.getAttributeInt(null,
+ XML_ATTRIBUTE_PERSISTENCE_VERSION, 0);
+
+ // Use inline string literals for xml tags/attrs when parsing old versions since
+ // these should never be changed even with refactorings.
+ if (persistenceVersion == 0) {
+ boolean enabled = parser.getAttributeBoolean(null, "enabled", false);
+ SparseBooleanArray individualEnabled = new SparseBooleanArray();
+ version = 0;
+
XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if ("individual-sensor-privacy".equals(tagName)) {
+ int sensor = XmlUtils.readIntAttribute(parser, "sensor");
+ boolean indEnabled = XmlUtils.readBooleanAttribute(parser,
+ "enabled");
+ individualEnabled.put(sensor, indEnabled);
+ XmlUtils.skipCurrentTag(parser);
+ } else {
+ XmlUtils.nextElement(parser);
+ }
+ }
+ map.put(VER0_ENABLED, enabled);
+ map.put(VER0_INDIVIDUAL_ENABLED, individualEnabled);
+ } else if (persistenceVersion == CURRENT_PERSISTENCE_VERSION) {
+ SparseBooleanArray enabled = new SparseBooleanArray();
+ SparseArray<SparseBooleanArray> individualEnabled = new SparseArray<>();
+ version = parser.getAttributeInt(null,
+ XML_ATTRIBUTE_VERSION, 1);
+
+ int currentUserId = -1;
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ XmlUtils.nextElement(parser);
+ String tagName = parser.getName();
+ if (XML_TAG_USER.equals(tagName)) {
+ currentUserId = parser.getAttributeInt(null, XML_ATTRIBUTE_ID);
+ boolean isEnabled = parser.getAttributeBoolean(null,
+ XML_ATTRIBUTE_ENABLED);
+ if (enabled.indexOfKey(currentUserId) >= 0) {
+ Log.e(TAG, "User listed multiple times in file.",
+ new RuntimeException());
+ mAtomicFile.delete();
+ version = -1;
+ break;
+ }
+
+ if (mUserManagerInternal.getUserInfo(currentUserId) == null) {
+ // User may no longer exist, skip this user
+ currentUserId = -1;
+ continue;
+ }
+
+ enabled.put(currentUserId, isEnabled);
+ }
+ if (XML_TAG_INDIVIDUAL_SENSOR_PRIVACY.equals(tagName)) {
+ if (mUserManagerInternal.getUserInfo(currentUserId) == null) {
+ // User may no longer exist or isn't set
+ continue;
+ }
+ int sensor = parser.getAttributeIndex(null, XML_ATTRIBUTE_SENSOR);
+ boolean isEnabled = parser.getAttributeBoolean(null,
+ XML_ATTRIBUTE_ENABLED);
+ SparseBooleanArray userIndividualEnabled = individualEnabled.get(
+ currentUserId, new SparseBooleanArray());
+
+ userIndividualEnabled.put(sensor, isEnabled);
+ individualEnabled.put(currentUserId, userIndividualEnabled);
+ }
+ }
+
+ map.put(VER1_ENABLED, enabled);
+ map.put(VER1_INDIVIDUAL_ENABLED, individualEnabled);
+ } else {
+ Log.e(TAG, "Unknown persistence version: " + persistenceVersion
+ + ". Deleting.",
+ new RuntimeException());
+ mAtomicFile.delete();
+ version = -1;
+ }
+
+ } catch (IOException | XmlPullParserException e) {
+ Log.e(TAG, "Caught an exception reading the state from storage: ", e);
+ // Delete the file to prevent the same error on subsequent calls and assume
+ // sensor privacy is not enabled.
+ mAtomicFile.delete();
+ version = -1;
+ }
+ }
+
+ return upgradeAndInit(version, map);
+ }
+
+ private boolean upgradeAndInit(int version, SparseArray map) {
+ if (version == -1) {
+ // New file, default state for current version goes here.
+ mEnabled = new SparseBooleanArray();
+ mIndividualEnabled = new SparseArray<>();
+ forAllUsers(userId -> mEnabled.put(userId, false));
+ forAllUsers(userId -> mIndividualEnabled.put(userId, new SparseBooleanArray()));
+ return true;
+ }
+ boolean upgraded = false;
+ final int[] users = getLocalService(UserManagerInternal.class).getUserIds();
+ if (version == 0) {
+ final boolean enabled = (boolean) map.get(VER0_ENABLED);
+ final SparseBooleanArray individualEnabled =
+ (SparseBooleanArray) map.get(VER0_INDIVIDUAL_ENABLED);
+
+ final SparseBooleanArray perUserEnabled = new SparseBooleanArray();
+ final SparseArray<SparseBooleanArray> perUserIndividualEnabled =
+ new SparseArray<>();
+
+ // Copy global state to each user
+ for (int i = 0; i < users.length; i++) {
+ int user = users[i];
+ perUserEnabled.put(user, enabled);
+ SparseBooleanArray userIndividualSensorEnabled = new SparseBooleanArray();
+ perUserIndividualEnabled.put(user, userIndividualSensorEnabled);
+ for (int j = 0; j < individualEnabled.size(); j++) {
+ final int sensor = individualEnabled.keyAt(j);
+ final boolean isSensorEnabled = individualEnabled.valueAt(j);
+ userIndividualSensorEnabled.put(sensor, isSensorEnabled);
}
}
- } catch (IOException | XmlPullParserException e) {
- Log.e(TAG, "Caught an exception reading the state from storage: ", e);
- // Delete the file to prevent the same error on subsequent calls and assume sensor
- // privacy is not enabled.
- mAtomicFile.delete();
+ map.clear();
+ map.put(VER1_ENABLED, perUserEnabled);
+ map.put(VER1_INDIVIDUAL_ENABLED, perUserIndividualEnabled);
+
+ version = 1;
+ upgraded = true;
}
+ if (version == CURRENT_VERSION) {
+ mEnabled = (SparseBooleanArray) map.get(VER1_ENABLED);
+ mIndividualEnabled =
+ (SparseArray<SparseBooleanArray>) map.get(VER1_INDIVIDUAL_ENABLED);
+ }
+ return upgraded;
}
/**
@@ -338,16 +515,29 @@
TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
serializer.startDocument(null, true);
serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
- serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, mEnabled);
- int numIndividual = mIndividualEnabled.size();
- for (int i = 0; i < numIndividual; i++) {
- serializer.startTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY);
- int sensor = mIndividualEnabled.keyAt(i);
- boolean enabled = mIndividualEnabled.valueAt(i);
- serializer.attributeInt(null, XML_ATTRIBUTE_SENSOR, sensor);
- serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enabled);
- serializer.endTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY);
- }
+ serializer.attributeInt(
+ null, XML_ATTRIBUTE_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION);
+ serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, CURRENT_VERSION);
+ forAllUsers(userId -> {
+ serializer.startTag(null, XML_TAG_USER);
+ serializer.attributeInt(null, XML_ATTRIBUTE_ID, userId);
+ serializer.attributeBoolean(
+ null, XML_ATTRIBUTE_ENABLED, isSensorPrivacyEnabled(userId));
+
+ SparseBooleanArray individualEnabled =
+ mIndividualEnabled.get(userId, new SparseBooleanArray());
+ int numIndividual = individualEnabled.size();
+ for (int i = 0; i < numIndividual; i++) {
+ serializer.startTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY);
+ int sensor = individualEnabled.keyAt(i);
+ boolean enabled = individualEnabled.valueAt(i);
+ serializer.attributeInt(null, XML_ATTRIBUTE_SENSOR, sensor);
+ serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enabled);
+ serializer.endTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY);
+ }
+ serializer.endTag(null, XML_TAG_USER);
+
+ });
serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
serializer.endDocument();
mAtomicFile.finishWrite(outputStream);
@@ -369,6 +559,18 @@
}
/**
+ * Registers a listener to be notified when the sensor privacy state changes.
+ */
+ @Override
+ public void addIndividualSensorPrivacyListener(int userId, int sensor,
+ ISensorPrivacyListener listener) {
+ if (listener == null) {
+ throw new NullPointerException("listener cannot be null");
+ }
+ mHandler.addListener(userId, sensor, listener);
+ }
+
+ /**
* Unregisters a listener from sensor privacy state change notifications.
*/
@Override
@@ -422,22 +624,32 @@
*/
private void dump(@NonNull DualDumpOutputStream dumpStream) {
synchronized (mLock) {
- dumpStream.write("is_enabled", SensorPrivacyServiceDumpProto.IS_ENABLED, mEnabled);
- int numIndividualEnabled = mIndividualEnabled.size();
- for (int i = 0; i < numIndividualEnabled; i++) {
- long token = dumpStream.start("individual_enabled_sensor",
- SensorPrivacyServiceDumpProto.INDIVIDUAL_ENABLED_SENSOR);
+ forAllUsers(userId -> {
+ long userToken = dumpStream.start("users", SensorPrivacyServiceDumpProto.USER);
+ dumpStream.write("user_id", SensorPrivacyUserProto.USER_ID, userId);
+ dumpStream.write("is_enabled", SensorPrivacyUserProto.IS_ENABLED,
+ mEnabled.get(userId, false));
- dumpStream.write("sensor",
- SensorPrivacyIndividualEnabledSensorProto.SENSOR,
- mIndividualEnabled.keyAt(i));
- dumpStream.write("is_enabled",
- SensorPrivacyIndividualEnabledSensorProto.IS_ENABLED,
- mIndividualEnabled.valueAt(i));
+ SparseBooleanArray individualEnabled = mIndividualEnabled.get(userId);
+ if (individualEnabled != null) {
+ int numIndividualEnabled = individualEnabled.size();
+ for (int i = 0; i < numIndividualEnabled; i++) {
+ long individualToken = dumpStream.start("individual_enabled_sensor",
+ SensorPrivacyUserProto.INDIVIDUAL_ENABLED_SENSOR);
- dumpStream.end(token);
- }
+ dumpStream.write("sensor",
+ SensorPrivacyIndividualEnabledSensorProto.SENSOR,
+ individualEnabled.keyAt(i));
+ dumpStream.write("is_enabled",
+ SensorPrivacyIndividualEnabledSensorProto.IS_ENABLED,
+ individualEnabled.valueAt(i));
+
+ dumpStream.end(individualToken);
+ }
+ }
+ dumpStream.end(userToken);
+ });
}
dumpStream.flush();
@@ -477,30 +689,32 @@
return handleDefaultCommands(cmd);
}
+ int userId = Integer.parseInt(getNextArgRequired());
+
final PrintWriter pw = getOutPrintWriter();
switch (cmd) {
case "enable" : {
- int sensor = sensorStrToId(getNextArg());
+ int sensor = sensorStrToId(getNextArgRequired());
if (sensor == UNKNOWN) {
pw.println("Invalid sensor");
return -1;
}
- setIndividualSensorPrivacy(sensor, true);
+ setIndividualSensorPrivacy(userId, sensor, true);
}
break;
case "disable" : {
- int sensor = sensorStrToId(getNextArg());
+ int sensor = sensorStrToId(getNextArgRequired());
if (sensor == UNKNOWN) {
pw.println("Invalid sensor");
return -1;
}
- setIndividualSensorPrivacy(sensor, false);
+ setIndividualSensorPrivacy(userId, sensor, false);
}
break;
case "reset": {
- int sensor = sensorStrToId(getNextArg());
+ int sensor = sensorStrToId(getNextArgRequired());
if (sensor == UNKNOWN) {
pw.println("Invalid sensor");
return -1;
@@ -509,7 +723,11 @@
enforceSensorPrivacyPermission();
synchronized (mLock) {
- mIndividualEnabled.delete(sensor);
+ SparseBooleanArray individualEnabled =
+ mIndividualEnabled.get(userId);
+ if (individualEnabled != null) {
+ individualEnabled.delete(sensor);
+ }
persistSensorPrivacyState();
}
}
@@ -530,13 +748,13 @@
pw.println(" help");
pw.println(" Print this help text.");
pw.println("");
- pw.println(" enable SENSOR");
+ pw.println(" enable USER_ID SENSOR");
pw.println(" Enable privacy for a certain sensor.");
pw.println("");
- pw.println(" disable SENSOR");
+ pw.println(" disable USER_ID SENSOR");
pw.println(" Disable privacy for a certain sensor.");
pw.println("");
- pw.println(" reset SENSOR");
+ pw.println(" reset USER_ID SENSOR");
pw.println(" Reset privacy state for a certain sensor.");
pw.println("");
}
@@ -555,6 +773,9 @@
@GuardedBy("mListenerLock")
private final RemoteCallbackList<ISensorPrivacyListener> mListeners =
new RemoteCallbackList<>();
+ @GuardedBy("mListenerLock")
+ private final SparseArray<SparseArray<RemoteCallbackList<ISensorPrivacyListener>>>
+ mIndividualSensorListeners = new SparseArray<>();
private final ArrayMap<ISensorPrivacyListener, DeathRecipient> mDeathRecipients;
private final Context mContext;
@@ -572,6 +793,14 @@
mSensorPrivacyServiceImpl));
}
+ public void onSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
+ sendMessage(PooledLambda.obtainMessage(SensorPrivacyHandler::handleSensorPrivacyChanged,
+ this, userId, sensor, enabled));
+ sendMessage(
+ PooledLambda.obtainMessage(SensorPrivacyServiceImpl::persistSensorPrivacyState,
+ mSensorPrivacyServiceImpl));
+ }
+
public void addListener(ISensorPrivacyListener listener) {
synchronized (mListenerLock) {
DeathRecipient deathRecipient = new DeathRecipient(listener);
@@ -580,6 +809,25 @@
}
}
+ public void addListener(int userId, int sensor, ISensorPrivacyListener listener) {
+ synchronized (mListenerLock) {
+ DeathRecipient deathRecipient = new DeathRecipient(listener);
+ mDeathRecipients.put(listener, deathRecipient);
+ SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
+ mIndividualSensorListeners.get(userId);
+ if (listenersForUser == null) {
+ listenersForUser = new SparseArray<>();
+ mIndividualSensorListeners.put(userId, listenersForUser);
+ }
+ RemoteCallbackList<ISensorPrivacyListener> listeners = listenersForUser.get(sensor);
+ if (listeners == null) {
+ listeners = new RemoteCallbackList<>();
+ listenersForUser.put(sensor, listeners);
+ }
+ listeners.register(listener);
+ }
+ }
+
public void removeListener(ISensorPrivacyListener listener) {
synchronized (mListenerLock) {
DeathRecipient deathRecipient = mDeathRecipients.remove(listener);
@@ -587,6 +835,12 @@
deathRecipient.destroy();
}
mListeners.unregister(listener);
+ for (int i = 0, numUsers = mIndividualSensorListeners.size(); i < numUsers; i++) {
+ for (int j = 0, numListeners = mIndividualSensorListeners.valueAt(i).size();
+ j < numListeners; j++) {
+ mIndividualSensorListeners.valueAt(i).valueAt(j).unregister(listener);
+ }
+ }
}
}
@@ -602,6 +856,28 @@
}
mListeners.finishBroadcast();
}
+
+ public void handleSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
+ SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
+ mIndividualSensorListeners.get(userId);
+ if (listenersForUser == null) {
+ return;
+ }
+ RemoteCallbackList<ISensorPrivacyListener> listeners = listenersForUser.get(sensor);
+ if (listeners == null) {
+ return;
+ }
+ final int count = listeners.beginBroadcast();
+ for (int i = 0; i < count; i++) {
+ ISensorPrivacyListener listener = listeners.getBroadcastItem(i);
+ try {
+ listener.onSensorPrivacyChanged(enabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", e);
+ }
+ }
+ listeners.finishBroadcast();
+ }
}
private final class DeathRecipient implements IBinder.DeathRecipient {
@@ -628,4 +904,11 @@
}
}
}
+
+ private void forAllUsers(FunctionalUtils.ThrowingConsumer<Integer> c) {
+ int[] userIds = mUserManagerInternal.getUserIds();
+ for (int i = 0; i < userIds.length; i++) {
+ c.accept(userIds[i]);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c951fd4..b0f2e24 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1716,7 +1716,7 @@
public StorageManagerService(Context context) {
sSelf = this;
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mContext = context;
mResolver = mContext.getContentResolver();
mCallbacks = new Callbacks(FgThread.get().getLooper());
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 74e3851..c191a78 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -16,13 +16,15 @@
package com.android.server;
+import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
+
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
+import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.NetworkProvider;
-import android.net.NetworkRequest;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.VcnConfig;
import android.os.Binder;
@@ -43,6 +45,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.TelephonySubscriptionTracker;
+import com.android.server.vcn.Vcn;
+import com.android.server.vcn.VcnContext;
+import com.android.server.vcn.VcnNetworkProvider;
import com.android.server.vcn.util.PersistableBundleUtils;
import java.io.IOException;
@@ -51,6 +57,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
/**
* VcnManagementService manages Virtual Carrier Network profiles and lifecycles.
@@ -115,6 +122,10 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";
+ // TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final long CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
+
/* Binder context for this service */
@NonNull private final Context mContext;
@NonNull private final Dependencies mDeps;
@@ -122,11 +133,23 @@
@NonNull private final Looper mLooper;
@NonNull private final Handler mHandler;
@NonNull private final VcnNetworkProvider mNetworkProvider;
+ @NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb;
+ @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
+ @NonNull private final VcnContext mVcnContext;
@GuardedBy("mLock")
@NonNull
private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>();
+ @GuardedBy("mLock")
+ @NonNull
+ private final Map<ParcelUuid, Vcn> mVcns = new ArrayMap<>();
+
+ @GuardedBy("mLock")
+ @NonNull
+ private TelephonySubscriptionSnapshot mLastSnapshot =
+ TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT;
+
@NonNull private final Object mLock = new Object();
@NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper;
@@ -139,8 +162,12 @@
mLooper = mDeps.getLooper();
mHandler = new Handler(mLooper);
mNetworkProvider = new VcnNetworkProvider(mContext, mLooper);
+ mTelephonySubscriptionTrackerCb = new VcnSubscriptionTrackerCallback();
+ mTelephonySubscriptionTracker = mDeps.newTelephonySubscriptionTracker(
+ mContext, mLooper, mTelephonySubscriptionTrackerCb);
mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
+ mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider);
// Run on handler to ensure I/O does not block system server startup
mHandler.post(() -> {
@@ -174,7 +201,10 @@
mConfigs.put(entry.getKey(), entry.getValue());
}
}
- // TODO: Trigger re-evaluation of active VCNs; start/stop VCNs as needed.
+
+ // Re-evaluate subscriptions, and start/stop VCNs. This starts with an empty
+ // snapshot, and therefore safe even before telephony subscriptions are loaded.
+ mTelephonySubscriptionTrackerCb.onNewSnapshot(mLastSnapshot);
}
}
});
@@ -203,6 +233,14 @@
return mHandlerThread.getLooper();
}
+ /** Creates a new VcnInstance using the provided configuration */
+ public TelephonySubscriptionTracker newTelephonySubscriptionTracker(
+ @NonNull Context context,
+ @NonNull Looper looper,
+ @NonNull TelephonySubscriptionTrackerCallback callback) {
+ return new TelephonySubscriptionTracker(context, new Handler(looper), callback);
+ }
+
/**
* Retrieves the caller's UID
*
@@ -225,12 +263,29 @@
newPersistableBundleLockingReadWriteHelper(@NonNull String path) {
return new PersistableBundleUtils.LockingReadWriteHelper(path);
}
+
+ /** Creates a new VcnContext */
+ public VcnContext newVcnContext(
+ @NonNull Context context,
+ @NonNull Looper looper,
+ @NonNull VcnNetworkProvider vcnNetworkProvider) {
+ return new VcnContext(context, looper, vcnNetworkProvider);
+ }
+
+ /** Creates a new Vcn instance using the provided configuration */
+ public Vcn newVcn(
+ @NonNull VcnContext vcnContext,
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull VcnConfig config) {
+ return new Vcn(vcnContext, subscriptionGroup, config);
+ }
}
/** Notifies the VcnManagementService that external dependencies can be set up. */
public void systemReady() {
mContext.getSystemService(ConnectivityManager.class)
.registerNetworkProvider(mNetworkProvider);
+ mTelephonySubscriptionTracker.register();
}
private void enforcePrimaryUser() {
@@ -277,27 +332,112 @@
"Carrier privilege required for subscription group to set VCN Config");
}
+ private class VcnSubscriptionTrackerCallback implements TelephonySubscriptionTrackerCallback {
+ /**
+ * Handles subscription group changes, as notified by {@link TelephonySubscriptionTracker}
+ *
+ * <p>Start any unstarted VCN instances
+ *
+ * @hide
+ */
+ public void onNewSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+ // Startup VCN instances
+ synchronized (mLock) {
+ mLastSnapshot = snapshot;
+
+ // Start any VCN instances as necessary
+ for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) {
+ if (snapshot.packageHasPermissionsForSubscriptionGroup(
+ entry.getKey(), entry.getValue().getProvisioningPackageName())) {
+ if (!mVcns.containsKey(entry.getKey())) {
+ startVcnLocked(entry.getKey(), entry.getValue());
+ }
+
+ // Cancel any scheduled teardowns for active subscriptions
+ mHandler.removeCallbacksAndMessages(mVcns.get(entry.getKey()));
+ }
+ }
+
+ // Schedule teardown of any VCN instances that have lost carrier privileges (after a
+ // delay)
+ for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
+ final VcnConfig config = mConfigs.get(entry.getKey());
+ if (config == null
+ || !snapshot.packageHasPermissionsForSubscriptionGroup(
+ entry.getKey(), config.getProvisioningPackageName())) {
+ final ParcelUuid uuidToTeardown = entry.getKey();
+ final Vcn instanceToTeardown = entry.getValue();
+
+ mHandler.postDelayed(() -> {
+ synchronized (mLock) {
+ // Guard against case where this is run after a old instance was
+ // torn down, and a new instance was started. Verify to ensure
+ // correct instance is torn down. This could happen as a result of a
+ // Carrier App manually removing/adding a VcnConfig.
+ if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
+ mVcns.remove(uuidToTeardown).teardownAsynchronously();
+ }
+ }
+ }, instanceToTeardown, CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ }
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
+ Slog.v(TAG, "Starting VCN config for subGrp: " + subscriptionGroup);
+
+ // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
+ // VCN.
+
+ final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config);
+ mVcns.put(subscriptionGroup, newInstance);
+ }
+
+ @GuardedBy("mLock")
+ private void startOrUpdateVcnLocked(
+ @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
+ Slog.v(TAG, "Starting or updating VCN config for subGrp: " + subscriptionGroup);
+
+ if (mVcns.containsKey(subscriptionGroup)) {
+ mVcns.get(subscriptionGroup).updateConfig(config);
+ } else {
+ startVcnLocked(subscriptionGroup, config);
+ }
+ }
+
/**
* Sets a VCN config for a given subscription group.
*
* <p>Implements the IVcnManagementService Binder interface.
*/
@Override
- public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
+ public void setVcnConfig(
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull VcnConfig config,
+ @NonNull String opPkgName) {
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
requireNonNull(config, "config was null");
+ requireNonNull(opPkgName, "opPkgName was null");
+ if (!config.getProvisioningPackageName().equals(opPkgName)) {
+ throw new IllegalArgumentException("Mismatched caller and VcnConfig creator");
+ }
+ Slog.v(TAG, "VCN config updated for subGrp: " + subscriptionGroup);
+ mContext.getSystemService(AppOpsManager.class)
+ .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName());
enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
- synchronized (mLock) {
- mConfigs.put(subscriptionGroup, config);
+ Binder.withCleanCallingIdentity(() -> {
+ synchronized (mLock) {
+ mConfigs.put(subscriptionGroup, config);
+ startOrUpdateVcnLocked(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
+ writeConfigsToDiskLocked();
+ }
+ });
}
/**
@@ -308,18 +448,21 @@
@Override
public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
+ Slog.v(TAG, "VCN config cleared for subGrp: " + subscriptionGroup);
enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
- synchronized (mLock) {
- mConfigs.remove(subscriptionGroup);
+ Binder.withCleanCallingIdentity(() -> {
+ synchronized (mLock) {
+ mConfigs.remove(subscriptionGroup);
- // Must be done synchronously to ensure that writes do not happen out-of-order.
- writeConfigsToDiskLocked();
- }
+ if (mVcns.containsKey(subscriptionGroup)) {
+ mVcns.remove(subscriptionGroup).teardownAsynchronously();
+ }
- // TODO: Clear Binder calling identity
- // TODO: Trigger teardown as necessary
+ writeConfigsToDiskLocked();
+ }
+ });
}
@GuardedBy("mLock")
@@ -345,19 +488,11 @@
}
}
- /**
- * Network provider for VCN networks.
- *
- * @hide
- */
- public class VcnNetworkProvider extends NetworkProvider {
- VcnNetworkProvider(Context context, Looper looper) {
- super(context, looper, VcnNetworkProvider.class.getSimpleName());
- }
-
- @Override
- public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
- // TODO: Handle network requests - Ensure VCN started, and start appropriate tunnels.
+ /** Get current configuration list for testing purposes */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public Map<ParcelUuid, Vcn> getAllVcns() {
+ synchronized (mLock) {
+ return Collections.unmodifiableMap(mVcns);
}
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 15e31ba..1170983 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -4756,7 +4756,7 @@
IAccountManagerResponse getResponseAndClose() {
if (mResponse == null) {
- // this session has already been closed
+ close();
return null;
}
IAccountManagerResponse response = mResponse;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a094eac..c1ab5b6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5730,20 +5730,6 @@
return mActivityTaskManager.getRecentTasks(maxNum, flags, userId);
}
- /**
- * Moves the top activity in the input rootTaskId to the pinned root task.
- *
- * @param rootTaskId Id of root task to move the top activity to pinned root task.
- * @param bounds Bounds to use for pinned root task.
- *
- * @return True if the top activity of the input root task was successfully moved to the pinned
- * root task.
- */
- @Override
- public boolean moveTopActivityToPinnedRootTask(int rootTaskId, Rect bounds) {
- return mActivityTaskManager.moveTopActivityToPinnedRootTask(rootTaskId, bounds);
- }
-
@Override
public List<RootTaskInfo> getAllRootTaskInfos() {
return mActivityTaskManager.getAllRootTaskInfos();
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index fffa814..fe71fbf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2596,8 +2596,6 @@
return runStackList(pw);
case "info":
return runRootTaskInfo(pw);
- case "move-top-activity-to-pinned-stack":
- return runMoveTopActivityToPinnedRootTask(pw);
case "remove":
return runRootTaskRemove(pw);
default:
@@ -2687,41 +2685,6 @@
return 0;
}
- int runMoveTopActivityToPinnedRootTask(PrintWriter pw) throws RemoteException {
- int rootTaskId = Integer.parseInt(getNextArgRequired());
- final Rect bounds = getBounds();
- if (bounds == null) {
- getErrPrintWriter().println("Error: invalid input bounds");
- return -1;
- }
-
- if (!mTaskInterface.moveTopActivityToPinnedRootTask(rootTaskId, bounds)) {
- getErrPrintWriter().println("Didn't move top activity to pinned stack.");
- return -1;
- }
- return 0;
- }
-
- void setBoundsSide(Rect bounds, String side, int value) {
- switch (side) {
- case "l":
- bounds.left = value;
- break;
- case "r":
- bounds.right = value;
- break;
- case "t":
- bounds.top = value;
- break;
- case "b":
- bounds.bottom = value;
- break;
- default:
- getErrPrintWriter().println("Unknown set side: " + side);
- break;
- }
- }
-
int runTask(PrintWriter pw) throws RemoteException {
String op = getNextArgRequired();
if (op.equals("lock")) {
@@ -3386,16 +3349,6 @@
pw.println(" move-task <TASK_ID> <STACK_ID> [true|false]");
pw.println(" Move <TASK_ID> from its current stack to the top (true) or");
pw.println(" bottom (false) of <STACK_ID>.");
- pw.println(" resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]");
- pw.println(" Change docked stack to <LEFT,TOP,RIGHT,BOTTOM>");
- pw.println(" and supplying temporary different task bounds indicated by");
- pw.println(" <TASK_LEFT,TOP,RIGHT,BOTTOM>");
- pw.println(" move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>");
- pw.println(" Moves the top activity from");
- pw.println(" <STACK_ID> to the pinned stack using <LEFT,TOP,RIGHT,BOTTOM> for the");
- pw.println(" bounds of the pinned stack.");
- pw.println(" positiontask <TASK_ID> <STACK_ID> <POSITION>");
- pw.println(" Place <TASK_ID> in <STACK_ID> at <POSITION>");
pw.println(" list");
pw.println(" List all of the activity stacks and their sizes.");
pw.println(" info <WINDOWING_MODE> <ACTIVITY_TYPE>");
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 6f6cad0..53d75d1 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -711,7 +711,7 @@
mAppDataIsolationEnabled =
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mAppDataIsolationWhitelistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java b/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java
new file mode 100644
index 0000000..9d43a39
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+/**
+ * Flags and constants that modify app hibernation behavior.
+ */
+final class AppHibernationConstants {
+
+ private AppHibernationConstants() {}
+
+ // Device config feature flag for app hibernation
+ static final String KEY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled";
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 493c688..9898d76 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -56,9 +56,9 @@
public AcquisitionClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int cookie, int sensorId, int statsModality,
- int statsAction, int statsClient, boolean shouldLogMetrics) {
+ int statsAction, int statsClient) {
super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality,
- statsAction, statsClient, shouldLogMetrics);
+ statsAction, statsClient);
mPowerManager = context.getSystemService(PowerManager.class);
mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 5292328..5663495 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -66,8 +66,7 @@
int statsModality, int statsClient, @Nullable TaskStackListener taskStackListener,
@NonNull LockoutTracker lockoutTracker) {
super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
- statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- true /* shouldLogMetrics */);
+ statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
mIsStrongBiometric = isStrongBiometric;
mOperationId = operationId;
mRequireConfirmation = requireConfirmation;
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
index d588b8d..49cddaa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
@@ -21,8 +21,10 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.os.AsyncTask;
import android.os.Environment;
+import android.util.AtomicFile;
import android.util.Slog;
import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -35,6 +37,7 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -46,17 +49,16 @@
public abstract class BiometricUserState<T extends BiometricAuthenticator.Identifier> {
private static final String TAG = "UserState";
+ private static final String TAG_INVALIDATION = "authenticatorIdInvalidation_tag";
+ private static final String ATTR_INVALIDATION = "authenticatorIdInvalidation_attr";
+
@GuardedBy("this")
protected final ArrayList<T> mBiometrics = new ArrayList<>();
+ protected boolean mInvalidationInProgress;
protected final Context mContext;
protected final File mFile;
- private final Runnable mWriteStateRunnable = new Runnable() {
- @Override
- public void run() {
- doWriteState();
- }
- };
+ private final Runnable mWriteStateRunnable = this::doWriteStateInternal;
/**
* @return The tag for the biometrics. There may be multiple instances of a biometric within.
@@ -73,10 +75,40 @@
*/
protected abstract ArrayList<T> getCopy(ArrayList<T> array);
+ protected abstract void doWriteState(@NonNull TypedXmlSerializer serializer) throws Exception;
+
/**
- * @return Writes the cached data to persistent storage.
+ * @Writes the cached data to persistent storage.
*/
- protected abstract void doWriteState();
+ private void doWriteStateInternal() {
+ AtomicFile destination = new AtomicFile(mFile);
+
+ FileOutputStream out = null;
+
+ try {
+ out = destination.startWrite();
+ TypedXmlSerializer serializer = Xml.resolveSerializer(out);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startDocument(null, true);
+
+ // Store the authenticatorId
+ serializer.startTag(null, TAG_INVALIDATION);
+ serializer.attributeBoolean(null, ATTR_INVALIDATION, mInvalidationInProgress);
+ serializer.endTag(null, TAG_INVALIDATION);
+
+ // Do any additional serialization that subclasses may require
+ doWriteState(serializer);
+
+ serializer.endDocument();
+ destination.finishWrite(out);
+ } catch (Throwable t) {
+ Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
+ destination.failWrite(out);
+ throw new IllegalStateException("Failed to write to file: " + mFile.toString(), t);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
/**
* @return
@@ -93,6 +125,19 @@
}
}
+ public void setInvalidationInProgress(boolean invalidationInProgress) {
+ synchronized (this) {
+ mInvalidationInProgress = invalidationInProgress;
+ scheduleWriteStateLocked();
+ }
+ }
+
+ public boolean isInvalidationInProgress() {
+ synchronized (this) {
+ return mInvalidationInProgress;
+ }
+ }
+
public void addBiometric(T identifier) {
synchronized (this) {
mBiometrics.add(identifier);
@@ -202,6 +247,8 @@
String tagName = parser.getName();
if (tagName.equals(getBiometricsTag())) {
parseBiometricsLocked(parser);
+ } else if (tagName.equals(TAG_INVALIDATION)) {
+ mInvalidationInProgress = parser.getAttributeBoolean(null, ATTR_INVALIDATION);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java
index d52b340..ebe4679 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java
@@ -31,4 +31,6 @@
void removeBiometricForUser(Context context, int userId, int biometricId);
void renameBiometricForUser(Context context, int userId, int biometricId, CharSequence name);
CharSequence getUniqueName(Context context, int userId);
+ void setInvalidationInProgress(Context context, int userId, boolean inProgress);
+ boolean isInvalidationInProgress(Context context, int userId);
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 27c2dd0..bbd6523 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -105,8 +105,8 @@
public ClientMonitor(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
- int statsClient, boolean shouldLogMetrics) {
- super(statsModality, statsAction, statsClient, shouldLogMetrics);
+ int statsClient) {
+ super(statsModality, statsAction, statsClient);
mSequentialId = sCount++;
mContext = context;
mLazyDaemon = lazyDaemon;
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index fbe3d84..8bf9680 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -49,10 +49,10 @@
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
int timeoutSec, int statsModality, int sensorId,
- boolean shouldVibrate, boolean shouldLogMetrics) {
+ boolean shouldVibrate) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
statsModality, BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN, shouldLogMetrics);
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
mTimeoutSec = timeoutSec;
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index f6a1040..bac944f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -32,7 +32,7 @@
@NonNull String owner, int sensorId) {
super(context, lazyDaemon, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, true /* shouldLogMetrics */);
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index e56e116..e738d17 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -107,8 +107,7 @@
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
userId, owner, 0 /* cookie */, sensorId, statsModality,
- BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN,
- true /* shouldLogMetrics */);
+ BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
mEnrolledList = enrolledList;
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 158b836..e07f712 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -51,7 +51,7 @@
// is all done internally.
super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner,
0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, true /* shouldLogMetrics */);
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
mEnrolledList = enrolledList;
mUtils = utils;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
new file mode 100644
index 0000000..b8084d5
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
+
+/**
+ * ClientMonitor subclass for requesting authenticatorId invalidation. See
+ * {@link InvalidationRequesterClient} for more info.
+ */
+public abstract class InvalidationClient<S extends BiometricAuthenticator.Identifier, T>
+ extends ClientMonitor<T> {
+
+ private final BiometricUtils<S> mUtils;
+
+ public InvalidationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ int userId, int sensorId, @NonNull BiometricUtils<S> utils) {
+ super(context, lazyDaemon, null /* token */, null /* listener */, userId,
+ context.getOpPackageName(), 0 /* cookie */, sensorId,
+ BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
+ mUtils = utils;
+ }
+
+ public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
+ // TODO: Update framework w/ newAuthenticatorId
+ mCallback.onClientFinished(this, true /* success */);
+ }
+
+ @Override
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
+
+ startHalOperation();
+ }
+
+ @Override
+ public void unableToStart() {
+
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
new file mode 100644
index 0000000..ca34eee
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricsProtoEnums;
+
+/**
+ * ClientMonitor subclass responsible for coordination of authenticatorId invalidation of other
+ * sensors. See {@link InvalidationClient} for the ClientMonitor subclass responsible for initiating
+ * the invalidation with individual HALs. AuthenticatorId invalidation is required on devices with
+ * multiple strong biometric sensors.
+ *
+ * The public Keystore and Biometric APIs are biometric-tied, not modality-tied, meaning that keys
+ * are unlockable by "any/all strong biometrics on the device", and not "only a specific strong
+ * sensor". The Keystore API allows for creation of biometric-tied keys that are invalidated upon
+ * new biometric enrollment. See
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setInvalidatedByBiometricEnrollment}
+ *
+ * This has been supported on single-sensor devices by the various getAuthenticatorId APIs on the
+ * HIDL and AIDL biometric HAL interfaces, where:
+ * 1) authenticatorId is requested and stored during key generation
+ * 2) authenticatorId is contained within the HAT when biometric authentication succeeds
+ * 3) authenticatorId is automatically changed (below the framework) whenever a new biometric
+ * enrollment occurs.
+ *
+ * For multi-biometric devices, this will be done the following way:
+ * 1) New enrollment added for Sensor1. Sensor1's HAL/TEE updates its authenticatorId automatically
+ * when enrollment completes
+ * 2) Framework marks Sensor1 as "invalidationInProgress". See
+ * {@link BiometricUtils#setInvalidationInProgress(Context, int, boolean)}
+ * 3) After all other sensors have finished invalidation, the framework will clear the invalidation
+ * flag for Sensor1.
+ * 4) New keys that are generated will include all new authenticatorIds
+ *
+ * The above is robust to incomplete invalidation. For example, when system boots or after user
+ * switches, the framework can check if any sensor has the "invalidationInProgress" flag set. If so,
+ * the framework should re-start the invalidation process described above.
+ */
+public abstract class InvalidationRequesterClient<T> extends ClientMonitor<T> {
+
+ private final BiometricManager mBiometricManager;
+
+ public InvalidationRequesterClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ int userId, int sensorId) {
+ super(context, lazyDaemon, null /* token */, null /* listener */, userId,
+ context.getOpPackageName(), 0 /* cookie */, sensorId,
+ BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
+ mBiometricManager = context.getSystemService(BiometricManager.class);
+ }
+
+ @Override
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
+
+ // TODO(b/159667191): Request BiometricManager/BiometricService to invalidate
+ // authenticatorIds. Be sure to invoke BiometricUtils#setInvalidationInProgress(true)
+ }
+
+ @Override
+ public void unableToStart() {
+
+ }
+
+ @Override
+ protected void startHalOperation() {
+ // No HAL operations necessary
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
index 59e40da..3ca0691 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
@@ -38,7 +38,7 @@
private final int mStatsAction;
private final int mStatsClient;
private long mFirstAcquireTimeMs;
- private boolean mShouldLogMetrics;
+ private boolean mShouldLogMetrics = true;
/**
* Only valid for AuthenticationClient.
@@ -52,14 +52,15 @@
* @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants.
* @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants.
* @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants.
- * @param shouldLogMetrics If set to false, metrics will not be reported to statsd.
*/
- public LoggableMonitor(int statsModality, int statsAction, int statsClient,
- boolean shouldLogMetrics) {
+ public LoggableMonitor(int statsModality, int statsAction, int statsClient) {
mStatsModality = statsModality;
mStatsAction = statsAction;
mStatsClient = statsClient;
- mShouldLogMetrics = shouldLogMetrics;
+ }
+
+ protected void setShouldLog(boolean shouldLog) {
+ mShouldLogMetrics = shouldLog;
}
public int getStatsClient() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 22b5717..f79abd5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -45,7 +45,7 @@
int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
statsModality, BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, true /* shouldLogMetrics */);
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricId = biometricId;
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index dcbd4b5..5deb8fa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -27,8 +27,7 @@
@NonNull IBinder token, @NonNull String owner, int sensorId) {
super(context, lazyDaemon, token, null /* listener */, 0 /* userId */, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN,
- true /* shouldLogMetrics */);
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
index a26662d..a9981d0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
@@ -16,23 +16,18 @@
package com.android.server.biometrics.sensors.face;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.face.Face;
-import android.util.AtomicFile;
-import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
-import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.server.biometrics.sensors.BiometricUserState;
-import libcore.io.IoUtils;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
@@ -75,46 +70,26 @@
}
@Override
- protected void doWriteState() {
- AtomicFile destination = new AtomicFile(mFile);
-
- ArrayList<Face> faces;
+ protected void doWriteState(@NonNull TypedXmlSerializer serializer) throws Exception {
+ final ArrayList<Face> faces;
synchronized (this) {
faces = getCopy(mBiometrics);
}
- FileOutputStream out = null;
- try {
- out = destination.startWrite();
+ serializer.startTag(null, TAG_FACES);
- TypedXmlSerializer serializer = Xml.resolveSerializer(out);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startDocument(null, true);
- serializer.startTag(null, TAG_FACES);
-
- final int count = faces.size();
- for (int i = 0; i < count; i++) {
- Face f = faces.get(i);
- serializer.startTag(null, TAG_FACE);
- serializer.attributeInt(null, ATTR_FACE_ID, f.getBiometricId());
- serializer.attribute(null, ATTR_NAME, f.getName().toString());
- serializer.attributeLong(null, ATTR_DEVICE_ID, f.getDeviceId());
- serializer.endTag(null, TAG_FACE);
- }
-
- serializer.endTag(null, TAG_FACES);
- serializer.endDocument();
- destination.finishWrite(out);
-
- // Any error while writing is fatal.
- } catch (Throwable t) {
- Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
- destination.failWrite(out);
- throw new IllegalStateException("Failed to write faces", t);
- } finally {
- IoUtils.closeQuietly(out);
+ final int count = faces.size();
+ for (int i = 0; i < count; i++) {
+ Face f = faces.get(i);
+ serializer.startTag(null, TAG_FACE);
+ serializer.attributeInt(null, ATTR_FACE_ID, f.getBiometricId());
+ serializer.attribute(null, ATTR_NAME, f.getName().toString());
+ serializer.attributeLong(null, ATTR_DEVICE_ID, f.getDeviceId());
+ serializer.endTag(null, TAG_FACE);
}
+
+ serializer.endTag(null, TAG_FACES);
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
index a0cd4a5..c574478 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
@@ -18,7 +18,6 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.face.Face;
import android.text.TextUtils;
import android.util.SparseArray;
@@ -115,6 +114,16 @@
return getStateForUser(context, userId).getUniqueName();
}
+ @Override
+ public void setInvalidationInProgress(Context context, int userId, boolean inProgress) {
+ getStateForUser(context, userId).setInvalidationInProgress(inProgress);
+ }
+
+ @Override
+ public boolean isInvalidationInProgress(Context context, int userId) {
+ return getStateForUser(context, userId).isInvalidationInProgress();
+ }
+
private FaceUserState getStateForUser(Context ctx, int userId) {
synchronized (this) {
FaceUserState state = mUserStates.get(userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 99b89e3..f09df1e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -62,7 +62,7 @@
@Nullable NativeHandle previewSurface, int sensorId, int maxTemplatesPerUser) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
- false /* shouldVibrate */, true /* shouldLogMetrics */);
+ false /* shouldVibrate */);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
mEnrollIgnoreListVendor = getContext().getResources()
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index ce0bb45..c27b6e5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -38,8 +38,7 @@
Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, opPackageName,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FACE,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN,
- true /* shouldLogMetrics */);
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mAuthenticatorIds = authenticatorIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
new file mode 100644
index 0000000..f512cef
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import android.content.Context;
+import android.hardware.biometrics.face.ISession;
+
+import android.annotation.NonNull;
+import android.hardware.face.Face;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.InvalidationClient;
+import com.android.server.biometrics.sensors.face.FaceUtils;
+
+public class FaceInvalidationClient extends InvalidationClient<Face, ISession> {
+ private static final String TAG = "FaceInvalidationClient";
+
+ public FaceInvalidationClient(@NonNull Context context,
+ @NonNull LazyDaemon<ISession> lazyDaemon, int userId, int sensorId,
+ @NonNull FaceUtils utils) {
+ super(context, lazyDaemon, userId, sensorId, utils);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().invalidateAuthenticatorId(mSequentialId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 5000995..5b1f546 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -50,8 +50,7 @@
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN,
- true /* shouldLogMetrics */);
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index bc1eace..1a7544fc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -59,7 +59,7 @@
@Nullable NativeHandle surfaceHandle, int sensorId) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
- false /* shouldVibrate */, true /* shouldLogMetrics */);
+ false /* shouldVibrate */);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mSurfaceHandle = surfaceHandle;
mEnrollIgnoreList = getContext().getResources()
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
index 3758774..e25bb81 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
@@ -47,7 +47,7 @@
@NonNull String owner, int sensorId, int feature, int faceId) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, true /* shouldLogMetrics */);
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
mFeature = feature;
mFaceId = faceId;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
index 06808e0..8df9b9f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
@@ -42,8 +42,7 @@
@NonNull byte[] hardwareAuthToken) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN,
- true /* shouldLogMetrics */);
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mHardwareAuthToken = new ArrayList<>();
for (byte b : hardwareAuthToken) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
index b2db6c7..0e20728 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
@@ -49,7 +49,7 @@
byte[] hardwareAuthToken, int faceId) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, true /* shouldLogMetrics */);
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
mFeature = feature;
mEnabled = enabled;
mFaceId = faceId;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index c9d2cd3..22275e5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -43,8 +43,7 @@
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN,
- true /* shouldLogMetrics */);
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mCurrentUserId = currentUserId;
mHasEnrolledBiometrics = hasEnrolledBIometrics;
mAuthenticatorIds = authenticatorIds;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
index 671e08b..ae173f7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
@@ -16,23 +16,18 @@
package com.android.server.biometrics.sensors.fingerprint;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
-import android.util.AtomicFile;
-import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
-import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.server.biometrics.sensors.BiometricUserState;
-import libcore.io.IoUtils;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
@@ -76,47 +71,27 @@
}
@Override
- protected void doWriteState() {
- AtomicFile destination = new AtomicFile(mFile);
-
- ArrayList<Fingerprint> fingerprints;
+ protected void doWriteState(@NonNull TypedXmlSerializer serializer) throws Exception {
+ final ArrayList<Fingerprint> fingerprints;
synchronized (this) {
fingerprints = getCopy(mBiometrics);
}
- FileOutputStream out = null;
- try {
- out = destination.startWrite();
+ serializer.startTag(null, TAG_FINGERPRINTS);
- TypedXmlSerializer serializer = Xml.resolveSerializer(out);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startDocument(null, true);
- serializer.startTag(null, TAG_FINGERPRINTS);
-
- final int count = fingerprints.size();
- for (int i = 0; i < count; i++) {
- Fingerprint fp = fingerprints.get(i);
- serializer.startTag(null, TAG_FINGERPRINT);
- serializer.attributeInt(null, ATTR_FINGER_ID, fp.getBiometricId());
- serializer.attribute(null, ATTR_NAME, fp.getName().toString());
- serializer.attributeInt(null, ATTR_GROUP_ID, fp.getGroupId());
- serializer.attributeLong(null, ATTR_DEVICE_ID, fp.getDeviceId());
- serializer.endTag(null, TAG_FINGERPRINT);
- }
-
- serializer.endTag(null, TAG_FINGERPRINTS);
- serializer.endDocument();
- destination.finishWrite(out);
-
- // Any error while writing is fatal.
- } catch (Throwable t) {
- Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
- destination.failWrite(out);
- throw new IllegalStateException("Failed to write fingerprints", t);
- } finally {
- IoUtils.closeQuietly(out);
+ final int count = fingerprints.size();
+ for (int i = 0; i < count; i++) {
+ Fingerprint fp = fingerprints.get(i);
+ serializer.startTag(null, TAG_FINGERPRINT);
+ serializer.attributeInt(null, ATTR_FINGER_ID, fp.getBiometricId());
+ serializer.attribute(null, ATTR_NAME, fp.getName().toString());
+ serializer.attributeInt(null, ATTR_GROUP_ID, fp.getGroupId());
+ serializer.attributeLong(null, ATTR_DEVICE_ID, fp.getDeviceId());
+ serializer.endTag(null, TAG_FINGERPRINT);
}
+
+ serializer.endTag(null, TAG_FINGERPRINTS);
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
index b3d2419..dc6fd3a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
@@ -118,6 +118,16 @@
return getStateForUser(context, userId).getUniqueName();
}
+ @Override
+ public void setInvalidationInProgress(Context context, int userId, boolean inProgress) {
+ getStateForUser(context, userId).setInvalidationInProgress(inProgress);
+ }
+
+ @Override
+ public boolean isInvalidationInProgress(Context context, int userId) {
+ return getStateForUser(context, userId).isInvalidationInProgress();
+ }
+
private FingerprintUserState getStateForUser(Context ctx, int userId) {
synchronized (this) {
FingerprintUserState state = mUserStates.get(userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 483ca5d..3398323 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -51,7 +51,7 @@
int statsClient) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE,
- statsClient, true /* shouldLogMetrics */);
+ statsClient);
mIsStrongBiometric = isStrongBiometric;
mUdfpsOverlayController = udfpsOverlayController;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index b4f7054..ab59abd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -55,9 +55,10 @@
boolean shouldLogMetrics) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
- true /* shouldVibrate */, shouldLogMetrics);
+ true /* shouldVibrate */);
mUdfpsOverlayController = udfpsOvelayController;
mMaxTemplatesPerUser = maxTemplatesPerUser;
+ setShouldLog(shouldLogMetrics);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index b30cc1a..2ad1fa3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -38,8 +38,7 @@
int sensorId, Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN,
- true /* shouldLogMetrics */);
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mAuthenticatorIds = authenticatorIds;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
new file mode 100644
index 0000000..b6d8892
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.InvalidationClient;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+
+public class FingerprintInvalidationClient extends InvalidationClient<Fingerprint, ISession> {
+ private static final String TAG = "FingerprintInvalidationClient";
+
+ public FingerprintInvalidationClient(@NonNull Context context,
+ @NonNull LazyDaemon<ISession> lazyDaemon, int userId, int sensorId,
+ @NonNull FingerprintUtils utils) {
+ super(context, lazyDaemon, userId, sensorId, utils);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().invalidateAuthenticatorId(mSequentialId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 093c944..1f1d19d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -50,8 +50,7 @@
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN,
- true /* shouldLogMetrics */);
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index c00dd04..4747488 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -56,7 +56,7 @@
boolean isStrongBiometric, int statsClient) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE,
- statsClient, true /* shouldLogMetrics */);
+ statsClient);
mUdfpsOverlayController = udfpsOverlayController;
mIsStrongBiometric = isStrongBiometric;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index a261b0b..d1f1cf8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -56,8 +56,9 @@
boolean shouldLogMetrics) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
- true /* shouldVibrate */, shouldLogMetrics);
+ true /* shouldVibrate */);
mUdfpsOverlayController = udfpsOverlayController;
+ setShouldLog(shouldLogMetrics);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index c785eef..00e2413 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -50,8 +50,7 @@
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN,
- true /* shouldLogMetrics */);
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mCurrentUserId = currentUserId;
mHasEnrolledBiometrics = hasEnrolledBiometrics;
mAuthenticatorIds = authenticatorIds;
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index c4ff99b..18907a1 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -213,6 +213,19 @@
}
/**
+ * Find if this change will be enabled for the given package after installation.
+ *
+ * @param packageName The package name in question
+ * @return {@code true} if the change should be enabled for the package.
+ */
+ boolean willBeEnabled(String packageName) {
+ if (hasDeferredOverride(packageName)) {
+ return mDeferredOverrides.get(packageName);
+ }
+ return defaultValue();
+ }
+
+ /**
* 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.
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index f03a608..9376e8d 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -146,6 +146,25 @@
}
/**
+ * Find if a given change will be enabled for a given package name, prior to installation.
+ *
+ * @param changeId The ID of the change in question
+ * @param packageName Package name to check for
+ * @return {@code true} if the change would be enabled for this package name. Also returns
+ * {@code true} if the change ID is not known, as unknown changes are enabled by default.
+ */
+ boolean willChangeBeEnabled(long changeId, String packageName) {
+ synchronized (mChanges) {
+ CompatChange c = mChanges.get(changeId);
+ if (c == null) {
+ // we know nothing about this change: default behaviour is enabled.
+ return true;
+ }
+ return c.willBeEnabled(packageName);
+ }
+ }
+
+ /**
* Overrides the enabled state for a given change and app. This method is intended to be used
* *only* for debugging purposes, ultimately invoked either by an adb command, or from some
* developer settings UI.
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 283dba7..1ea468c 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -137,6 +137,9 @@
@UserIdInt int userId) {
checkCompatChangeReadAndLogPermission();
ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
+ if (appInfo == null) {
+ return mCompatConfig.willChangeBeEnabled(changeId, packageName);
+ }
return isChangeEnabled(changeId, appInfo);
}
diff --git a/services/core/java/com/android/server/content/SyncManager.md b/services/core/java/com/android/server/content/SyncManager.md
index 8507abd..8564fea 100644
--- a/services/core/java/com/android/server/content/SyncManager.md
+++ b/services/core/java/com/android/server/content/SyncManager.md
@@ -29,8 +29,10 @@
### Two Levels of Exemption
Specifically, there are two different levels of exemption, depending on the state of the caller:
-1. `ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET`
+1. `ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET`.
+ This is shown as `STANDBY-EXEMPTED` in `dumpsys content`.
2. `ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP`, which is more powerful than 1.
+ This is shown as `STANDBY-EXEMPTED(TOP)` in `dumpsys content`.
The exemption level is calculated in
[ContentService.getSyncExemptionAndCleanUpExtrasForCaller()](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/content/ContentService.java?q=%22int%20getSyncExemptionAndCleanUpExtrasForCaller%22&ss=android%2Fplatform%2Fsuperproject),
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 52121f3..0b10cc3 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -752,8 +752,8 @@
message.getParams(),
false)) {
// Vendor command listener may not have been registered yet. Respond with
- // <Feature Abort> [NOT_IN_CORRECT_MODE] so that the sender can try again later.
- mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
+ // <Feature Abort> [Refused] so that the sender can try again later.
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
}
return true;
}
@@ -764,7 +764,7 @@
if (vendorId == mService.getVendorId()) {
if (!mService.invokeVendorCommandListenersOnReceived(
mDeviceType, message.getSource(), message.getDestination(), params, true)) {
- mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
}
} else if (message.getDestination() != Constants.ADDR_BROADCAST
&& message.getSource() != Constants.ADDR_UNREGISTERED) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 9ab30b4..7ec4a5a 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -26,6 +26,8 @@
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
import static android.location.LocationRequest.LOW_POWER_EXCEPTIONS;
+import static android.location.provider.LocationProviderBase.ACTION_FUSED_PROVIDER;
+import static android.location.provider.LocationProviderBase.ACTION_NETWORK_PROVIDER;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
@@ -64,7 +66,7 @@
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
import android.os.Bundle;
@@ -213,11 +215,6 @@
public static final String TAG = "LocationManagerService";
public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
- private static final String NETWORK_LOCATION_SERVICE_ACTION =
- "com.android.location.service.v3.NetworkLocationProvider";
- private static final String FUSED_LOCATION_SERVICE_ACTION =
- "com.android.location.service.FusedLocationProvider";
-
private static final String ATTRIBUTION_TAG = "LocationService";
private final Object mLock = new Object();
@@ -339,7 +336,7 @@
// provider has unfortunate hard dependencies on the network provider
ProxyLocationProvider networkProvider = ProxyLocationProvider.create(
mContext,
- NETWORK_LOCATION_SERVICE_ACTION,
+ ACTION_NETWORK_PROVIDER,
com.android.internal.R.bool.config_enableNetworkLocationOverlay,
com.android.internal.R.string.config_networkLocationProviderPackageName);
if (networkProvider != null) {
@@ -352,13 +349,13 @@
// ensure that a fused provider exists which will work in direct boot
Preconditions.checkState(!mContext.getPackageManager().queryIntentServicesAsUser(
- new Intent(FUSED_LOCATION_SERVICE_ACTION),
+ new Intent(ACTION_FUSED_PROVIDER),
MATCH_DIRECT_BOOT_AWARE | MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM).isEmpty(),
"Unable to find a direct boot aware fused location provider");
ProxyLocationProvider fusedProvider = ProxyLocationProvider.create(
mContext,
- FUSED_LOCATION_SERVICE_ACTION,
+ ACTION_FUSED_PROVIDER,
com.android.internal.R.bool.config_enableFusedLocationOverlay,
com.android.internal.R.string.config_fusedLocationProviderPackageName);
if (fusedProvider != null) {
@@ -410,16 +407,17 @@
for (String testProviderString : testProviderStrings) {
String[] fragments = testProviderString.split(",");
String name = fragments[0].trim();
- ProviderProperties properties = new ProviderProperties(
- Boolean.parseBoolean(fragments[1]) /* requiresNetwork */,
- Boolean.parseBoolean(fragments[2]) /* requiresSatellite */,
- Boolean.parseBoolean(fragments[3]) /* requiresCell */,
- Boolean.parseBoolean(fragments[4]) /* hasMonetaryCost */,
- Boolean.parseBoolean(fragments[5]) /* supportsAltitude */,
- Boolean.parseBoolean(fragments[6]) /* supportsSpeed */,
- Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
- Integer.parseInt(fragments[8]) /* powerUsage */,
- Integer.parseInt(fragments[9]) /* accuracy */);
+ ProviderProperties properties = new ProviderProperties.Builder()
+ .setHasNetworkRequirement(Boolean.parseBoolean(fragments[1]))
+ .setHasSatelliteRequirement(Boolean.parseBoolean(fragments[2]))
+ .setHasCellRequirement(Boolean.parseBoolean(fragments[3]))
+ .setHasMonetaryCost(Boolean.parseBoolean(fragments[4]))
+ .setHasAltitudeSupport(Boolean.parseBoolean(fragments[5]))
+ .setHasSpeedSupport(Boolean.parseBoolean(fragments[6]))
+ .setHasBearingSupport(Boolean.parseBoolean(fragments[7]))
+ .setPowerUsage(Integer.parseInt(fragments[8]))
+ .setAccuracy(Integer.parseInt(fragments[9]))
+ .build();
getOrAddLocationProviderManager(name).setMockProvider(
new MockLocationProvider(properties, CallerIdentity.fromContext(mContext)));
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 477c037b..d16267f 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -16,6 +16,9 @@
package com.android.server.location.gnss;
+import static android.location.provider.ProviderProperties.ACCURACY_FINE;
+import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
+
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_GSM_CELLID;
import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
@@ -58,7 +61,8 @@
import android.location.LocationManager;
import android.location.LocationRequest;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.AsyncTask;
import android.os.BatteryStats;
@@ -88,7 +92,6 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.location.GpsNetInitiatedHandler;
import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
-import com.android.internal.location.ProviderRequest;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.FgThread;
import com.android.server.location.gnss.GnssSatelliteBlocklistHelper.GnssSatelliteBlocklistCallback;
@@ -122,16 +125,14 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
- private static final ProviderProperties PROPERTIES = new ProviderProperties(
- /* requiresNetwork = */false,
- /* requiresSatellite = */true,
- /* requiresCell = */false,
- /* hasMonetaryCost = */false,
- /* supportAltitude = */true,
- /* supportsSpeed = */true,
- /* supportsBearing = */true,
- ProviderProperties.POWER_USAGE_HIGH,
- ProviderProperties.ACCURACY_FINE);
+ private static final ProviderProperties PROPERTIES = new ProviderProperties.Builder()
+ .setHasSatelliteRequirement(true)
+ .setHasAltitudeSupport(true)
+ .setHasSpeedSupport(true)
+ .setHasBearingSupport(true)
+ .setPowerUsage(POWER_USAGE_HIGH)
+ .setAccuracy(ACCURACY_FINE)
+ .build();
// The AGPS SUPL mode
private static final int AGPS_SUPL_MODE_MSA = 0x02;
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/injector/LocationEventLog.java
index b34beed..b8b54b3 100644
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/injector/LocationEventLog.java
@@ -26,11 +26,11 @@
import android.annotation.Nullable;
import android.location.LocationRequest;
+import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Build;
import android.os.PowerManager.LocationPowerSaveMode;
-import com.android.internal.location.ProviderRequest;
import com.android.server.utils.eventlog.LocalEventLog;
/** In memory event log for location events. */
diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
index 5364feb..e22a014 100644
--- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
@@ -18,12 +18,12 @@
import android.annotation.Nullable;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
import android.os.Bundle;
-import com.android.internal.location.ProviderRequest;
import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderController.java b/services/core/java/com/android/server/location/provider/LocationProviderController.java
index e49d9db..a0e3794 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderController.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderController.java
@@ -17,9 +17,9 @@
package com.android.server.location.provider;
import android.annotation.Nullable;
+import android.location.provider.ProviderRequest;
import android.os.Bundle;
-import com.android.internal.location.ProviderRequest;
import com.android.server.location.provider.AbstractLocationProvider.Listener;
import com.android.server.location.provider.AbstractLocationProvider.State;
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 858b762..14f0100 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -55,7 +55,8 @@
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
import android.os.Build;
@@ -82,7 +83,6 @@
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.location.ProviderRequest;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
index f9aa402..dce7b08 100644
--- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
@@ -21,12 +21,11 @@
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
-import com.android.internal.location.ProviderRequest;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
index c1b0abf..cb7264e 100644
--- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
@@ -21,12 +21,12 @@
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.location.ProviderRequest;
import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
index 1f4c4cf..a5758a3 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
@@ -16,16 +16,18 @@
package com.android.server.location.provider;
+import static android.location.provider.ProviderProperties.ACCURACY_FINE;
+import static android.location.provider.ProviderProperties.POWER_USAGE_LOW;
+
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import android.content.Context;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
-import com.android.internal.location.ProviderRequest;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -38,16 +40,10 @@
*/
public class PassiveLocationProvider extends AbstractLocationProvider {
- private static final ProviderProperties PROPERTIES = new ProviderProperties(
- /* requiresNetwork = */false,
- /* requiresSatellite = */false,
- /* requiresCell = */false,
- /* hasMonetaryCost = */false,
- /* supportsAltitude = */false,
- /* supportsSpeed = */false,
- /* supportsBearing = */false,
- ProviderProperties.POWER_USAGE_LOW,
- ProviderProperties.ACCURACY_COARSE);
+ private static final ProviderProperties PROPERTIES = new ProviderProperties.Builder()
+ .setPowerUsage(POWER_USAGE_LOW)
+ .setAccuracy(ACCURACY_FINE)
+ .build();
public PassiveLocationProvider(Context context) {
// using a direct executor is ok because this class has no locks that could deadlock
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
index d3fee82..b35af4f 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
@@ -20,9 +20,9 @@
import android.content.Context;
import android.location.LocationManager;
import android.location.LocationResult;
+import android.location.provider.ProviderRequest;
import android.os.Binder;
-import com.android.internal.location.ProviderRequest;
import com.android.internal.util.Preconditions;
import com.android.server.location.injector.Injector;
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index 345fdc0..c274c28 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -22,7 +22,10 @@
import android.content.ComponentName;
import android.content.Context;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ILocationProvider;
+import android.location.provider.ILocationProviderManager;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
import android.os.Bundle;
@@ -30,9 +33,6 @@
import android.os.RemoteException;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.location.ILocationProvider;
-import com.android.internal.location.ILocationProviderManager;
-import com.android.internal.location.ProviderRequest;
import com.android.internal.util.ArrayUtils;
import com.android.server.ServiceWatcher;
import com.android.server.location.provider.AbstractLocationProvider;
@@ -106,7 +106,7 @@
ProviderRequest request = mRequest;
if (!request.equals(ProviderRequest.EMPTY_REQUEST)) {
- provider.setRequest(request, request.getWorkSource());
+ provider.setRequest(request);
}
}
}
@@ -142,7 +142,7 @@
mRequest = request;
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
- provider.setRequest(request, request.getWorkSource());
+ provider.setRequest(request);
});
}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
index 2b3c0bf..0d284fc 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java
+++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
@@ -73,6 +73,10 @@
// Non-null after initialize()
private Callback mCallback;
+ /** Indicates both providers have completed initialization. */
+ @GuardedBy("mSharedLock")
+ private boolean mProvidersInitialized;
+
/**
* Used for scheduling uncertainty timeouts, i.e after a provider has reported uncertainty.
* This timeout is not provider-specific: it is started when the controller becomes uncertain
@@ -108,6 +112,7 @@
ControllerImpl.this::onProviderStateChange;
mPrimaryProvider.initialize(providerListener);
mSecondaryProvider.initialize(providerListener);
+ mProvidersInitialized = true;
alterProvidersStartedStateIfRequired(
null /* oldConfiguration */, mCurrentUserConfiguration);
@@ -322,6 +327,16 @@
assertProviderKnown(provider);
synchronized (mSharedLock) {
+ // Ignore provider state changes during initialization. e.g. if the primary provider
+ // moves to PROVIDER_STATE_PERM_FAILED during initialization, the secondary will not
+ // be ready to take over yet.
+ if (!mProvidersInitialized) {
+ warnLog("onProviderStateChange: Ignoring provider state change because both"
+ + " providers have not yet completed initialization."
+ + " providerState=" + providerState);
+ return;
+ }
+
switch (providerState.stateEnum) {
case PROVIDER_STATE_STARTED_INITIALIZING:
case PROVIDER_STATE_STOPPED:
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cc5dfdc..4ab8279 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -446,16 +446,6 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L;
- /**
- * Rate limit showing toasts, on a per package basis.
- *
- * It limits the effects of {@link android.widget.Toast#show()} calls to prevent overburdening
- * the user with too many toasts in a limited time. Any attempt to show more toasts than allowed
- * in a certain time frame will result in the toast being discarded.
- */
- @ChangeId
- private static final long RATE_LIMIT_TOASTS = 154198299L;
-
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
@@ -527,6 +517,9 @@
@GuardedBy("mNotificationLock")
final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
+ // set of uids for which toast rate limiting is disabled
+ @GuardedBy("mToastQueue")
+ private final Set<Integer> mToastRateLimitingDisabledUids = new ArraySet<>();
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
// True if the toast that's on top of the queue is being shown at the moment.
@@ -864,7 +857,7 @@
@VisibleForTesting
protected void handleSavePolicyFile() {
if (!IoThread.getHandler().hasCallbacks(mSavePolicyFile)) {
- IoThread.getHandler().post(mSavePolicyFile);
+ IoThread.getHandler().postDelayed(mSavePolicyFile, 250);
}
}
@@ -3068,6 +3061,22 @@
}
@Override
+ public void setToastRateLimitingEnabled(boolean enable) {
+ getContext().enforceCallingPermission(
+ android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING,
+ "App doesn't have the permission to enable/disable toast rate limiting");
+
+ synchronized (mToastQueue) {
+ int uid = Binder.getCallingUid();
+ if (enable) {
+ mToastRateLimitingDisabledUids.remove(uid);
+ } else {
+ mToastRateLimitingDisabledUids.add(uid);
+ }
+ }
+ }
+
+ @Override
public void finishToken(String pkg, IBinder token) {
synchronized (mToastQueue) {
final long callingId = Binder.clearCallingIdentity();
@@ -7378,7 +7387,7 @@
while (record != null) {
int userId = UserHandle.getUserId(record.uid);
boolean rateLimitingEnabled =
- CompatChanges.isChangeEnabled(RATE_LIMIT_TOASTS, record.uid);
+ !mToastRateLimitingDisabledUids.contains(record.uid);
boolean isWithinQuota =
mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG);
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
index 06706cd..0ffc1ed 100644
--- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -184,7 +184,7 @@
List<PackageInfo> allPackages;
try {
allPackages = mPackageManager.getInstalledPackages(
- flags | PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM).getList();
+ flags | PackageManager.MATCH_APEX, UserHandle.getCallingUserId()).getList();
} catch (RemoteException e) {
Slog.w(TAG, "Unable to retrieve all package names", e);
return Collections.emptyList();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cf9867c..27008d8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -393,7 +393,11 @@
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
+import com.android.server.utils.Watchable;
+import com.android.server.utils.Watched;
import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedSparseBooleanArray;
+import com.android.server.utils.Watcher;
import com.android.server.wm.ActivityTaskManagerInternal;
import dalvik.system.CloseGuard;
@@ -506,6 +510,8 @@
public static final boolean DEBUG_PERMISSIONS = false;
private static final boolean DEBUG_SHARED_LIBRARIES = false;
public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
+ public static final boolean DEBUG_CACHES = false;
+ public static final boolean TRACE_CACHES = false;
// Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService
// and PackageDexOptimizer. All these classes have their own flag to allow switching a single
@@ -793,11 +799,13 @@
final Object mLock;
// Keys are String (package name), values are Package.
+ @Watched
@GuardedBy("mLock")
- final ArrayMap<String, AndroidPackage> mPackages = new ArrayMap<>();
+ final WatchedArrayMap<String, AndroidPackage> mPackages = new WatchedArrayMap<>();
// Keys are isolated uids and values are the uid of the application
- // that created the isolated proccess.
+ // that created the isolated process.
+ @Watched
@GuardedBy("mLock")
final SparseIntArray mIsolatedOwners = new SparseIntArray();
@@ -822,6 +830,7 @@
private final TestUtilityService mTestUtilityService;
+ @Watched
@GuardedBy("mLock")
final Settings mSettings;
@@ -852,6 +861,7 @@
@GuardedBy("mAvailableFeatures")
final ArrayMap<String, FeatureInfo> mAvailableFeatures;
+ @Watched
private final InstantAppRegistry mInstantAppRegistry;
@GuardedBy("mLock")
@@ -1208,12 +1218,14 @@
// Avoid invalidation-thrashing by preventing cache invalidations from causing property
// writes if the cache isn't enabled yet. We re-enable writes later when we're
// done initializing.
+ sSnapshotCorked = true;
PackageManager.corkPackageInfoCache();
}
@Override
public void enablePackageCaches() {
// Uncork cache invalidations and allow clients to cache package information.
+ sSnapshotCorked = false;
PackageManager.uncorkPackageInfoCache();
}
}
@@ -1286,18 +1298,23 @@
public String incrementalVersion = Build.VERSION.INCREMENTAL;
}
+ @Watched
private final AppsFilter mAppsFilter;
final PackageParser2.Callback mPackageParserCallback;
// Currently known shared libraries.
- final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = new ArrayMap<>();
- final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mStaticLibsByDeclaringPackage =
- new ArrayMap<>();
+ @Watched
+ final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries =
+ new WatchedArrayMap<>();
+ @Watched
+ final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>>
+ mStaticLibsByDeclaringPackage = new WatchedArrayMap<>();
// Mapping from instrumentation class names to info about them.
- final ArrayMap<ComponentName, ParsedInstrumentation> mInstrumentation =
- new ArrayMap<>();
+ @Watched
+ final WatchedArrayMap<ComponentName, ParsedInstrumentation> mInstrumentation =
+ new WatchedArrayMap<>();
// Packages whose data we have transfered into another package, thus
// should no longer exist.
@@ -1336,15 +1353,22 @@
/** Token for keys in mPendingEnableRollback. */
private int mPendingEnableRollbackToken = 0;
+ @Watched
volatile boolean mSystemReady;
- volatile boolean mSafeMode;
+ @Watched
+ private volatile boolean mSafeMode;
volatile boolean mHasSystemUidErrors;
- private volatile SparseBooleanArray mWebInstantAppsDisabled = new SparseBooleanArray();
+ @Watched
+ private final WatchedSparseBooleanArray mWebInstantAppsDisabled =
+ new WatchedSparseBooleanArray();
- ApplicationInfo mAndroidApplication;
+ @Watched
+ private ApplicationInfo mAndroidApplication;
+ @Watched
final ActivityInfo mResolveActivity = new ActivityInfo();
final ResolveInfo mResolveInfo = new ResolveInfo();
- ComponentName mResolveComponentName;
+ @Watched
+ private ComponentName mResolveComponentName;
AndroidPackage mPlatformPackage;
ComponentName mCustomResolverComponentName;
@@ -1361,8 +1385,10 @@
final ComponentName mInstantAppResolverSettingsComponent;
/** Activity used to install instant applications */
- ActivityInfo mInstantAppInstallerActivity;
- final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
+ @Watched
+ private ActivityInfo mInstantAppInstallerActivity;
+ @Watched
+ private final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
@@ -1744,7 +1770,7 @@
private static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
2 * 60 * 60 * 1000L; /* two hours */
- UserManagerService mUserManager;
+ final UserManagerService mUserManager;
// Stores a list of users whose package restrictions file needs to be updated
private ArraySet<Integer> mDirtyUsers = new ArraySet<>();
@@ -1828,6 +1854,2908 @@
*/
public static void invalidatePackageInfoCache() {
PackageManager.invalidatePackageInfoCache();
+ onChanged();
+ }
+
+ private final Watcher mWatcher = new Watcher() {
+ @Override
+ public void onChange(@Nullable Watchable what) {
+ PackageManagerService.this.onChange(what);
+ }
+ };
+
+ /**
+ * A Snapshot is a subset of PackageManagerService state. A snapshot is either live
+ * or snapped. Live snapshots directly reference PackageManagerService attributes.
+ * Snapped snapshots contain deep copies of the attributes.
+ */
+ private class Snapshot {
+ public static final int LIVE = 1;
+ public static final int SNAPPED = 2;
+
+ public final Settings settings;
+ public final SparseIntArray isolatedOwners;
+ public final WatchedArrayMap<String, AndroidPackage> packages;
+ public final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> sharedLibs;
+ public final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> staticLibs;
+ public final WatchedArrayMap<ComponentName, ParsedInstrumentation> instrumentation;
+ public final WatchedSparseBooleanArray webInstantAppsDisabled;
+ public final ComponentName resolveComponentName;
+ public final ActivityInfo resolveActivity;
+ public final ActivityInfo instantAppInstallerActivity;
+ public final ResolveInfo instantAppInstallerInfo;
+ public final InstantAppRegistry instantAppRegistry;
+ public final ApplicationInfo androidApplication;
+ public final String appPredictionServicePackage;
+ public final AppsFilter appsFilter;
+ public final PackageManagerService service;
+
+ Snapshot(int type) {
+ if (type == Snapshot.SNAPPED) {
+ settings = mSettings.snapshot();
+ isolatedOwners = mIsolatedOwners.clone();
+ packages = mPackages.snapshot();
+ sharedLibs = mSharedLibraries.snapshot();
+ staticLibs = mStaticLibsByDeclaringPackage.snapshot();
+ instrumentation = mInstrumentation.snapshot();
+ resolveComponentName = mResolveComponentName.clone();
+ resolveActivity = new ActivityInfo(mResolveActivity);
+ instantAppInstallerActivity =
+ (mInstantAppInstallerActivity == null)
+ ? null
+ : new ActivityInfo(mInstantAppInstallerActivity);
+ instantAppInstallerInfo = new ResolveInfo(mInstantAppInstallerInfo);
+ webInstantAppsDisabled = mWebInstantAppsDisabled.snapshot();
+ instantAppRegistry = mInstantAppRegistry.snapshot();
+ androidApplication =
+ (mAndroidApplication == null)
+ ? null
+ : new ApplicationInfo(mAndroidApplication);
+ appPredictionServicePackage = mAppPredictionServicePackage;
+ appsFilter = mAppsFilter.snapshot();
+ } else if (type == Snapshot.LIVE) {
+ settings = mSettings;
+ isolatedOwners = mIsolatedOwners;
+ packages = mPackages;
+ sharedLibs = mSharedLibraries;
+ staticLibs = mStaticLibsByDeclaringPackage;
+ instrumentation = mInstrumentation;
+ resolveComponentName = mResolveComponentName;
+ resolveActivity = mResolveActivity;
+ instantAppInstallerActivity = mInstantAppInstallerActivity;
+ instantAppInstallerInfo = mInstantAppInstallerInfo;
+ webInstantAppsDisabled = mWebInstantAppsDisabled;
+ instantAppRegistry = mInstantAppRegistry;
+ androidApplication = mAndroidApplication;
+ appPredictionServicePackage = mAppPredictionServicePackage;
+ appsFilter = mAppsFilter;
+ } else {
+ throw new IllegalArgumentException();
+ }
+ service = PackageManagerService.this;
+ }
+ }
+
+ /**
+ * A computer provides the functional interface to the cache
+ */
+ private interface Computer {
+
+ @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
+ int flags, @PrivateResolveFlags int privateResolveFlags, int filterCallingUid,
+ int userId, boolean resolveForStart, boolean allowDynamicSplits);
+ @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
+ int flags, int userId);
+ @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType,
+ int flags, int userId, int callingUid, boolean includeInstantApps);
+ @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
+ String resolvedType, int flags, int userId, int callingUid,
+ String instantAppPkgName);
+ @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent,
+ String resolvedType, int flags, int filterCallingUid, int userId,
+ boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
+ String instantAppPkgName);
+ @Nullable ComponentName findInstallFailureActivity(String packageName, int filterCallingUid,
+ int userId);
+ ActivityInfo getActivityInfo(ComponentName component, int flags, int userId);
+ ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
+ int filterCallingUid, int userId);
+ ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
+ int filterCallingUid, int userId);
+ AndroidPackage getPackage(String packageName);
+ AndroidPackage getPackage(int uid);
+ ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
+ int filterCallingUid, int userId);
+ ApplicationInfo getApplicationInfo(String packageName, int flags, int userId);
+ ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
+ int filterCallingUid, int userId);
+ ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
+ int filterCallingUid, int userId);
+ ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(Intent intent,
+ int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
+ int userId, boolean debug);
+ ComponentName getDefaultHomeActivity(int userId);
+ ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId);
+ CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType,
+ int flags, int sourceUserId, int parentUserId);
+ Intent getHomeIntent();
+ List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
+ String resolvedType, int userId);
+ List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
+ String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
+ boolean resolveForStart, int userId, Intent intent);
+ List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
+ String instantAppPkgName, @UserIdInt int userId, int filterCallingUid);
+ List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
+ int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
+ int userId);
+ List<ResolveInfo> filterIfNotSystemUser(List<ResolveInfo> resolveInfos, int userId);
+ List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent,
+ String resolvedType, int flags, int userId, boolean resolveForStart,
+ boolean isRequesterInstantApp);
+ PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId);
+ PackageInfo getPackageInfo(String packageName, int flags, int userId);
+ PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags,
+ int filterCallingUid, int userId);
+ PackageInfo getPackageInfoInternalBody(String packageName, long versionCode, int flags,
+ int filterCallingUid, int userId);
+ PackageSetting getPackageSetting(String packageName);
+ PackageSetting getPackageSettingInternal(String packageName, int callingUid);
+ ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId);
+ ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
+ int callingUid);
+ ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, Intent intent,
+ String resolvedType, int flags, int sourceUserId);
+ ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter, int sourceUserId,
+ int targetUserId);
+ ResolveInfo queryCrossProfileIntents(List<CrossProfileIntentFilter> matchingFilters,
+ Intent intent, String resolvedType, int flags, int sourceUserId,
+ boolean matchInCurrentProfile);
+ ResolveInfo querySkipCurrentProfileIntents(List<CrossProfileIntentFilter> matchingFilters,
+ Intent intent, String resolvedType, int flags, int sourceUserId);
+ ServiceInfo getServiceInfo(ComponentName component, int flags, int userId);
+ ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
+ int callingUid);
+ SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version);
+ String getInstantAppPackageName(int callingUid);
+ String resolveExternalPackageNameLPr(AndroidPackage pkg);
+ String resolveInternalPackageNameInternalLocked(String packageName, long versionCode,
+ int callingUid);
+ String resolveInternalPackageNameLPr(String packageName, long versionCode);
+ String[] getPackagesForUid(int uid);
+ String[] getPackagesForUidInternal(int uid, int callingUid);
+ String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
+ boolean isCallerInstantApp);
+ UserInfo getProfileParent(int userId);
+ boolean areWebInstantAppsDisabled(int userId);
+ boolean canViewInstantApps(int callingUid, int userId);
+ boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
+ int flags);
+ boolean hasCrossUserPermission(int callingUid, int callingUserId, int userId,
+ boolean requireFullPermission, boolean requirePermissionWhenSameUser);
+ boolean hasNonNegativePriority(List<ResolveInfo> resolveInfos);
+ boolean hasPermission(String permission);
+ boolean isCallerSameApp(String packageName, int uid);
+ boolean isComponentVisibleToInstantApp(@Nullable ComponentName component);
+ boolean isComponentVisibleToInstantApp(@Nullable ComponentName component,
+ @ComponentType int type);
+ boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
+ String resolvedType, int flags);
+ boolean isInstantApp(String packageName, int userId);
+ boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid);
+ boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId, int callingUid);
+ boolean isInstantAppResolutionAllowed(Intent intent, List<ResolveInfo> resolvedActivities,
+ int userId, boolean skipPackageCheck);
+ boolean isInstantAppResolutionAllowedBody(Intent intent,
+ List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck);
+ boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
+ String resolvedType, int flags);
+ boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId);
+ boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId);
+ boolean isUserEnabled(int userId);
+ boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
+ @Nullable ComponentName component, @ComponentType int componentType, int userId);
+ boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
+ int userId);
+ int bestDomainVerificationStatus(int status1, int status2);
+ int checkUidPermission(String permName, int uid);
+ int getPackageUidInternal(String packageName, int flags, int userId, int callingUid);
+ int updateFlags(int flags, int userId);
+ int updateFlagsForApplication(int flags, int userId);
+ int updateFlagsForComponent(int flags, int userId);
+ int updateFlagsForPackage(int flags, int userId);
+ int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
+ boolean isImplicitImageCaptureIntentAndNotSetByDpc);
+ int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
+ boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc);
+ long getDomainVerificationStatusLPr(PackageSetting ps, int userId);
+ void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
+ boolean requireFullPermission, boolean checkShell, String message);
+ void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+ boolean requireFullPermission, boolean checkShell, String message);
+ void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+ boolean requireFullPermission, boolean checkShell,
+ boolean requirePermissionWhenSameUser, String message);
+ }
+
+ /**
+ * This class contains the implementation of the Computer functions. It
+ * is entirely self-contained - it has no implicit access to
+ * PackageManagerService.
+ */
+ private static class ComputerEngine implements Computer {
+
+ // Cached attributes. The names in this class are the same as the
+ // names in PackageManagerService; see that class for documentation.
+ private final Settings mSettings;
+ private final SparseIntArray mIsolatedOwners;
+ private final WatchedArrayMap<String, AndroidPackage> mPackages;
+ private final WatchedArrayMap<ComponentName, ParsedInstrumentation>
+ mInstrumentation;
+ private final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>>
+ mStaticLibsByDeclaringPackage;
+ private final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>>
+ mSharedLibraries;
+ private final ComponentName mLocalResolveComponentName;
+ private final ActivityInfo mResolveActivity;
+ private final WatchedSparseBooleanArray mWebInstantAppsDisabled;
+ private final ActivityInfo mLocalInstantAppInstallerActivity;
+ private final ResolveInfo mInstantAppInstallerInfo;
+ private final InstantAppRegistry mInstantAppRegistry;
+ private final ApplicationInfo mLocalAndroidApplication;
+
+ // Immutable service attribute
+ private final String mAppPredictionServicePackage;
+
+ // TODO: create cache copies of the following attributes
+ private final AppsFilter mAppsFilter;
+
+ // The following are not cloned since changes to these have never
+ // been guarded by the PMS lock.
+ private final Context mContext;
+ private final UserManagerService mUserManager;
+ private final PermissionManagerServiceInternal mPermissionManager;
+ private final ApexManager mApexManager;
+ private final Injector mInjector;
+ private final ComponentResolver mComponentResolver;
+ private final InstantAppResolverConnection mInstantAppResolverConnection;
+ private final DefaultAppProvider mDefaultAppProvider;
+
+ // PackageManagerService attributes that are primitives are referenced through the
+ // pms object directly. Primitives are the only attributes so referenced.
+ protected final PackageManagerService mService;
+ protected boolean safeMode() {
+ return mService.mSafeMode;
+ }
+ protected ComponentName resolveComponentName() {
+ return mLocalResolveComponentName;
+ }
+ protected ActivityInfo instantAppInstallerActivity() {
+ return mLocalInstantAppInstallerActivity;
+ }
+ protected ApplicationInfo androidApplication() {
+ return mLocalAndroidApplication;
+ }
+
+ ComputerEngine(Snapshot args) {
+ mSettings = args.settings;
+ mIsolatedOwners = args.isolatedOwners;
+ mPackages = args.packages;
+ mSharedLibraries = args.sharedLibs;
+ mStaticLibsByDeclaringPackage = args.staticLibs;
+ mInstrumentation = args.instrumentation;
+ mWebInstantAppsDisabled = args.webInstantAppsDisabled;
+ mLocalResolveComponentName = args.resolveComponentName;
+ mResolveActivity = args.resolveActivity;
+ mLocalInstantAppInstallerActivity = args.instantAppInstallerActivity;
+ mInstantAppInstallerInfo = args.instantAppInstallerInfo;
+ mInstantAppRegistry = args.instantAppRegistry;
+ mLocalAndroidApplication = args.androidApplication;
+ mAppsFilter = args.appsFilter;
+
+ mAppPredictionServicePackage = args.appPredictionServicePackage;
+
+ // The following are not cached copies. Instead they are
+ // references to outside services.
+ mPermissionManager = args.service.mPermissionManager;
+ mUserManager = args.service.mUserManager;
+ mContext = args.service.mContext;
+ mInjector = args.service.mInjector;
+ mApexManager = args.service.mApexManager;
+ mComponentResolver = args.service.mComponentResolver;
+ mInstantAppResolverConnection = args.service.mInstantAppResolverConnection;
+ mDefaultAppProvider = args.service.mDefaultAppProvider;
+
+ // Used to reference PMS attributes that are primitives and which are not
+ // updated under control of the PMS lock.
+ mService = args.service;
+ }
+
+ public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
+ String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
+ int filterCallingUid, int userId, boolean resolveForStart,
+ boolean allowDynamicSplits) {
+ if (!mUserManager.exists(userId)) return Collections.emptyList();
+ final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ false /* requireFullPermission */, false /* checkShell */,
+ "query intent activities");
+ final String pkgName = intent.getPackage();
+ ComponentName comp = intent.getComponent();
+ if (comp == null) {
+ if (intent.getSelector() != null) {
+ intent = intent.getSelector();
+ comp = intent.getComponent();
+ }
+ }
+
+ flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+ comp != null || pkgName != null /*onlyExposedExplicitly*/,
+ isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
+ flags));
+ if (comp != null) {
+ final List<ResolveInfo> list = new ArrayList<>(1);
+ final ActivityInfo ai = getActivityInfo(comp, flags, userId);
+ if (ai != null) {
+ // When specifying an explicit component, we prevent the activity from being
+ // used when either 1) the calling package is normal and the activity is within
+ // an ephemeral application or 2) the calling package is ephemeral and the
+ // activity is not visible to ephemeral applications.
+ final boolean matchInstantApp =
+ (flags & PackageManager.MATCH_INSTANT) != 0;
+ final boolean matchVisibleToInstantAppOnly =
+ (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+ final boolean matchExplicitlyVisibleOnly =
+ (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
+ final boolean isCallerInstantApp =
+ instantAppPkgName != null;
+ final boolean isTargetSameInstantApp =
+ comp.getPackageName().equals(instantAppPkgName);
+ final boolean isTargetInstantApp =
+ (ai.applicationInfo.privateFlags
+ & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
+ final boolean isTargetVisibleToInstantApp =
+ (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
+ final boolean isTargetExplicitlyVisibleToInstantApp =
+ isTargetVisibleToInstantApp
+ && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP)
+ == 0;
+ final boolean isTargetHiddenFromInstantApp =
+ !isTargetVisibleToInstantApp
+ || (matchExplicitlyVisibleOnly
+ && !isTargetExplicitlyVisibleToInstantApp);
+ final boolean blockInstantResolution =
+ !isTargetSameInstantApp
+ && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
+ || (matchVisibleToInstantAppOnly && isCallerInstantApp
+ && isTargetHiddenFromInstantApp));
+ final boolean blockNormalResolution =
+ !resolveForStart && !isTargetInstantApp && !isCallerInstantApp
+ && shouldFilterApplicationLocked(
+ getPackageSettingInternal(ai.applicationInfo.packageName,
+ Process.SYSTEM_UID), filterCallingUid, userId);
+ if (!blockInstantResolution && !blockNormalResolution) {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = ai;
+ list.add(ri);
+ }
+ }
+
+ List<ResolveInfo> result = applyPostResolutionFilter(
+ list, instantAppPkgName, allowDynamicSplits, filterCallingUid,
+ resolveForStart,
+ userId, intent);
+ return result;
+ }
+
+ QueryIntentActivitiesResult lockedResult =
+ queryIntentActivitiesInternalBody(
+ intent, resolvedType, flags, filterCallingUid, userId, resolveForStart,
+ allowDynamicSplits, pkgName, instantAppPkgName);
+ if (lockedResult.answer != null) {
+ return lockedResult.answer;
+ }
+
+ if (lockedResult.addInstant) {
+ String callingPkgName = getInstantAppPackageName(filterCallingUid);
+ boolean isRequesterInstantApp = isInstantApp(callingPkgName, userId);
+ lockedResult.result = maybeAddInstantAppInstaller(lockedResult.result, intent,
+ resolvedType, flags, userId, resolveForStart, isRequesterInstantApp);
+ }
+ if (lockedResult.sortResult) {
+ Collections.sort(lockedResult.result, RESOLVE_PRIORITY_SORTER);
+ }
+ return applyPostResolutionFilter(
+ lockedResult.result, instantAppPkgName, allowDynamicSplits, filterCallingUid,
+ resolveForStart, userId, intent);
+ }
+
+ public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
+ String resolvedType, int flags, int userId) {
+ return queryIntentActivitiesInternal(
+ intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(),
+ userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
+ }
+
+ public @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
+ String resolvedType, int flags, int userId, int callingUid,
+ boolean includeInstantApps) {
+ if (!mUserManager.exists(userId)) return Collections.emptyList();
+ enforceCrossUserOrProfilePermission(callingUid,
+ userId,
+ false /*requireFullPermission*/,
+ false /*checkShell*/,
+ "query intent receivers");
+ final String instantAppPkgName = getInstantAppPackageName(callingUid);
+ flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
+ false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
+ ComponentName comp = intent.getComponent();
+ if (comp == null) {
+ if (intent.getSelector() != null) {
+ intent = intent.getSelector();
+ comp = intent.getComponent();
+ }
+ }
+ if (comp != null) {
+ final List<ResolveInfo> list = new ArrayList<>(1);
+ final ServiceInfo si = getServiceInfo(comp, flags, userId);
+ if (si != null) {
+ // When specifying an explicit component, we prevent the service from being
+ // used when either 1) the service is in an instant application and the
+ // caller is not the same instant application or 2) the calling package is
+ // ephemeral and the activity is not visible to ephemeral applications.
+ final boolean matchInstantApp =
+ (flags & PackageManager.MATCH_INSTANT) != 0;
+ final boolean matchVisibleToInstantAppOnly =
+ (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+ final boolean isCallerInstantApp =
+ instantAppPkgName != null;
+ final boolean isTargetSameInstantApp =
+ comp.getPackageName().equals(instantAppPkgName);
+ final boolean isTargetInstantApp =
+ (si.applicationInfo.privateFlags
+ & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
+ final boolean isTargetHiddenFromInstantApp =
+ (si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
+ final boolean blockInstantResolution =
+ !isTargetSameInstantApp
+ && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
+ || (matchVisibleToInstantAppOnly && isCallerInstantApp
+ && isTargetHiddenFromInstantApp));
+
+ final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
+ && shouldFilterApplicationLocked(
+ getPackageSettingInternal(si.applicationInfo.packageName,
+ Process.SYSTEM_UID), callingUid, userId);
+ if (!blockInstantResolution && !blockNormalResolution) {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.serviceInfo = si;
+ list.add(ri);
+ }
+ }
+ return list;
+ }
+
+ return queryIntentServicesInternalBody(intent, resolvedType, flags,
+ userId, callingUid, instantAppPkgName);
+ }
+
+ public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
+ String resolvedType, int flags, int userId, int callingUid,
+ String instantAppPkgName) {
+ // reader
+ String pkgName = intent.getPackage();
+ if (pkgName == null) {
+ final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+ resolvedType, flags, userId);
+ if (resolveInfos == null) {
+ return Collections.emptyList();
+ }
+ return applyPostServiceResolutionFilter(
+ resolveInfos, instantAppPkgName, userId, callingUid);
+ }
+ final AndroidPackage pkg = mPackages.get(pkgName);
+ if (pkg != null) {
+ final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+ resolvedType, flags, pkg.getServices(),
+ userId);
+ if (resolveInfos == null) {
+ return Collections.emptyList();
+ }
+ return applyPostServiceResolutionFilter(
+ resolveInfos, instantAppPkgName, userId, callingUid);
+ }
+ return Collections.emptyList();
+ }
+
+ public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
+ Intent intent, String resolvedType, int flags, int filterCallingUid, int userId,
+ boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
+ String instantAppPkgName) {
+ // reader
+ boolean sortResult = false;
+ boolean addInstant = false;
+ List<ResolveInfo> result = null;
+ if (pkgName == null) {
+ List<CrossProfileIntentFilter> matchingFilters =
+ getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
+ // Check for results that need to skip the current profile.
+ ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
+ resolvedType, flags, userId);
+ if (xpResolveInfo != null) {
+ List<ResolveInfo> xpResult = new ArrayList<>(1);
+ xpResult.add(xpResolveInfo);
+ return new QueryIntentActivitiesResult(
+ applyPostResolutionFilter(
+ filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
+ allowDynamicSplits, filterCallingUid, resolveForStart, userId,
+ intent));
+ }
+
+ // Check for results in the current profile.
+ result = filterIfNotSystemUser(mComponentResolver.queryActivities(
+ intent, resolvedType, flags, userId), userId);
+ addInstant = isInstantAppResolutionAllowed(intent, result, userId,
+ false /*skipPackageCheck*/);
+ // Check for cross profile results.
+ boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
+ xpResolveInfo = queryCrossProfileIntents(
+ matchingFilters, intent, resolvedType, flags, userId,
+ hasNonNegativePriorityResult);
+ if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {
+ boolean isVisibleToUser = filterIfNotSystemUser(
+ Collections.singletonList(xpResolveInfo), userId).size() > 0;
+ if (isVisibleToUser) {
+ result.add(xpResolveInfo);
+ sortResult = true;
+ }
+ }
+ if (intent.hasWebURI()) {
+ CrossProfileDomainInfo xpDomainInfo = null;
+ final UserInfo parent = getProfileParent(userId);
+ if (parent != null) {
+ xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType,
+ flags, userId, parent.id);
+ }
+ if (xpDomainInfo != null) {
+ if (xpResolveInfo != null) {
+ // If we didn't remove it, the cross-profile ResolveInfo would be twice
+ // in the result.
+ result.remove(xpResolveInfo);
+ }
+ if (result.size() == 0 && !addInstant) {
+ // No result in current profile, but found candidate in parent user.
+ // And we are not going to add ephemeral app, so we can return the
+ // result straight away.
+ result.add(xpDomainInfo.resolveInfo);
+ return new QueryIntentActivitiesResult(
+ applyPostResolutionFilter(result, instantAppPkgName,
+ allowDynamicSplits, filterCallingUid, resolveForStart,
+ userId, intent));
+ }
+ } else if (result.size() <= 1 && !addInstant) {
+ // No result in parent user and <= 1 result in current profile, and we
+ // are not going to add ephemeral app, so we can return the result without
+ // further processing.
+ return new QueryIntentActivitiesResult(
+ applyPostResolutionFilter(result, instantAppPkgName,
+ allowDynamicSplits, filterCallingUid, resolveForStart, userId,
+ intent));
+ }
+ // We have more than one candidate (combining results from current and parent
+ // profile), so we need filtering and sorting.
+ result = filterCandidatesWithDomainPreferredActivitiesLPr(
+ intent, flags, result, xpDomainInfo, userId);
+ sortResult = true;
+ }
+ } else {
+ final PackageSetting setting =
+ getPackageSettingInternal(pkgName, Process.SYSTEM_UID);
+ result = null;
+ if (setting != null && setting.pkg != null && (resolveForStart
+ || !shouldFilterApplicationLocked(setting, filterCallingUid, userId))) {
+ result = filterIfNotSystemUser(mComponentResolver.queryActivities(
+ intent, resolvedType, flags, setting.pkg.getActivities(), userId),
+ userId);
+ }
+ if (result == null || result.size() == 0) {
+ // the caller wants to resolve for a particular package; however, there
+ // were no installed results, so, try to find an ephemeral result
+ addInstant = isInstantAppResolutionAllowed(
+ intent, null /*result*/, userId, true /*skipPackageCheck*/);
+ if (result == null) {
+ result = new ArrayList<>();
+ }
+ }
+ }
+ return new QueryIntentActivitiesResult(sortResult, addInstant, result);
+ }
+
+ /**
+ * Returns the activity component that can handle install failures.
+ * <p>By default, the instant application installer handles failures. However, an
+ * application may want to handle failures on its own. Applications do this by
+ * creating an activity with an intent filter that handles the action
+ * {@link Intent#ACTION_INSTALL_FAILURE}.
+ */
+ public @Nullable ComponentName findInstallFailureActivity(
+ String packageName, int filterCallingUid, int userId) {
+ final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE);
+ failureActivityIntent.setPackage(packageName);
+ // IMPORTANT: disallow dynamic splits to avoid an infinite loop
+ final List<ResolveInfo> result = queryIntentActivitiesInternal(
+ failureActivityIntent, null /*resolvedType*/, 0 /*flags*/,
+ 0 /*privateResolveFlags*/, filterCallingUid, userId, false /*resolveForStart*/,
+ false /*allowDynamicSplits*/);
+ final int NR = result.size();
+ if (NR > 0) {
+ for (int i = 0; i < NR; i++) {
+ final ResolveInfo info = result.get(i);
+ if (info.activityInfo.splitName != null) {
+ continue;
+ }
+ return new ComponentName(packageName, info.activityInfo.name);
+ }
+ }
+ return null;
+ }
+
+ public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
+ return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
+ }
+
+ /**
+ * Important: The provided filterCallingUid is used exclusively to filter out activities
+ * that can be seen based on user state. It's typically the original caller uid prior
+ * to clearing. Because it can only be provided by trusted code, its value can be
+ * trusted and will be used as-is; unlike userId which will be validated by this method.
+ */
+ public ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
+ int filterCallingUid, int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ flags = updateFlagsForComponent(flags, userId);
+
+ if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ false /* requireFullPermission */, false /* checkShell */,
+ "get activity info");
+ }
+
+ return getActivityInfoInternalBody(component, flags, filterCallingUid, userId);
+ }
+
+ public ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
+ int filterCallingUid, int userId) {
+ ParsedActivity a = mComponentResolver.getActivity(component);
+
+ if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
+
+ AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
+ if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
+ PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
+ if (ps == null) return null;
+ if (shouldFilterApplicationLocked(
+ ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
+ return null;
+ }
+ return PackageInfoUtils.generateActivityInfo(pkg,
+ a, flags, ps.readUserState(userId), userId, ps);
+ }
+ if (resolveComponentName().equals(component)) {
+ return PackageParser.generateActivityInfo(
+ mResolveActivity, flags, new PackageUserState(), userId);
+ }
+ return null;
+ }
+
+ public AndroidPackage getPackage(String packageName) {
+ packageName = resolveInternalPackageNameLPr(
+ packageName, PackageManager.VERSION_CODE_HIGHEST);
+ return mPackages.get(packageName);
+ }
+
+ public AndroidPackage getPackage(int uid) {
+ final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID);
+ AndroidPackage pkg = null;
+ final int numPackages = packageNames == null ? 0 : packageNames.length;
+ for (int i = 0; pkg == null && i < numPackages; i++) {
+ pkg = mPackages.get(packageNames[i]);
+ }
+ return pkg;
+ }
+
+ public ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
+ int filterCallingUid, int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
+ return null;
+ }
+ if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
+ return null;
+ }
+ if (ps.pkg == null) {
+ final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
+ if (pInfo != null) {
+ return pInfo.applicationInfo;
+ }
+ return null;
+ }
+ ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(ps.pkg, flags,
+ ps.readUserState(userId), userId, ps);
+ if (ai != null) {
+ ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
+ }
+ return ai;
+ }
+ return null;
+ }
+
+ public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
+ return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
+ }
+
+ /**
+ * Important: The provided filterCallingUid is used exclusively to filter out applications
+ * that can be seen based on user state. It's typically the original caller uid prior
+ * to clearing. Because it can only be provided by trusted code, its value can be
+ * trusted and will be used as-is; unlike userId which will be validated by this method.
+ */
+ public ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
+ int filterCallingUid, int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ flags = updateFlagsForApplication(flags, userId);
+
+ if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ false /* requireFullPermission */, false /* checkShell */,
+ "get application info");
+ }
+
+ return getApplicationInfoInternalBody(packageName, flags, filterCallingUid, userId);
+ }
+
+ public ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
+ int filterCallingUid, int userId) {
+ // writer
+ // Normalize package name to handle renamed packages and static libs
+ packageName = resolveInternalPackageNameLPr(packageName,
+ PackageManager.VERSION_CODE_HIGHEST);
+
+ AndroidPackage p = mPackages.get(packageName);
+ if (DEBUG_PACKAGE_INFO) Log.v(
+ TAG, "getApplicationInfo " + packageName
+ + ": " + p);
+ if (p != null) {
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
+ if (ps == null) return null;
+ if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
+ return null;
+ }
+ if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
+ return null;
+ }
+ // Note: isEnabledLP() does not apply here - always return info
+ ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(
+ p, flags, ps.readUserState(userId), userId, ps);
+ if (ai != null) {
+ ai.packageName = resolveExternalPackageNameLPr(p);
+ }
+ return ai;
+ }
+ if ((flags & PackageManager.MATCH_APEX) != 0) {
+ // For APKs, PackageInfo.applicationInfo is not exactly the same as ApplicationInfo
+ // returned from getApplicationInfo, but for APEX packages difference shouldn't be
+ // very big.
+ // TODO(b/155328545): generate proper application info for APEXes as well.
+ int apexFlags = ApexManager.MATCH_ACTIVE_PACKAGE;
+ if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+ apexFlags = ApexManager.MATCH_FACTORY_PACKAGE;
+ }
+ final PackageInfo pi = mApexManager.getPackageInfo(packageName, apexFlags);
+ if (pi == null) {
+ return null;
+ }
+ return pi.applicationInfo;
+ }
+ if ("android".equals(packageName)||"system".equals(packageName)) {
+ return androidApplication();
+ }
+ if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
+ // Already generates the external package name
+ return generateApplicationInfoFromSettingsLPw(packageName,
+ flags, filterCallingUid, userId);
+ }
+ return null;
+ }
+
+ public ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
+ Intent intent, int matchFlags, List<ResolveInfo> candidates,
+ CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
+ final ArrayList<ResolveInfo> result = new ArrayList<>();
+ final ArrayList<ResolveInfo> alwaysList = new ArrayList<>();
+ final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
+ final ArrayList<ResolveInfo> alwaysAskList = new ArrayList<>();
+ final ArrayList<ResolveInfo> neverList = new ArrayList<>();
+ final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
+ final int count = candidates.size();
+ // First, try to use linked apps. Partition the candidates into four lists:
+ // one for the final results, one for the "do not use ever", one for "undefined status"
+ // and finally one for "browser app type".
+ for (int n=0; n<count; n++) {
+ ResolveInfo info = candidates.get(n);
+ String packageName = info.activityInfo.packageName;
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ // Add to the special match all list (Browser use case)
+ if (info.handleAllWebDataURI) {
+ matchAllList.add(info);
+ continue;
+ }
+ // Try to get the status from User settings first
+ long packedStatus = getDomainVerificationStatusLPr(ps, userId);
+ int status = (int)(packedStatus >> 32);
+ int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+ if (DEBUG_DOMAIN_VERIFICATION || debug) {
+ Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ + " : linkgen=" + linkGeneration);
+ }
+
+ if (!intent.hasCategory(CATEGORY_BROWSABLE)
+ || !intent.hasCategory(CATEGORY_DEFAULT)) {
+ undefinedList.add(info);
+ continue;
+ }
+
+ // Use link-enabled generation as preferredOrder, i.e.
+ // prefer newly-enabled over earlier-enabled.
+ info.preferredOrder = linkGeneration;
+ alwaysList.add(info);
+ } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+ if (DEBUG_DOMAIN_VERIFICATION || debug) {
+ Slog.i(TAG, " + never: " + info.activityInfo.packageName);
+ }
+ neverList.add(info);
+ } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
+ if (DEBUG_DOMAIN_VERIFICATION || debug) {
+ Slog.i(TAG, " + always-ask: " + info.activityInfo.packageName);
+ }
+ alwaysAskList.add(info);
+ } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED ||
+ status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK) {
+ if (DEBUG_DOMAIN_VERIFICATION || debug) {
+ Slog.i(TAG, " + ask: " + info.activityInfo.packageName);
+ }
+ undefinedList.add(info);
+ }
+ }
+ }
+
+ // We'll want to include browser possibilities in a few cases
+ boolean includeBrowser = false;
+
+ // First try to add the "always" resolution(s) for the current user, if any
+ if (alwaysList.size() > 0) {
+ result.addAll(alwaysList);
+ } else {
+ // Add all undefined apps as we want them to appear in the disambiguation dialog.
+ result.addAll(undefinedList);
+ // Maybe add one for the other profile.
+ if (xpDomainInfo != null && (
+ xpDomainInfo.bestDomainVerificationStatus
+ != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) {
+ result.add(xpDomainInfo.resolveInfo);
+ }
+ includeBrowser = true;
+ }
+
+ // The presence of any 'always ask' alternatives means we'll also offer browsers.
+ // If there were 'always' entries their preferred order has been set, so we also
+ // back that off to make the alternatives equivalent
+ if (alwaysAskList.size() > 0) {
+ for (ResolveInfo i : result) {
+ i.preferredOrder = 0;
+ }
+ result.addAll(alwaysAskList);
+ includeBrowser = true;
+ }
+
+ if (includeBrowser) {
+ // Also add browsers (all of them or only the default one)
+ if (DEBUG_DOMAIN_VERIFICATION) {
+ Slog.v(TAG, " ...including browsers in candidate set");
+ }
+ if ((matchFlags & MATCH_ALL) != 0) {
+ result.addAll(matchAllList);
+ } else {
+ // Browser/generic handling case. If there's a default browser, go straight
+ // to that (but only if there is no other higher-priority match).
+ final String defaultBrowserPackageName = mDefaultAppProvider.getDefaultBrowser(
+ userId);
+ int maxMatchPrio = 0;
+ ResolveInfo defaultBrowserMatch = null;
+ final int numCandidates = matchAllList.size();
+ for (int n = 0; n < numCandidates; n++) {
+ ResolveInfo info = matchAllList.get(n);
+ // track the highest overall match priority...
+ if (info.priority > maxMatchPrio) {
+ maxMatchPrio = info.priority;
+ }
+ // ...and the highest-priority default browser match
+ if (info.activityInfo.packageName.equals(defaultBrowserPackageName)) {
+ if (defaultBrowserMatch == null
+ || (defaultBrowserMatch.priority < info.priority)) {
+ if (debug) {
+ Slog.v(TAG, "Considering default browser match " + info);
+ }
+ defaultBrowserMatch = info;
+ }
+ }
+ }
+ if (defaultBrowserMatch != null
+ && defaultBrowserMatch.priority >= maxMatchPrio
+ && !TextUtils.isEmpty(defaultBrowserPackageName))
+ {
+ if (debug) {
+ Slog.v(TAG, "Default browser match " + defaultBrowserMatch);
+ }
+ result.add(defaultBrowserMatch);
+ } else {
+ result.addAll(matchAllList);
+ }
+ }
+
+ // If there is nothing selected, add all candidates and remove the ones that the
+ //user
+ // has explicitly put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state
+ if (result.size() == 0) {
+ result.addAll(candidates);
+ result.removeAll(neverList);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Report the 'Home' activity which is currently set as "always use this one". If non is set
+ * then reports the most likely home activity or null if there are more than one.
+ */
+ public ComponentName getDefaultHomeActivity(int userId) {
+ List<ResolveInfo> allHomeCandidates = new ArrayList<>();
+ ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId);
+ if (cn != null) {
+ return cn;
+ }
+ // TODO: This should not happen since there should always be a default package set for
+ // ROLE_HOME in RoleManager. Continue with a warning log for now.
+ Slog.w(TAG, "Default package for ROLE_HOME is not set in RoleManager");
+
+ // Find the launcher with the highest priority and return that component if there are no
+ // other home activity with the same priority.
+ int lastPriority = Integer.MIN_VALUE;
+ ComponentName lastComponent = null;
+ final int size = allHomeCandidates.size();
+ for (int i = 0; i < size; i++) {
+ final ResolveInfo ri = allHomeCandidates.get(i);
+ if (ri.priority > lastPriority) {
+ lastComponent = ri.activityInfo.getComponentName();
+ lastPriority = ri.priority;
+ } else if (ri.priority == lastPriority) {
+ // Two components found with same priority.
+ lastComponent = null;
+ }
+ }
+ return lastComponent;
+ }
+
+ public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
+ int userId) {
+ Intent intent = getHomeIntent();
+ List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
+ PackageManager.GET_META_DATA, userId);
+ allHomeCandidates.clear();
+ if (resolveInfos == null) {
+ return null;
+ }
+ allHomeCandidates.addAll(resolveInfos);
+
+ final String packageName = mDefaultAppProvider.getDefaultHome(userId);
+ if (packageName == null) {
+ return null;
+ }
+
+ int resolveInfosSize = resolveInfos.size();
+ for (int i = 0; i < resolveInfosSize; i++) {
+ ResolveInfo resolveInfo = resolveInfos.get(i);
+
+ if (resolveInfo.activityInfo != null && TextUtils.equals(
+ resolveInfo.activityInfo.packageName, packageName)) {
+ return new ComponentName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name);
+ }
+ }
+ return null;
+ }
+
+ public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
+ String resolvedType, int flags, int sourceUserId, int parentUserId) {
+ if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
+ sourceUserId)) {
+ return null;
+ }
+ List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
+ resolvedType, flags, parentUserId);
+
+ if (resultTargetUser == null || resultTargetUser.isEmpty()) {
+ return null;
+ }
+ CrossProfileDomainInfo result = null;
+ int size = resultTargetUser.size();
+ for (int i = 0; i < size; i++) {
+ ResolveInfo riTargetUser = resultTargetUser.get(i);
+ // Intent filter verification is only for filters that specify a host. So don't
+ //return
+ // those that handle all web uris.
+ if (riTargetUser.handleAllWebDataURI) {
+ continue;
+ }
+ String packageName = riTargetUser.activityInfo.packageName;
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ continue;
+ }
+ long verificationState = getDomainVerificationStatusLPr(ps, parentUserId);
+ int status = (int)(verificationState >> 32);
+ if (result == null) {
+ result = new CrossProfileDomainInfo();
+ result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
+ sourceUserId, parentUserId);
+ result.bestDomainVerificationStatus = status;
+ } else {
+ result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
+ result.bestDomainVerificationStatus);
+ }
+ }
+ // Don't consider matches with status NEVER across profiles.
+ if (result != null && result.bestDomainVerificationStatus
+ == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+ return null;
+ }
+ return result;
+ }
+
+ public Intent getHomeIntent() {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ return intent;
+ }
+
+ public List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
+ String resolvedType, int userId) {
+ CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
+ if (resolver != null) {
+ return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
+ }
+ return null;
+ }
+
+ /**
+ * Filters out ephemeral activities.
+ * <p>When resolving for an ephemeral app, only activities that 1) are defined in the
+ * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
+ *
+ * @param resolveInfos The pre-filtered list of resolved activities
+ * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
+ * is performed.
+ * @param intent
+ * @return A filtered list of resolved activities.
+ */
+ public List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
+ String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
+ boolean resolveForStart, int userId, Intent intent) {
+ final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
+ for (int i = resolveInfos.size() - 1; i >= 0; i--) {
+ final ResolveInfo info = resolveInfos.get(i);
+ // remove locally resolved instant app web results when disabled
+ if (info.isInstantAppAvailable && blockInstant) {
+ resolveInfos.remove(i);
+ continue;
+ }
+ // allow activities that are defined in the provided package
+ if (allowDynamicSplits
+ && info.activityInfo != null
+ && info.activityInfo.splitName != null
+ && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
+ info.activityInfo.splitName)) {
+ if (instantAppInstallerActivity() == null) {
+ if (DEBUG_INSTALL) {
+ Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
+ }
+ resolveInfos.remove(i);
+ continue;
+ }
+ if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) {
+ resolveInfos.remove(i);
+ continue;
+ }
+ // requested activity is defined in a split that hasn't been installed yet.
+ // add the installer to the resolve list
+ if (DEBUG_INSTALL) {
+ Slog.v(TAG, "Adding installer to the ResolveInfo list");
+ }
+ final ResolveInfo installerInfo = new ResolveInfo(
+ mInstantAppInstallerInfo);
+ final ComponentName installFailureActivity = findInstallFailureActivity(
+ info.activityInfo.packageName, filterCallingUid, userId);
+ installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
+ installFailureActivity,
+ info.activityInfo.packageName,
+ info.activityInfo.applicationInfo.longVersionCode,
+ info.activityInfo.splitName);
+ // add a non-generic filter
+ installerInfo.filter = new IntentFilter();
+
+ // This resolve info may appear in the chooser UI, so let us make it
+ // look as the one it replaces as far as the user is concerned which
+ // requires loading the correct label and icon for the resolve info.
+ installerInfo.resolvePackageName = info.getComponentInfo().packageName;
+ installerInfo.labelRes = info.resolveLabelResId();
+ installerInfo.icon = info.resolveIconResId();
+ installerInfo.isInstantAppAvailable = true;
+ resolveInfos.set(i, installerInfo);
+ continue;
+ }
+ if (ephemeralPkgName == null) {
+ // caller is a full app
+ SettingBase callingSetting =
+ mSettings.getSettingLPr(UserHandle.getAppId(filterCallingUid));
+ PackageSetting resolvedSetting =
+ getPackageSettingInternal(info.activityInfo.packageName, 0);
+ if (resolveForStart
+ || !mAppsFilter.shouldFilterApplication(
+ filterCallingUid, callingSetting, resolvedSetting, userId)) {
+ continue;
+ }
+ } else if (ephemeralPkgName.equals(info.activityInfo.packageName)) {
+ // caller is same app; don't need to apply any other filtering
+ continue;
+ } else if (resolveForStart
+ && (intent.isWebIntent()
+ || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0)
+ && intent.getPackage() == null
+ && intent.getComponent() == null) {
+ // ephemeral apps can launch other ephemeral apps indirectly
+ continue;
+ } else if (((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP)
+ != 0)
+ && !info.activityInfo.applicationInfo.isInstantApp()) {
+ // allow activities that have been explicitly exposed to ephemeral apps
+ continue;
+ }
+ resolveInfos.remove(i);
+ }
+ return resolveInfos;
+ }
+
+ public List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
+ String instantAppPkgName, @UserIdInt int userId, int filterCallingUid) {
+ for (int i = resolveInfos.size() - 1; i >= 0; i--) {
+ final ResolveInfo info = resolveInfos.get(i);
+ if (instantAppPkgName == null) {
+ SettingBase callingSetting =
+ mSettings.getSettingLPr(UserHandle.getAppId(filterCallingUid));
+ PackageSetting resolvedSetting =
+ getPackageSettingInternal(info.serviceInfo.packageName, 0);
+ if (!mAppsFilter.shouldFilterApplication(
+ filterCallingUid, callingSetting, resolvedSetting, userId)) {
+ continue;
+ }
+ }
+ final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp();
+ // allow services that are defined in the provided package
+ if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) {
+ if (info.serviceInfo.splitName != null
+ && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames,
+ info.serviceInfo.splitName)) {
+ // requested service is defined in a split that hasn't been installed yet.
+ // add the installer to the resolve list
+ if (DEBUG_INSTANT) {
+ Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+ }
+ final ResolveInfo installerInfo = new ResolveInfo(
+ mInstantAppInstallerInfo);
+ installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
+ null /* installFailureActivity */,
+ info.serviceInfo.packageName,
+ info.serviceInfo.applicationInfo.longVersionCode,
+ info.serviceInfo.splitName);
+ // add a non-generic filter
+ installerInfo.filter = new IntentFilter();
+ // load resources from the correct package
+ installerInfo.resolvePackageName = info.getComponentInfo().packageName;
+ resolveInfos.set(i, installerInfo);
+ }
+ continue;
+ }
+ // allow services that have been explicitly exposed to ephemeral apps
+ if (!isEphemeralApp
+ && ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP)
+ != 0)) {
+ continue;
+ }
+ resolveInfos.remove(i);
+ }
+ return resolveInfos;
+ }
+
+ public List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
+ int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
+ int userId) {
+ final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
+
+ if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
+ Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " +
+ candidates.size());
+ }
+
+ final ArrayList<ResolveInfo> result =
+ filterCandidatesWithDomainPreferredActivitiesLPrBody(
+ intent, matchFlags, candidates, xpDomainInfo, userId, debug);
+
+ if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
+ Slog.v(TAG, "Filtered results with preferred activities. New candidates count: "
+ + result.size());
+ for (ResolveInfo info : result) {
+ Slog.v(TAG, " + " + info.activityInfo);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Filter out activities with systemUserOnly flag set, when current user is not System.
+ *
+ * @return filtered list
+ */
+ public List<ResolveInfo> filterIfNotSystemUser(List<ResolveInfo> resolveInfos, int userId) {
+ if (userId == UserHandle.USER_SYSTEM) {
+ return resolveInfos;
+ }
+ for (int i = resolveInfos.size() - 1; i >= 0; i--) {
+ ResolveInfo info = resolveInfos.get(i);
+ if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
+ resolveInfos.remove(i);
+ }
+ }
+ return resolveInfos;
+ }
+
+ public List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result,
+ Intent intent,
+ String resolvedType, int flags, int userId, boolean resolveForStart,
+ boolean isRequesterInstantApp) {
+ // first, check to see if we've got an instant app already installed
+ final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
+ ResolveInfo localInstantApp = null;
+ boolean blockResolution = false;
+ if (!alreadyResolvedLocally) {
+ final List<ResolveInfo> instantApps = mComponentResolver.queryActivities(
+ intent,
+ resolvedType,
+ flags
+ | PackageManager.GET_RESOLVED_FILTER
+ | PackageManager.MATCH_INSTANT
+ | PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY,
+ userId);
+ for (int i = instantApps.size() - 1; i >= 0; --i) {
+ final ResolveInfo info = instantApps.get(i);
+ final String packageName = info.activityInfo.packageName;
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
+ if (ps.getInstantApp(userId)) {
+ final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
+ final int status = (int)(packedStatus >> 32);
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+ // there's a local instant application installed, but, the user has
+ // chosen to never use it; skip resolution and don't acknowledge
+ // an instant application is even available
+ if (DEBUG_INSTANT) {
+ Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName);
+ }
+ blockResolution = true;
+ break;
+ } else {
+ // we have a locally installed instant application; skip resolution
+ // but acknowledge there's an instant application available
+ if (DEBUG_INSTANT) {
+ Slog.v(TAG, "Found installed instant app; pkg: " + packageName);
+ }
+ localInstantApp = info;
+ break;
+ }
+ }
+ }
+ }
+ // no app installed, let's see if one's available
+ AuxiliaryResolveInfo auxiliaryResponse = null;
+ if (!blockResolution) {
+ if (localInstantApp == null) {
+ // we don't have an instant app locally, resolve externally
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
+ String token = UUID.randomUUID().toString();
+ InstantAppDigest digest = InstantAppResolver.parseDigest(intent);
+ final InstantAppRequest requestObject =
+ new InstantAppRequest(null /*responseObj*/,
+ intent /*origIntent*/, resolvedType, null /*callingPackage*/,
+ null /*callingFeatureId*/, isRequesterInstantApp, userId,
+ null /*verificationBundle*/, resolveForStart,
+ digest.getDigestPrefixSecure(), token);
+ auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
+ mInstantAppResolverConnection, requestObject);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ } else {
+ // we have an instant application locally, but, we can't admit that since
+ // callers shouldn't be able to determine prior browsing. create a placeholder
+ // auxiliary response so the downstream code behaves as if there's an
+ // instant application available externally. when it comes time to start
+ // the instant application, we'll do the right thing.
+ final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo;
+ auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */,
+ ai.packageName, ai.longVersionCode,
+ null /* splitName */);
+ }
+ }
+ if (intent.isWebIntent() && auxiliaryResponse == null) {
+ return result;
+ }
+ final PackageSetting ps =
+ mSettings.getPackageLPr(instantAppInstallerActivity().packageName);
+ if (ps == null
+ || !ps.readUserState(userId).isEnabled(instantAppInstallerActivity(), 0)) {
+ return result;
+ }
+ final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
+ ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
+ instantAppInstallerActivity(), 0, ps.readUserState(userId), userId);
+ ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ // add a non-generic filter
+ ephemeralInstaller.filter = new IntentFilter();
+ if (intent.getAction() != null) {
+ ephemeralInstaller.filter.addAction(intent.getAction());
+ }
+ if (intent.getData() != null && intent.getData().getPath() != null) {
+ ephemeralInstaller.filter.addDataPath(
+ intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
+ }
+ ephemeralInstaller.isInstantAppAvailable = true;
+ // make sure this resolver is the default
+ ephemeralInstaller.isDefault = true;
+ ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
+ if (DEBUG_INSTANT) {
+ Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+ }
+
+ result.add(ephemeralInstaller);
+ return result;
+ }
+
+ public PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ if (ps == null) {
+ return null;
+ }
+ final int callingUid = Binder.getCallingUid();
+ // Filter out ephemeral app metadata:
+ // * The system/shell/root can see metadata for any app
+ // * An installed app can see metadata for 1) other installed apps
+ // and 2) ephemeral apps that have explicitly interacted with it
+ // * Ephemeral apps can only see their own data and exposed installed apps
+ // * Holding a signature permission allows seeing instant apps
+ if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ return null;
+ }
+
+ if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0
+ && ps.isSystem()) {
+ flags |= MATCH_ANY_USER;
+ }
+
+ final PackageUserState state = ps.readUserState(userId);
+ AndroidPackage p = ps.pkg;
+ if (p != null) {
+ // Compute GIDs only if requested
+ final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY
+ : mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
+ // Compute granted permissions only if package has requested permissions
+ final Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions())
+ ? Collections.emptySet()
+ : mPermissionManager.getGrantedPermissions(ps.name, userId);
+
+ PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
+ ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId, ps);
+
+ if (packageInfo == null) {
+ return null;
+ }
+
+ packageInfo.packageName = packageInfo.applicationInfo.packageName =
+ resolveExternalPackageNameLPr(p);
+
+ return packageInfo;
+ } else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) {
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = ps.name;
+ pi.setLongVersionCode(ps.versionCode);
+ pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null;
+ pi.firstInstallTime = ps.firstInstallTime;
+ pi.lastUpdateTime = ps.lastUpdateTime;
+
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = ps.name;
+ ai.uid = UserHandle.getUid(userId, ps.appId);
+ ai.primaryCpuAbi = ps.primaryCpuAbiString;
+ ai.secondaryCpuAbi = ps.secondaryCpuAbiString;
+ ai.setVersionCode(ps.versionCode);
+ ai.flags = ps.pkgFlags;
+ ai.privateFlags = ps.pkgPrivateFlags;
+ pi.applicationInfo =
+ PackageParser.generateApplicationInfo(ai, flags, state, userId);
+
+ if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
+ + ps.name + "]. Provides a minimum info.");
+ return pi;
+ } else {
+ return null;
+ }
+ }
+
+ public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
+ return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
+ flags, Binder.getCallingUid(), userId);
+ }
+
+ /**
+ * Important: The provided filterCallingUid is used exclusively to filter out packages
+ * that can be seen based on user state. It's typically the original caller uid prior
+ * to clearing. Because it can only be provided by trusted code, its value can be
+ * trusted and will be used as-is; unlike userId which will be validated by this method.
+ */
+ public PackageInfo getPackageInfoInternal(String packageName, long versionCode,
+ int flags, int filterCallingUid, int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ flags = updateFlagsForPackage(flags, userId);
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ false /* requireFullPermission */, false /* checkShell */, "get package info");
+
+ return getPackageInfoInternalBody(packageName, versionCode, flags, filterCallingUid,
+ userId);
+ }
+
+ public PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
+ int flags, int filterCallingUid, int userId) {
+ // reader
+ // Normalize package name to handle renamed packages and static libs
+ packageName = resolveInternalPackageNameLPr(packageName, versionCode);
+
+ final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
+ if (matchFactoryOnly) {
+ // Instant app filtering for APEX modules is ignored
+ if ((flags & MATCH_APEX) != 0) {
+ return mApexManager.getPackageInfo(packageName,
+ ApexManager.MATCH_FACTORY_PACKAGE);
+ }
+ final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
+ if (ps != null) {
+ if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
+ return null;
+ }
+ if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
+ return null;
+ }
+ return generatePackageInfo(ps, flags, userId);
+ }
+ }
+
+ AndroidPackage p = mPackages.get(packageName);
+ if (matchFactoryOnly && p != null && !p.isSystem()) {
+ return null;
+ }
+ if (DEBUG_PACKAGE_INFO)
+ Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
+ if (p != null) {
+ final PackageSetting ps = getPackageSetting(p.getPackageName());
+ if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
+ return null;
+ }
+ if (ps != null && shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
+ return null;
+ }
+
+ return generatePackageInfo(ps, flags, userId);
+ }
+ if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
+ if (ps == null) return null;
+ if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
+ return null;
+ }
+ if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
+ return null;
+ }
+ return generatePackageInfo(ps, flags, userId);
+ }
+ if ((flags & MATCH_APEX) != 0) {
+ return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE);
+ }
+ return null;
+ }
+
+ @Nullable
+ public PackageSetting getPackageSetting(String packageName) {
+ return getPackageSettingInternal(packageName, Binder.getCallingUid());
+ }
+
+ public PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
+ packageName = resolveInternalPackageNameInternalLocked(
+ packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
+ return mSettings.getPackageLPr(packageName);
+ }
+
+ public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return ParceledListSlice.emptyList();
+ }
+ if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
+ flags = updateFlagsForPackage(flags, userId);
+
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "get installed packages");
+
+ return getInstalledPackagesBody(flags, userId, callingUid);
+ }
+
+ public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
+ int callingUid) {
+ // writer
+ final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
+ final boolean listApex = (flags & MATCH_APEX) != 0;
+ final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0;
+
+ ArrayList<PackageInfo> list;
+ if (listUninstalled) {
+ list = new ArrayList<>(mSettings.getPackagesLocked().size());
+ for (PackageSetting ps : mSettings.getPackagesLocked().values()) {
+ if (listFactory) {
+ if (!ps.isSystem()) {
+ continue;
+ }
+ PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
+ if (psDisabled != null) {
+ ps = psDisabled;
+ }
+ }
+ if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
+ continue;
+ }
+ if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ continue;
+ }
+ final PackageInfo pi = generatePackageInfo(ps, flags, userId);
+ if (pi != null) {
+ list.add(pi);
+ }
+ }
+ } else {
+ list = new ArrayList<>(mPackages.size());
+ for (AndroidPackage p : mPackages.values()) {
+ PackageSetting ps = getPackageSetting(p.getPackageName());
+ if (listFactory) {
+ if (!p.isSystem()) {
+ continue;
+ }
+ PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
+ if (psDisabled != null) {
+ ps = psDisabled;
+ }
+ }
+ if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
+ continue;
+ }
+ if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ continue;
+ }
+ final PackageInfo pi = generatePackageInfo(ps, flags, userId);
+ if (pi != null) {
+ list.add(pi);
+ }
+ }
+ }
+ if (listApex) {
+ if (listFactory) {
+ list.addAll(mApexManager.getFactoryPackages());
+ } else {
+ list.addAll(mApexManager.getActivePackages());
+ }
+ if (listUninstalled) {
+ list.addAll(mApexManager.getInactivePackages());
+ }
+ }
+ return new ParceledListSlice<>(list);
+ }
+
+ /**
+ * If the filter's target user can handle the intent and is enabled: returns a ResolveInfo
+ * that
+ * will forward the intent to the filter's target user.
+ * Otherwise, returns null.
+ */
+ public ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter,
+ Intent intent,
+ String resolvedType, int flags, int sourceUserId) {
+ int targetUserId = filter.getTargetUserId();
+ List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
+ resolvedType, flags, targetUserId);
+ if (resultTargetUser != null && isUserEnabled(targetUserId)) {
+ // If all the matches in the target profile are suspended, return null.
+ for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
+ if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_SUSPENDED) == 0) {
+ return createForwardingResolveInfoUnchecked(filter, sourceUserId,
+ targetUserId);
+ }
+ }
+ }
+ return null;
+ }
+
+ public ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter,
+ int sourceUserId, int targetUserId) {
+ ResolveInfo forwardingResolveInfo = new ResolveInfo();
+ final long ident = Binder.clearCallingIdentity();
+ boolean targetIsProfile;
+ try {
+ targetIsProfile = mUserManager.getUserInfo(targetUserId).isManagedProfile();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ String className;
+ if (targetIsProfile) {
+ className = FORWARD_INTENT_TO_MANAGED_PROFILE;
+ } else {
+ className = FORWARD_INTENT_TO_PARENT;
+ }
+ ComponentName forwardingActivityComponentName = new ComponentName(
+ androidApplication().packageName, className);
+ ActivityInfo forwardingActivityInfo =
+ getActivityInfo(forwardingActivityComponentName, 0,
+ sourceUserId);
+ if (!targetIsProfile) {
+ forwardingActivityInfo.showUserIcon = targetUserId;
+ forwardingResolveInfo.noResourceId = true;
+ }
+ forwardingResolveInfo.activityInfo = forwardingActivityInfo;
+ forwardingResolveInfo.priority = 0;
+ forwardingResolveInfo.preferredOrder = 0;
+ forwardingResolveInfo.match = 0;
+ forwardingResolveInfo.isDefault = true;
+ forwardingResolveInfo.filter = filter;
+ forwardingResolveInfo.targetUserId = targetUserId;
+ return forwardingResolveInfo;
+ }
+
+ // Return matching ResolveInfo in target user if any.
+ public ResolveInfo queryCrossProfileIntents(
+ List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
+ int flags, int sourceUserId, boolean matchInCurrentProfile) {
+ if (matchingFilters != null) {
+ // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
+ // match the same intent. For performance reasons, it is better not to
+ // run queryIntent twice for the same userId
+ SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray();
+ int size = matchingFilters.size();
+ for (int i = 0; i < size; i++) {
+ CrossProfileIntentFilter filter = matchingFilters.get(i);
+ int targetUserId = filter.getTargetUserId();
+ boolean skipCurrentProfile =
+ (filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0;
+ boolean skipCurrentProfileIfNoMatchFound =
+ (filter.getFlags() & PackageManager.ONLY_IF_NO_MATCH_FOUND) != 0;
+ if (!skipCurrentProfile && !alreadyTriedUserIds.get(targetUserId)
+ && (!skipCurrentProfileIfNoMatchFound || !matchInCurrentProfile)) {
+ // Checking if there are activities in the target user that can handle the
+ // intent.
+ ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
+ resolvedType, flags, sourceUserId);
+ if (resolveInfo != null) return resolveInfo;
+ alreadyTriedUserIds.put(targetUserId, true);
+ }
+ }
+ }
+ return null;
+ }
+
+ public ResolveInfo querySkipCurrentProfileIntents(
+ List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
+ int flags, int sourceUserId) {
+ if (matchingFilters != null) {
+ int size = matchingFilters.size();
+ for (int i = 0; i < size; i ++) {
+ CrossProfileIntentFilter filter = matchingFilters.get(i);
+ if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
+ // Checking if there are activities in the target user that can handle the
+ // intent.
+ ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
+ resolvedType, flags, sourceUserId);
+ if (resolveInfo != null) {
+ return resolveInfo;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ final int callingUid = Binder.getCallingUid();
+ flags = updateFlagsForComponent(flags, userId);
+ enforceCrossUserOrProfilePermission(callingUid, userId,
+ false /* requireFullPermission */,
+ false /* checkShell */, "get service info");
+ return getServiceInfoBody(component, flags, userId, callingUid);
+ }
+
+ public ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
+ int callingUid) {
+ ParsedService s = mComponentResolver.getService(component);
+ if (DEBUG_PACKAGE_INFO) Log.v(
+ TAG, "getServiceInfo " + component + ": " + s);
+ if (s == null) {
+ return null;
+ }
+
+ AndroidPackage pkg = mPackages.get(s.getPackageName());
+ if (mSettings.isEnabledAndMatchLPr(pkg, s, flags, userId)) {
+ PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
+ if (ps == null) return null;
+ if (shouldFilterApplicationLocked(
+ ps, callingUid, component, TYPE_SERVICE, userId)) {
+ return null;
+ }
+ return PackageInfoUtils.generateServiceInfo(pkg,
+ s, flags, ps.readUserState(userId), userId, ps);
+ }
+ return null;
+ }
+
+ @Nullable
+ public SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
+ return getSharedLibraryInfo(name, version, mSharedLibraries, null);
+ }
+
+ /**
+ * Returns the package name of the calling Uid if it's an instant app. If it isn't
+ * instant, returns {@code null}.
+ */
+ public String getInstantAppPackageName(int callingUid) {
+ // If the caller is an isolated app use the owner's uid for the lookup.
+ if (Process.isIsolated(callingUid)) {
+ callingUid = mIsolatedOwners.get(callingUid);
+ }
+ final int appId = UserHandle.getAppId(callingUid);
+ final Object obj = mSettings.getSettingLPr(appId);
+ if (obj instanceof PackageSetting) {
+ final PackageSetting ps = (PackageSetting) obj;
+ final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
+ return isInstantApp ? ps.pkg.getPackageName() : null;
+ }
+ return null;
+ }
+
+ public String resolveExternalPackageNameLPr(AndroidPackage pkg) {
+ if (pkg.getStaticSharedLibName() != null) {
+ return pkg.getManifestPackageName();
+ }
+ return pkg.getPackageName();
+ }
+
+ public String resolveInternalPackageNameInternalLocked(
+ String packageName, long versionCode, int callingUid) {
+ // Handle renamed packages
+ String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
+ packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
+
+ // Is this a static library?
+ LongSparseArray<SharedLibraryInfo> versionedLib =
+ mStaticLibsByDeclaringPackage.get(packageName);
+ if (versionedLib == null || versionedLib.size() <= 0) {
+ return packageName;
+ }
+
+ // Figure out which lib versions the caller can see
+ LongSparseLongArray versionsCallerCanSee = null;
+ final int callingAppId = UserHandle.getAppId(callingUid);
+ if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
+ && callingAppId != Process.ROOT_UID) {
+ versionsCallerCanSee = new LongSparseLongArray();
+ String libName = versionedLib.valueAt(0).getName();
+ String[] uidPackages = getPackagesForUidInternal(callingUid, callingUid);
+ if (uidPackages != null) {
+ for (String uidPackage : uidPackages) {
+ PackageSetting ps = mSettings.getPackageLPr(uidPackage);
+ final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
+ if (libIdx >= 0) {
+ final long libVersion = ps.usesStaticLibrariesVersions[libIdx];
+ versionsCallerCanSee.append(libVersion, libVersion);
+ }
+ }
+ }
+ }
+
+ // Caller can see nothing - done
+ if (versionsCallerCanSee != null && versionsCallerCanSee.size() <= 0) {
+ return packageName;
+ }
+
+ // Find the version the caller can see and the app version code
+ SharedLibraryInfo highestVersion = null;
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
+ if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
+ libraryInfo.getLongVersion()) < 0) {
+ continue;
+ }
+ final long libVersionCode = libraryInfo.getDeclaringPackage().getLongVersionCode();
+ if (versionCode != PackageManager.VERSION_CODE_HIGHEST) {
+ if (libVersionCode == versionCode) {
+ return libraryInfo.getPackageName();
+ }
+ } else if (highestVersion == null) {
+ highestVersion = libraryInfo;
+ } else if (libVersionCode > highestVersion
+ .getDeclaringPackage().getLongVersionCode()) {
+ highestVersion = libraryInfo;
+ }
+ }
+
+ if (highestVersion != null) {
+ return highestVersion.getPackageName();
+ }
+
+ return packageName;
+ }
+
+ public String resolveInternalPackageNameLPr(String packageName, long versionCode) {
+ final int callingUid = Binder.getCallingUid();
+ return resolveInternalPackageNameInternalLocked(packageName, versionCode,
+ callingUid);
+ }
+
+ /**
+ * <em>IMPORTANT:</em> Not all packages returned by this method may be known
+ * to the system. There are two conditions in which this may occur:
+ * <ol>
+ * <li>The package is on adoptable storage and the device has been removed</li>
+ * <li>The package is being removed and the internal structures are partially updated</li>
+ * </ol>
+ * The second is an artifact of the current data structures and should be fixed. See
+ * b/111075456 for one such instance.
+ * This binder API is cached. If the algorithm in this method changes,
+ * or if the underlying objecs (as returned by getSettingLPr()) change
+ * then the logic that invalidates the cache must be revisited. See
+ * calls to invalidateGetPackagesForUidCache() to locate the points at
+ * which the cache is invalidated.
+ */
+ public String[] getPackagesForUid(int uid) {
+ return getPackagesForUidInternal(uid, Binder.getCallingUid());
+ }
+
+ public String[] getPackagesForUidInternal(int uid, int callingUid) {
+ final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
+ final int userId = UserHandle.getUserId(uid);
+ final int appId = UserHandle.getAppId(uid);
+ return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
+ }
+
+ public String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
+ boolean isCallerInstantApp) {
+ // reader
+ final Object obj = mSettings.getSettingLPr(appId);
+ if (obj instanceof SharedUserSetting) {
+ if (isCallerInstantApp) {
+ return null;
+ }
+ final SharedUserSetting sus = (SharedUserSetting) obj;
+ final int N = sus.packages.size();
+ String[] res = new String[N];
+ int i = 0;
+ for (int index = 0; index < N; index++) {
+ final PackageSetting ps = sus.packages.valueAt(index);
+ if (ps.getInstalled(userId)) {
+ res[i++] = ps.name;
+ }
+ }
+ return ArrayUtils.trimToSize(res, i);
+ } else if (obj instanceof PackageSetting) {
+ final PackageSetting ps = (PackageSetting) obj;
+ if (ps.getInstalled(userId)
+ && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ return new String[]{ps.name};
+ }
+ }
+ return null;
+ }
+
+ public UserInfo getProfileParent(int userId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mUserManager.getProfileParent(userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Returns whether or not instant apps have been disabled remotely.
+ */
+ public boolean areWebInstantAppsDisabled(int userId) {
+ return mWebInstantAppsDisabled.get(userId);
+ }
+
+ /**
+ * Returns whether or not a full application can see an instant application.
+ * <p>
+ * Currently, there are four cases in which this can occur:
+ * <ol>
+ * <li>The calling application is a "special" process. Special processes
+ * are those with a UID < {@link Process#FIRST_APPLICATION_UID}.</li>
+ * <li>The calling application has the permission
+ * {@link android.Manifest.permission#ACCESS_INSTANT_APPS}.</li>
+ * <li>The calling application is the default launcher on the
+ * system partition.</li>
+ * <li>The calling application is the default app prediction service.</li>
+ * </ol>
+ */
+ public boolean canViewInstantApps(int callingUid, int userId) {
+ if (callingUid < Process.FIRST_APPLICATION_UID) {
+ return true;
+ }
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_INSTANT_APPS) == PERMISSION_GRANTED) {
+ return true;
+ }
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.VIEW_INSTANT_APPS) == PERMISSION_GRANTED) {
+ final ComponentName homeComponent = getDefaultHomeActivity(userId);
+ if (homeComponent != null
+ && isCallerSameApp(homeComponent.getPackageName(), callingUid)) {
+ return true;
+ }
+ // TODO(b/122900055) Change/Remove this and replace with new permission role.
+ if (mAppPredictionServicePackage != null
+ && isCallerSameApp(mAppPredictionServicePackage, callingUid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
+ int flags) {
+ // Callers can access only the libs they depend on, otherwise they need to explicitly
+ // ask for the shared libraries given the caller is allowed to access all static libs.
+ if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
+ // System/shell/root get to see all static libs
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
+ || appId == Process.ROOT_UID) {
+ return false;
+ }
+ // Installer gets to see all static libs.
+ if (PackageManager.PERMISSION_GRANTED
+ == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)) {
+ return false;
+ }
+ }
+
+ // No package means no static lib as it is always on internal storage
+ if (ps == null || ps.pkg == null || !ps.pkg.isStaticSharedLibrary()) {
+ return false;
+ }
+
+ final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(
+ ps.pkg.getStaticSharedLibName(), ps.pkg.getStaticSharedLibVersion());
+ if (libraryInfo == null) {
+ return false;
+ }
+
+ final int resolvedUid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
+ final String[] uidPackageNames = getPackagesForUid(resolvedUid);
+ if (uidPackageNames == null) {
+ return true;
+ }
+
+ for (String uidPackageName : uidPackageNames) {
+ if (ps.name.equals(uidPackageName)) {
+ return false;
+ }
+ PackageSetting uidPs = mSettings.getPackageLPr(uidPackageName);
+ if (uidPs != null) {
+ final int index = ArrayUtils.indexOf(uidPs.usesStaticLibraries,
+ libraryInfo.getName());
+ if (index < 0) {
+ continue;
+ }
+ if (uidPs.pkg.getUsesStaticLibrariesVersions()[index]
+ == libraryInfo.getLongVersion()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public boolean hasCrossUserPermission(
+ int callingUid, int callingUserId, int userId, boolean requireFullPermission,
+ boolean requirePermissionWhenSameUser) {
+ if (!requirePermissionWhenSameUser && userId == callingUserId) {
+ return true;
+ }
+ if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) {
+ return true;
+ }
+ if (requireFullPermission) {
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ }
+ return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ }
+
+ /**
+ * @param resolveInfos list of resolve infos in descending priority order
+ * @return if the list contains a resolve info with non-negative priority
+ */
+ public boolean hasNonNegativePriority(List<ResolveInfo> resolveInfos) {
+ return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0;
+ }
+
+ public boolean hasPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ public boolean isCallerSameApp(String packageName, int uid) {
+ AndroidPackage pkg = mPackages.get(packageName);
+ return pkg != null
+ && UserHandle.getAppId(uid) == pkg.getUid();
+ }
+
+ public boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
+ if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) {
+ return true;
+ }
+ if (isComponentVisibleToInstantApp(component, TYPE_SERVICE)) {
+ return true;
+ }
+ if (isComponentVisibleToInstantApp(component, TYPE_PROVIDER)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isComponentVisibleToInstantApp(
+ @Nullable ComponentName component, @ComponentType int type) {
+ if (type == TYPE_ACTIVITY) {
+ final ParsedActivity activity = mComponentResolver.getActivity(component);
+ if (activity == null) {
+ return false;
+ }
+ final boolean visibleToInstantApp =
+ (activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
+ final boolean explicitlyVisibleToInstantApp =
+ (activity.getFlags() & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP)
+ == 0;
+ return visibleToInstantApp && explicitlyVisibleToInstantApp;
+ } else if (type == TYPE_RECEIVER) {
+ final ParsedActivity activity = mComponentResolver.getReceiver(component);
+ if (activity == null) {
+ return false;
+ }
+ final boolean visibleToInstantApp =
+ (activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
+ final boolean explicitlyVisibleToInstantApp =
+ (activity.getFlags() & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP)
+ == 0;
+ return visibleToInstantApp && !explicitlyVisibleToInstantApp;
+ } else if (type == TYPE_SERVICE) {
+ final ParsedService service = mComponentResolver.getService(component);
+ return service != null
+ && (service.getFlags() & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
+ } else if (type == TYPE_PROVIDER) {
+ final ParsedProvider provider = mComponentResolver.getProvider(component);
+ return provider != null
+ && (provider.getFlags() & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
+ } else if (type == TYPE_UNKNOWN) {
+ return isComponentVisibleToInstantApp(component);
+ }
+ return false;
+ }
+
+ /**
+ * From Android R,
+ * camera intents have to match system apps. The only exception to this is if
+ * the DPC has set the camera persistent preferred activity. This case was introduced
+ * because it is important that the DPC has the ability to set both system and non-system
+ * camera persistent preferred activities.
+ *
+ * @return {@code true} if the intent is a camera intent and the persistent preferred
+ * activity was not set by the DPC.
+ */
+ public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
+ String resolvedType, int flags) {
+ return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
+ intent, userId, resolvedType, flags);
+ }
+
+ public boolean isInstantApp(String packageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ false /* checkShell */, "isInstantApp");
+
+ return isInstantAppInternal(packageName, userId, callingUid);
+ }
+
+ public boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
+ int callingUid) {
+ if (HIDE_EPHEMERAL_APIS) {
+ return false;
+ }
+ return isInstantAppInternalBody(packageName, userId, callingUid);
+ }
+
+ public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
+ int callingUid) {
+ if (Process.isIsolated(callingUid)) {
+ callingUid = mIsolatedOwners.get(callingUid);
+ }
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
+ final boolean returnAllowed =
+ ps != null
+ && (isCallerSameApp(packageName, callingUid)
+ || canViewInstantApps(callingUid, userId)
+ || mInstantAppRegistry.isInstantAccessGranted(
+ userId, UserHandle.getAppId(callingUid), ps.appId));
+ if (returnAllowed) {
+ return ps.getInstantApp(userId);
+ }
+ return false;
+ }
+
+ public boolean isInstantAppResolutionAllowed(
+ Intent intent, List<ResolveInfo> resolvedActivities, int userId,
+ boolean skipPackageCheck) {
+ if (mInstantAppResolverConnection == null) {
+ return false;
+ }
+ if (instantAppInstallerActivity() == null) {
+ return false;
+ }
+ if (intent.getComponent() != null) {
+ return false;
+ }
+ if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) {
+ return false;
+ }
+ if (!skipPackageCheck && intent.getPackage() != null) {
+ return false;
+ }
+ if (!intent.isWebIntent()) {
+ // for non web intents, we should not resolve externally if an app already exists to
+ // handle it or if the caller didn't explicitly request it.
+ if ((resolvedActivities != null && resolvedActivities.size() != 0)
+ || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) {
+ return false;
+ }
+ } else {
+ if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
+ return false;
+ } else if (areWebInstantAppsDisabled(userId)) {
+ return false;
+ }
+ }
+ // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
+ // Or if there's already an ephemeral app installed that handles the action
+ return isInstantAppResolutionAllowedBody(intent, resolvedActivities, userId,
+ skipPackageCheck);
+ }
+
+ // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
+ // Or if there's already an ephemeral app installed that handles the action
+ public boolean isInstantAppResolutionAllowedBody(
+ Intent intent, List<ResolveInfo> resolvedActivities, int userId,
+ boolean skipPackageCheck) {
+ final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
+ for (int n = 0; n < count; n++) {
+ final ResolveInfo info = resolvedActivities.get(n);
+ final String packageName = info.activityInfo.packageName;
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ // only check domain verification status if the app is not a browser
+ if (!info.handleAllWebDataURI) {
+ // Try to get the status from User settings first
+ final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
+ final int status = (int) (packedStatus >> 32);
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
+ || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
+ if (DEBUG_INSTANT) {
+ Slog.v(TAG, "DENY instant app;"
+ + " pkg: " + packageName + ", status: " + status);
+ }
+ return false;
+ }
+ }
+ if (ps.getInstantApp(userId)) {
+ if (DEBUG_INSTANT) {
+ Slog.v(TAG, "DENY instant app installed;"
+ + " pkg: " + packageName);
+ }
+ return false;
+ }
+ }
+ }
+ // We've exhausted all ways to deny ephemeral application; let the system look for them.
+ return true;
+ }
+
+ public boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
+ String resolvedType, int flags) {
+ PersistentPreferredIntentResolver ppir =
+ mSettings.getPersistentPreferredActivities(userId);
+ //TODO(b/158003772): Remove double query
+ List<PersistentPreferredActivity> pprefs = ppir != null
+ ? ppir.queryIntent(intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ userId)
+ : new ArrayList<>();
+ for (PersistentPreferredActivity ppa : pprefs) {
+ if (ppa.mIsSetByDpm) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
+ if (!mInjector.getLocalService(ActivityTaskManagerInternal.class)
+ .isCallerRecents(callingUid)) {
+ return false;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (ActivityManager.getCurrentUser() != callingUserId) {
+ return false;
+ }
+ return mUserManager.isSameProfileGroup(callingUserId, targetUserId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ public boolean isUserEnabled(int userId) {
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ UserInfo userInfo = mUserManager.getUserInfo(userId);
+ return userInfo != null && userInfo.isEnabled();
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ /**
+ * Returns whether or not access to the application should be filtered.
+ * <p>
+ * Access may be limited based upon whether the calling or target applications
+ * are instant applications.
+ *
+ * @see #canViewInstantApps(int, int)
+ */
+ public boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
+ @Nullable ComponentName component, @ComponentType int componentType, int userId) {
+ // if we're in an isolated process, get the real calling UID
+ if (Process.isIsolated(callingUid)) {
+ callingUid = mIsolatedOwners.get(callingUid);
+ }
+ final String instantAppPkgName = getInstantAppPackageName(callingUid);
+ final boolean callerIsInstantApp = instantAppPkgName != null;
+ if (ps == null) {
+ if (callerIsInstantApp) {
+ // pretend the application exists, but, needs to be filtered
+ return true;
+ }
+ return false;
+ }
+ // if the target and caller are the same application, don't filter
+ if (isCallerSameApp(ps.name, callingUid)) {
+ return false;
+ }
+ if (callerIsInstantApp) {
+ // both caller and target are both instant, but, different applications, filter
+ if (ps.getInstantApp(userId)) {
+ return true;
+ }
+ // request for a specific component; if it hasn't been explicitly exposed through
+ // property or instrumentation target, filter
+ if (component != null) {
+ final ParsedInstrumentation instrumentation =
+ mInstrumentation.get(component);
+ if (instrumentation != null
+ && isCallerSameApp(instrumentation.getTargetPackage(), callingUid)) {
+ return false;
+ }
+ return !isComponentVisibleToInstantApp(component, componentType);
+ }
+ // request for application; if no components have been explicitly exposed, filter
+ return !ps.pkg.isVisibleToInstantApps();
+ }
+ if (ps.getInstantApp(userId)) {
+ // caller can see all components of all instant applications, don't filter
+ if (canViewInstantApps(callingUid, userId)) {
+ return false;
+ }
+ // request for a specific instant application component, filter
+ if (component != null) {
+ return true;
+ }
+ // request for an instant application; if the caller hasn't been granted access,
+ //filter
+ return !mInstantAppRegistry.isInstantAccessGranted(
+ userId, UserHandle.getAppId(callingUid), ps.appId);
+ }
+ int appId = UserHandle.getAppId(callingUid);
+ final SettingBase callingPs = mSettings.getSettingLPr(appId);
+ return mAppsFilter.shouldFilterApplication(callingUid, callingPs, ps, userId);
+ }
+
+ /**
+ * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
+ */
+ public boolean shouldFilterApplicationLocked(
+ @Nullable PackageSetting ps, int callingUid, int userId) {
+ return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId);
+ }
+
+ /**
+ * Verification statuses are ordered from the worse to the best, except for
+ * INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
+ */
+ public int bestDomainVerificationStatus(int status1, int status2) {
+ if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+ return status2;
+ }
+ if (status2 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+ return status1;
+ }
+ return (int) MathUtils.max(status1, status2);
+ }
+
+ // NOTE: Can't remove without a major refactor. Keep around for now.
+ public int checkUidPermission(String permName, int uid) {
+ return mPermissionManager.checkUidPermission(uid, permName);
+ }
+
+ public int getPackageUidInternal(String packageName, int flags, int userId,
+ int callingUid) {
+ // reader
+ final AndroidPackage p = mPackages.get(packageName);
+ if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
+ PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
+ if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ return -1;
+ }
+ return UserHandle.getUid(userId, p.getUid());
+ }
+ if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
+ if (ps != null && ps.isMatch(flags)
+ && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ return UserHandle.getUid(userId, ps.appId);
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Update given flags based on encryption status of current user.
+ */
+ public int updateFlags(int flags, int userId) {
+ if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) {
+ // Caller expressed an explicit opinion about what encryption
+ // aware/unaware components they want to see, so fall through and
+ // give them what they want
+ } else {
+ // Caller expressed no opinion, so match based on user state
+ if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+ } else {
+ flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE;
+ }
+ }
+ return flags;
+ }
+
+ /**
+ * Update given flags when being used to request {@link ApplicationInfo}.
+ */
+ public int updateFlagsForApplication(int flags, int userId) {
+ return updateFlagsForPackage(flags, userId);
+ }
+
+ /**
+ * Update given flags when being used to request {@link ComponentInfo}.
+ */
+ public int updateFlagsForComponent(int flags, int userId) {
+ return updateFlags(flags, userId);
+ }
+
+ /**
+ * Update given flags when being used to request {@link PackageInfo}.
+ */
+ public int updateFlagsForPackage(int flags, int userId) {
+ final boolean isCallerSystemUser = UserHandle.getCallingUserId()
+ == UserHandle.USER_SYSTEM;
+ if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
+ // require the permission to be held; the calling uid and given user id referring
+ // to the same user is not sufficient
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
+ !isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId),
+ "MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission");
+ } else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0
+ && isCallerSystemUser
+ && mUserManager.hasManagedProfile(UserHandle.USER_SYSTEM)) {
+ // If the caller wants all packages and has a restricted profile associated with it,
+ // then match all users. This is to make sure that launchers that need to access
+ //work
+ // profile apps don't start breaking. TODO: Remove this hack when launchers stop
+ //using
+ // MATCH_UNINSTALLED_PACKAGES to query apps in other profiles. b/31000380
+ flags |= PackageManager.MATCH_ANY_USER;
+ }
+ return updateFlags(flags, userId);
+ }
+
+ /**
+ * Update given flags when being used to request {@link ResolveInfo}.
+ * <p>Instant apps are resolved specially, depending upon context. Minimally,
+ * {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT}
+ * flag set. However, this flag is only honoured in three circumstances:
+ * <ul>
+ * <li>when called from a system process</li>
+ * <li>when the caller holds the permission {@code
+ * android.permission.ACCESS_INSTANT_APPS}</li>
+ * <li>when resolution occurs to start an activity with a {@code android.intent.action.VIEW}
+ * action and a {@code android.intent.category.BROWSABLE} category</li>
+ * </ul>
+ */
+ public int updateFlagsForResolve(int flags, int userId, int callingUid,
+ boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
+ return updateFlagsForResolve(flags, userId, callingUid,
+ wantInstantApps, false /*onlyExposedExplicitly*/,
+ isImplicitImageCaptureIntentAndNotSetByDpc);
+ }
+
+ public int updateFlagsForResolve(int flags, int userId, int callingUid,
+ boolean wantInstantApps, boolean onlyExposedExplicitly,
+ boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
+ // Safe mode means we shouldn't match any third-party components
+ if (safeMode() || isImplicitImageCaptureIntentAndNotSetByDpc) {
+ flags |= PackageManager.MATCH_SYSTEM_ONLY;
+ }
+ if (getInstantAppPackageName(callingUid) != null) {
+ // But, ephemeral apps see both ephemeral and exposed, non-ephemeral components
+ if (onlyExposedExplicitly) {
+ flags |= PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY;
+ }
+ flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY;
+ flags |= PackageManager.MATCH_INSTANT;
+ } else {
+ final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0;
+ final boolean allowMatchInstant = wantInstantApps
+ || (wantMatchInstant && canViewInstantApps(callingUid, userId));
+ flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY
+ | PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY);
+ if (!allowMatchInstant) {
+ flags &= ~PackageManager.MATCH_INSTANT;
+ }
+ }
+ return updateFlagsForComponent(flags, userId);
+ }
+
+ // Returns a packed value as a long:
+ //
+ // high 'int'-sized word: link status: undefined/ask/never/always.
+ // low 'int'-sized word: relative priority among 'always' results.
+ public long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
+ long result = ps.getDomainVerificationStatusForUser(userId);
+ // if none available, get the status
+ if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ if (ps.getIntentFilterVerificationInfo() != null) {
+ result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if the request is from the system or an app that has the appropriate cross-user
+ * permissions defined as follows:
+ * <ul>
+ * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li>
+ * <li>INTERACT_ACROSS_USERS if the given {@code userId} is in a different profile group
+ * to the caller.</li>
+ * <li>Otherwise,
+ * INTERACT_ACROSS_PROFILES if the given {@code userId} is in the same profile
+ * group as the caller.</li>
+ * </ul>
+ *
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
+ public void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
+ boolean requireFullPermission, boolean checkShell, String message) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userId);
+ }
+ if (checkShell) {
+ PackageManagerServiceUtils.enforceShellRestriction(
+ mInjector.getUserManagerInternal(),
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
+ /*requirePermissionWhenSameUser= */ false)) {
+ return;
+ }
+ final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
+ if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
+ mContext,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+ PermissionChecker.PID_UNKNOWN,
+ callingUid,
+ getPackage(callingUid).getPackageName())
+ == PermissionChecker.PERMISSION_GRANTED) {
+ return;
+ }
+ String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
+ callingUid, userId, message, requireFullPermission, isSameProfileGroup);
+ Slog.w(TAG, errorMessage);
+ throw new SecurityException(errorMessage);
+ }
+
+ /**
+ * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
+ *
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
+ public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+ boolean requireFullPermission, boolean checkShell, String message) {
+ enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false,
+ message);
+ }
+
+ /**
+ * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
+ *
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param requirePermissionWhenSameUser When {@code true}, still require the cross user
+ * permission to be held even if the callingUid and
+ * userId
+ * reference the same user.
+ * @param message the message to log on security exception
+ */
+ public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+ boolean requireFullPermission, boolean checkShell,
+ boolean requirePermissionWhenSameUser, String message) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userId);
+ }
+ if (checkShell) {
+ PackageManagerServiceUtils.enforceShellRestriction(
+ mInjector.getUserManagerInternal(),
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (hasCrossUserPermission(
+ callingUid, callingUserId, userId, requireFullPermission,
+ requirePermissionWhenSameUser)) {
+ return;
+ }
+ String errorMessage = buildInvalidCrossUserPermissionMessage(
+ callingUid, userId, message, requireFullPermission);
+ Slog.w(TAG, errorMessage);
+ throw new SecurityException(errorMessage);
+ }
+
+ }
+
+ /**
+ * The live computer differs from the ComputerEngine in the methods that fetch data
+ * from PackageManagerService.
+ **/
+ private static class ComputerEngineLive extends ComputerEngine {
+ ComputerEngineLive(Snapshot args) {
+ super(args);
+ }
+ protected ComponentName resolveComponentName() {
+ return mService.mResolveComponentName;
+ }
+ protected ActivityInfo instantAppInstallerActivity() {
+ return mService.mInstantAppInstallerActivity;
+ }
+ protected ApplicationInfo androidApplication() {
+ return mService.mAndroidApplication;
+ }
+ }
+
+ /**
+ * This subclass is the external interface to the live computer. For each
+ * interface, it takes the PM lock and then delegates to the live
+ * computer engine. This is required because there are no locks taken in
+ * the engine itself.
+ */
+ private static class ComputerLocked extends ComputerEngine {
+ private final Object mLock;
+
+ ComputerLocked(Snapshot args) {
+ super(args);
+ mLock = mService.mLock;
+ }
+
+ protected ComponentName resolveComponentName() {
+ return mService.mResolveComponentName;
+ }
+ protected ActivityInfo instantAppInstallerActivity() {
+ return mService.mInstantAppInstallerActivity;
+ }
+ protected ApplicationInfo androidApplication() {
+ return mService.mAndroidApplication;
+ }
+
+ public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
+ String resolvedType, int flags, int userId, int callingUid,
+ String instantAppPkgName) {
+ synchronized (mLock) {
+ return super.queryIntentServicesInternalBody(intent, resolvedType, flags, userId,
+ callingUid, instantAppPkgName);
+ }
+ }
+ public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent,
+ String resolvedType, int flags, int filterCallingUid, int userId,
+ boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
+ String instantAppPkgName) {
+ synchronized (mLock) {
+ return super.queryIntentActivitiesInternalBody(intent, resolvedType, flags,
+ filterCallingUid, userId, resolveForStart, allowDynamicSplits, pkgName,
+ instantAppPkgName);
+ }
+ }
+ public ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
+ int filterCallingUid, int userId) {
+ synchronized (mLock) {
+ return super.getActivityInfoInternalBody(component, flags, filterCallingUid,
+ userId);
+ }
+ }
+ public AndroidPackage getPackage(String packageName) {
+ synchronized (mLock) {
+ return super.getPackage(packageName);
+ }
+ }
+ public AndroidPackage getPackage(int uid) {
+ synchronized (mLock) {
+ return super.getPackage(uid);
+ }
+ }
+ public ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
+ int filterCallingUid, int userId) {
+ synchronized (mLock) {
+ return super.getApplicationInfoInternalBody(packageName, flags, filterCallingUid,
+ userId);
+ }
+ }
+ public ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
+ Intent intent, int matchFlags, List<ResolveInfo> candidates,
+ CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
+ synchronized (mLock) {
+ return super.filterCandidatesWithDomainPreferredActivitiesLPrBody(intent,
+ matchFlags, candidates, xpDomainInfo, userId, debug);
+ }
+ }
+ public PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
+ int flags, int filterCallingUid, int userId) {
+ synchronized (mLock) {
+ return super.getPackageInfoInternalBody(packageName, versionCode, flags,
+ filterCallingUid, userId);
+ }
+ }
+ public PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
+ synchronized (mLock) {
+ return super.getPackageSettingInternal(packageName, callingUid);
+ }
+ }
+ public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
+ int callingUid) {
+ synchronized (mLock) {
+ return super.getInstalledPackagesBody(flags, userId, callingUid);
+ }
+ }
+ public ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
+ int callingUid) {
+ synchronized (mLock) {
+ return super.getServiceInfoBody(component, flags, userId, callingUid);
+ }
+ }
+ public String getInstantAppPackageName(int callingUid) {
+ synchronized (mLock) {
+ return super.getInstantAppPackageName(callingUid);
+ }
+ }
+ public String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
+ boolean isCallerInstantApp) {
+ synchronized (mLock) {
+ return super.getPackagesForUidInternalBody(callingUid, userId, appId,
+ isCallerInstantApp);
+ }
+ }
+ public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
+ int callingUid) {
+ synchronized (mLock) {
+ return super.isInstantAppInternalBody(packageName, userId, callingUid);
+ }
+ }
+ public boolean isInstantAppResolutionAllowedBody(Intent intent,
+ List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck) {
+ synchronized (mLock) {
+ return super.isInstantAppResolutionAllowedBody(intent, resolvedActivities, userId,
+ skipPackageCheck);
+ }
+ }
+ public int getPackageUidInternal(String packageName, int flags, int userId,
+ int callingUid) {
+ synchronized (mLock) {
+ return super.getPackageUidInternal(packageName, flags, userId, callingUid);
+ }
+ }
+ }
+
+
+ // Compute read-only functions, based on live data.
+ private final Computer mLiveComputer;
+ // A lock-free cache for frequently called functions.
+ private volatile Computer mSnapshotComputer;
+ // If true, the cached computer object is invalid (the cache is stale).
+ // The attribute is static since it may be set from outside classes.
+ private static volatile boolean sSnapshotInvalid = true;
+ // If true, the cache is corked. Do not create a new cache but continue to use the
+ // existing one. This throttles cache creation during periods of churn in Package
+ // Manager.
+ private static volatile boolean sSnapshotCorked = false;
+
+ // A counter of all queries that hit the cache.
+ private AtomicInteger mSnapshotHits = new AtomicInteger(0);
+
+ // The number of queries at the last miss. This is updated when the cache is rebuilt
+ // (guarded by mLock) and is used to report the hit run-length.
+ @GuardedBy("mLock")
+ private int mSnapshotRebuilt = 0;
+
+ // The snapshot disable/enable switch. An image with the flag set true uses snapshots
+ // and an image with the flag set false does not use snapshots.
+ private static final boolean SNAPSHOT_ENABLED = true;
+
+ /**
+ * Return the live or cached computer. The method will rebuild the
+ * cached computer if necessary.
+ */
+ private Computer computer(boolean live) {
+ if (live || !SNAPSHOT_ENABLED) {
+ return mLiveComputer;
+ } else {
+ int hits = 0;
+ if (TRACE_CACHES) {
+ hits = mSnapshotHits.incrementAndGet();
+ }
+ Computer c = mSnapshotComputer;
+ if ((sSnapshotInvalid || (c == null)) && !sSnapshotCorked) {
+ synchronized (mLock) {
+ // Rebuild the computer if it is invalid and if the cache is not
+ // corked. The lock is taken inside the rebuild method. Note that
+ // the cache might be invalidated as it is rebuilt. However, the
+ // cache is still consistent and is current as of the time this
+ // function is entered.
+ if (sSnapshotInvalid) {
+ rebuildSnapshot(hits);
+ }
+ // Guaranteed to be non-null
+ c = mSnapshotComputer;
+ }
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Return the live computer if the thread holds the lock, and the cached
+ * computer otehrwise. This method is for functions that are unsure
+ * which computer to use.
+ **/
+ private Computer computer() {
+ return computer(Thread.holdsLock(mLock));
+ }
+
+ /**
+ * Rebuild the cached computer.
+ */
+ @GuardedBy("mLock")
+ private void rebuildSnapshot(int hits) {
+ mSnapshotComputer = null;
+ sSnapshotInvalid = false;
+ final Snapshot args = new Snapshot(Snapshot.SNAPPED);
+ mSnapshotComputer = new ComputerEngine(args);
+
+ // Still guarded by mLock
+ final int run = hits - mSnapshotRebuilt;
+ mSnapshotRebuilt = hits;
+ if (TRACE_CACHES) {
+ Log.w(TAG, "computer: rebuild after " + run + " hits");
+ }
+ }
+
+ /**
+ * Create a live computer
+ */
+ private ComputerLocked liveComputer() {
+ return new ComputerLocked(new Snapshot(Snapshot.LIVE));
+ }
+
+ /**
+ * This method is called when the state of PackageManagerService changes so as to
+ * invalidate the current snapshot.
+ * @param what The {@link Watchable} that reported the change
+ * @hide
+ */
+ public static void onChange(@Nullable Watchable what) {
+ if (TRACE_CACHES) {
+ Log.e(TAG, "computer: onChange(" + what + ")");
+ }
+ sSnapshotInvalid = true;
+ }
+
+ /**
+ * Report a locally-detected change to observers. The <what> parameter is left null,
+ * but it signifies that the change was detected by PackageManagerService itself.
+ */
+ private static void onChanged() {
+ onChange(null);
}
class PackageHandler extends Handler {
@@ -3093,6 +6021,25 @@
mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName;
mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage;
mResolveComponentName = testParams.resolveComponentName;
+
+ // Create the computer as soon as the state objects have been installed. The
+ // cached computer is the same as the live computer until the end of the
+ // constructor, at which time the invalidation method updates it. The cache is
+ // corked initially to ensure a cached computer is not built until the end of the
+ // constructor.
+ sSnapshotCorked = true;
+ mLiveComputer = liveComputer();
+ mSnapshotComputer = mLiveComputer;
+
+ // Link up the watchers
+ mPackages.registerObserver(mWatcher);
+ mSharedLibraries.registerObserver(mWatcher);
+ mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
+ mInstrumentation.registerObserver(mWatcher);
+ mWebInstantAppsDisabled.registerObserver(mWatcher);
+ mAppsFilter.registerObserver(mWatcher);
+ Watchable.verifyWatchedAttributes(this, mWatcher);
+
mPackages.putAll(testParams.packages);
mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
mSdkVersion = testParams.sdkVersion;
@@ -3102,6 +6049,9 @@
mIsEngBuild = testParams.isEngBuild;
mIsUserDebugBuild = testParams.isUserDebugBuild;
mIncrementalVersion = testParams.incrementalVersion;
+
+ invalidatePackageInfoCache();
+ sSnapshotCorked = false;
}
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
@@ -3229,6 +6179,8 @@
}
}
+ mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager);
+
mDirsToScanAsSystem = new ArrayList<>();
mDirsToScanAsSystem.addAll(injector.getSystemPartitions());
mDirsToScanAsSystem.addAll(scanPartitions);
@@ -3237,6 +6189,24 @@
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
mAppLib32InstallDir = getAppLib32InstallDir();
+ // Link up the watchers
+ mPackages.registerObserver(mWatcher);
+ mSharedLibraries.registerObserver(mWatcher);
+ mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
+ mInstrumentation.registerObserver(mWatcher);
+ mWebInstantAppsDisabled.registerObserver(mWatcher);
+ mAppsFilter.registerObserver(mWatcher);
+ Watchable.verifyWatchedAttributes(this, mWatcher);
+
+ // Create the computer as soon as the state objects have been installed. The
+ // cached computer is the same as the live computer until the end of the
+ // constructor, at which time the invalidation method updates it. The cache is
+ // corked initially to ensure a cached computer is not built until the end of the
+ // constructor.
+ sSnapshotCorked = true;
+ mLiveComputer = liveComputer();
+ mSnapshotComputer = mLiveComputer;
+
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
@@ -3247,7 +6217,6 @@
mHandler = new PackageHandler(handlerThread.getLooper());
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
- mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager);
ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
= systemConfig.getSharedLibraries();
@@ -4616,96 +7585,11 @@
* </ol>
*/
private boolean canViewInstantApps(int callingUid, int userId) {
- if (callingUid < Process.FIRST_APPLICATION_UID) {
- return true;
- }
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_INSTANT_APPS) == PERMISSION_GRANTED) {
- return true;
- }
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.VIEW_INSTANT_APPS) == PERMISSION_GRANTED) {
- final ComponentName homeComponent = getDefaultHomeActivity(userId);
- if (homeComponent != null
- && isCallerSameApp(homeComponent.getPackageName(), callingUid)) {
- return true;
- }
- // TODO(b/122900055) Change/Remove this and replace with new permission role.
- if (mAppPredictionServicePackage != null
- && isCallerSameApp(mAppPredictionServicePackage, callingUid)) {
- return true;
- }
- }
- return false;
+ return computer(true).canViewInstantApps(callingUid, userId);
}
private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
- if (!mUserManager.exists(userId)) return null;
- if (ps == null) {
- return null;
- }
- final int callingUid = Binder.getCallingUid();
- // Filter out ephemeral app metadata:
- // * The system/shell/root can see metadata for any app
- // * An installed app can see metadata for 1) other installed apps
- // and 2) ephemeral apps that have explicitly interacted with it
- // * Ephemeral apps can only see their own data and exposed installed apps
- // * Holding a signature permission allows seeing instant apps
- if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return null;
- }
-
- if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0
- && ps.isSystem()) {
- flags |= MATCH_ANY_USER;
- }
-
- final PackageUserState state = ps.readUserState(userId);
- AndroidPackage p = ps.pkg;
- if (p != null) {
- // Compute GIDs only if requested
- final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY
- : mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
- // Compute granted permissions only if package has requested permissions
- final Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions())
- ? Collections.emptySet()
- : mPermissionManager.getGrantedPermissions(ps.name, userId);
-
- PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId, ps);
-
- if (packageInfo == null) {
- return null;
- }
-
- packageInfo.packageName = packageInfo.applicationInfo.packageName =
- resolveExternalPackageNameLPr(p);
-
- return packageInfo;
- } else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) {
- PackageInfo pi = new PackageInfo();
- pi.packageName = ps.name;
- pi.setLongVersionCode(ps.versionCode);
- pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null;
- pi.firstInstallTime = ps.firstInstallTime;
- pi.lastUpdateTime = ps.lastUpdateTime;
-
- ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = ps.name;
- ai.uid = UserHandle.getUid(userId, ps.appId);
- ai.primaryCpuAbi = ps.primaryCpuAbiString;
- ai.secondaryCpuAbi = ps.secondaryCpuAbiString;
- ai.setVersionCode(ps.versionCode);
- ai.flags = ps.pkgFlags;
- ai.privateFlags = ps.pkgPrivateFlags;
- pi.applicationInfo = PackageParser.generateApplicationInfo(ai, flags, state, userId);
-
- if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
- + ps.name + "]. Provides a minimum info.");
- return pi;
- } else {
- return null;
- }
+ return computer(true).generatePackageInfo(ps, flags, userId);
}
@Override
@@ -4770,8 +7654,8 @@
@Override
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
- return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
- flags, Binder.getCallingUid(), userId);
+ // SNAPSHOT
+ return computer(false).getPackageInfo(packageName, flags, userId);
}
@Override
@@ -4784,128 +7668,29 @@
/**
* Important: The provided filterCallingUid is used exclusively to filter out packages
* that can be seen based on user state. It's typically the original caller uid prior
- * to clearing. Because it can only be provided by trusted code, it's value can be
+ * to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private PackageInfo getPackageInfoInternal(String packageName, long versionCode,
int flags, int filterCallingUid, int userId) {
- if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForPackage(flags, userId);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
- false /* requireFullPermission */, false /* checkShell */, "get package info");
-
- return getPackageInfoInternalBody(packageName, versionCode, flags, filterCallingUid,
- userId);
+ return computer(true).getPackageInfoInternal(packageName, versionCode,
+ flags, filterCallingUid, userId);
}
private PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
int flags, int filterCallingUid, int userId) {
- // reader
- synchronized (mLock) {
- // Normalize package name to handle renamed packages and static libs
- packageName = resolveInternalPackageNameLPr(packageName, versionCode);
-
- final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
- if (matchFactoryOnly) {
- // Instant app filtering for APEX modules is ignored
- if ((flags & MATCH_APEX) != 0) {
- return mApexManager.getPackageInfo(packageName,
- ApexManager.MATCH_FACTORY_PACKAGE);
- }
- final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
- if (ps != null) {
- if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
- return null;
- }
- if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
- return null;
- }
- return generatePackageInfo(ps, flags, userId);
- }
- }
-
- AndroidPackage p = mPackages.get(packageName);
- if (matchFactoryOnly && p != null && !p.isSystem()) {
- return null;
- }
- if (DEBUG_PACKAGE_INFO)
- Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
- if (p != null) {
- final PackageSetting ps = getPackageSetting(p.getPackageName());
- if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
- return null;
- }
- if (ps != null && shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
- return null;
- }
-
- return generatePackageInfo(ps, flags, userId);
- }
- if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps == null) return null;
- if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
- return null;
- }
- if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
- return null;
- }
- return generatePackageInfo(ps, flags, userId);
- }
- if ((flags & MATCH_APEX) != 0) {
- return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE);
- }
- }
- return null;
+ return computer(true).getPackageInfoInternalBody(packageName, versionCode,
+ flags, filterCallingUid, userId);
}
private boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
- if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) {
- return true;
- }
- if (isComponentVisibleToInstantApp(component, TYPE_SERVICE)) {
- return true;
- }
- if (isComponentVisibleToInstantApp(component, TYPE_PROVIDER)) {
- return true;
- }
- return false;
+ return computer(true).isComponentVisibleToInstantApp(component);
}
private boolean isComponentVisibleToInstantApp(
@Nullable ComponentName component, @ComponentType int type) {
- if (type == TYPE_ACTIVITY) {
- final ParsedActivity activity = mComponentResolver.getActivity(component);
- if (activity == null) {
- return false;
- }
- final boolean visibleToInstantApp =
- (activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
- final boolean explicitlyVisibleToInstantApp =
- (activity.getFlags() & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
- return visibleToInstantApp && explicitlyVisibleToInstantApp;
- } else if (type == TYPE_RECEIVER) {
- final ParsedActivity activity = mComponentResolver.getReceiver(component);
- if (activity == null) {
- return false;
- }
- final boolean visibleToInstantApp =
- (activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
- final boolean explicitlyVisibleToInstantApp =
- (activity.getFlags() & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
- return visibleToInstantApp && !explicitlyVisibleToInstantApp;
- } else if (type == TYPE_SERVICE) {
- final ParsedService service = mComponentResolver.getService(component);
- return service != null
- && (service.getFlags() & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
- } else if (type == TYPE_PROVIDER) {
- final ParsedProvider provider = mComponentResolver.getProvider(component);
- return provider != null
- && (provider.getFlags() & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
- } else if (type == TYPE_UNKNOWN) {
- return isComponentVisibleToInstantApp(component);
- }
- return false;
+ return computer(true).isComponentVisibleToInstantApp(
+ component, type);
}
/**
@@ -4919,58 +7704,8 @@
@GuardedBy("mLock")
private boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
@Nullable ComponentName component, @ComponentType int componentType, int userId) {
- // if we're in an isolated process, get the real calling UID
- if (Process.isIsolated(callingUid)) {
- callingUid = mIsolatedOwners.get(callingUid);
- }
- final String instantAppPkgName = getInstantAppPackageName(callingUid);
- final boolean callerIsInstantApp = instantAppPkgName != null;
- if (ps == null) {
- if (callerIsInstantApp) {
- // pretend the application exists, but, needs to be filtered
- return true;
- }
- return false;
- }
- // if the target and caller are the same application, don't filter
- if (isCallerSameApp(ps.name, callingUid)) {
- return false;
- }
- if (callerIsInstantApp) {
- // both caller and target are both instant, but, different applications, filter
- if (ps.getInstantApp(userId)) {
- return true;
- }
- // request for a specific component; if it hasn't been explicitly exposed through
- // property or instrumentation target, filter
- if (component != null) {
- final ParsedInstrumentation instrumentation =
- mInstrumentation.get(component);
- if (instrumentation != null
- && isCallerSameApp(instrumentation.getTargetPackage(), callingUid)) {
- return false;
- }
- return !isComponentVisibleToInstantApp(component, componentType);
- }
- // request for application; if no components have been explicitly exposed, filter
- return !ps.pkg.isVisibleToInstantApps();
- }
- if (ps.getInstantApp(userId)) {
- // caller can see all components of all instant applications, don't filter
- if (canViewInstantApps(callingUid, userId)) {
- return false;
- }
- // request for a specific instant application component, filter
- if (component != null) {
- return true;
- }
- // request for an instant application; if the caller hasn't been granted access, filter
- return !mInstantAppRegistry.isInstantAccessGranted(
- userId, UserHandle.getAppId(callingUid), ps.appId);
- }
- int appId = UserHandle.getAppId(callingUid);
- final SettingBase callingPs = mSettings.getSettingLPr(appId);
- return mAppsFilter.shouldFilterApplication(callingUid, callingPs, ps, userId);
+ return computer(true).shouldFilterApplicationLocked(ps, callingUid,
+ component, componentType, userId);
}
/**
@@ -4979,63 +7714,15 @@
@GuardedBy("mLock")
private boolean shouldFilterApplicationLocked(
@Nullable PackageSetting ps, int callingUid, int userId) {
- return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId);
+ return computer(true).shouldFilterApplicationLocked(
+ ps, callingUid, userId);
}
@GuardedBy("mLock")
private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
int flags) {
- // Callers can access only the libs they depend on, otherwise they need to explicitly
- // ask for the shared libraries given the caller is allowed to access all static libs.
- if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
- // System/shell/root get to see all static libs
- final int appId = UserHandle.getAppId(uid);
- if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
- || appId == Process.ROOT_UID) {
- return false;
- }
- // Installer gets to see all static libs.
- if (PackageManager.PERMISSION_GRANTED
- == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)) {
- return false;
- }
- }
-
- // No package means no static lib as it is always on internal storage
- if (ps == null || ps.pkg == null || !ps.pkg.isStaticSharedLibrary()) {
- return false;
- }
-
- final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(
- ps.pkg.getStaticSharedLibName(), ps.pkg.getStaticSharedLibVersion());
- if (libraryInfo == null) {
- return false;
- }
-
- final int resolvedUid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
- final String[] uidPackageNames = getPackagesForUid(resolvedUid);
- if (uidPackageNames == null) {
- return true;
- }
-
- for (String uidPackageName : uidPackageNames) {
- if (ps.name.equals(uidPackageName)) {
- return false;
- }
- PackageSetting uidPs = mSettings.getPackageLPr(uidPackageName);
- if (uidPs != null) {
- final int index = ArrayUtils.indexOf(uidPs.usesStaticLibraries,
- libraryInfo.getName());
- if (index < 0) {
- continue;
- }
- if (uidPs.pkg.getUsesStaticLibrariesVersions()[index]
- == libraryInfo.getLongVersion()) {
- return false;
- }
- }
- }
- return true;
+ return computer(true).filterSharedLibPackageLPr(ps, uid, userId,
+ flags);
}
@Override
@@ -5105,26 +7792,7 @@
}
private int getPackageUidInternal(String packageName, int flags, int userId, int callingUid) {
- // reader
- synchronized (mLock) {
- final AndroidPackage p = mPackages.get(packageName);
- if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
- PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
- if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return -1;
- }
- return UserHandle.getUid(userId, p.getUid());
- }
- if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && ps.isMatch(flags)
- && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return UserHandle.getUid(userId, ps.appId);
- }
- }
- }
-
- return -1;
+ return computer(true).getPackageUidInternal(packageName, flags, userId, callingUid);
}
@Override
@@ -5171,111 +7839,32 @@
@GuardedBy("mLock")
private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
int filterCallingUid, int userId) {
- if (!mUserManager.exists(userId)) return null;
- PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null) {
- if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
- return null;
- }
- if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
- return null;
- }
- if (ps.pkg == null) {
- final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
- if (pInfo != null) {
- return pInfo.applicationInfo;
- }
- return null;
- }
- ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(ps.pkg, flags,
- ps.readUserState(userId), userId, ps);
- if (ai != null) {
- ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
- }
- return ai;
- }
- return null;
+ return computer(true).generateApplicationInfoFromSettingsLPw(packageName, flags,
+ filterCallingUid, userId);
}
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
- return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
+ // SNAPSHOT
+ return computer(false).getApplicationInfo(packageName, flags, userId);
}
/**
* Important: The provided filterCallingUid is used exclusively to filter out applications
* that can be seen based on user state. It's typically the original caller uid prior
- * to clearing. Because it can only be provided by trusted code, it's value can be
+ * to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
int filterCallingUid, int userId) {
- if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForApplication(flags, userId);
-
- if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
- false /* requireFullPermission */, false /* checkShell */,
- "get application info");
- }
-
- return getApplicationInfoInternalBody(packageName, flags, filterCallingUid, userId);
+ return computer(true).getApplicationInfoInternal(packageName, flags,
+ filterCallingUid, userId);
}
private ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
int filterCallingUid, int userId) {
- // writer
- synchronized (mLock) {
- // Normalize package name to handle renamed packages and static libs
- packageName = resolveInternalPackageNameLPr(packageName,
- PackageManager.VERSION_CODE_HIGHEST);
-
- AndroidPackage p = mPackages.get(packageName);
- if (DEBUG_PACKAGE_INFO) Log.v(
- TAG, "getApplicationInfo " + packageName
- + ": " + p);
- if (p != null) {
- PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps == null) return null;
- if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
- return null;
- }
- if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
- return null;
- }
- // Note: isEnabledLP() does not apply here - always return info
- ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(
- p, flags, ps.readUserState(userId), userId, ps);
- if (ai != null) {
- ai.packageName = resolveExternalPackageNameLPr(p);
- }
- return ai;
- }
- if ((flags & PackageManager.MATCH_APEX) != 0) {
- // For APKs, PackageInfo.applicationInfo is not exactly the same as ApplicationInfo
- // returned from getApplicationInfo, but for APEX packages difference shouldn't be
- // very big.
- // TODO(b/155328545): generate proper application info for APEXes as well.
- int apexFlags = ApexManager.MATCH_ACTIVE_PACKAGE;
- if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
- apexFlags = ApexManager.MATCH_FACTORY_PACKAGE;
- }
- final PackageInfo pi = mApexManager.getPackageInfo(packageName, apexFlags);
- if (pi == null) {
- return null;
- }
- return pi.applicationInfo;
- }
- if ("android".equals(packageName)||"system".equals(packageName)) {
- return mAndroidApplication;
- }
- if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
- // Already generates the external package name
- return generateApplicationInfoFromSettingsLPw(packageName,
- flags, filterCallingUid, userId);
- }
- }
- return null;
+ return computer(true).getApplicationInfoInternalBody(packageName, flags,
+ filterCallingUid, userId);
}
@GuardedBy("mLock")
@@ -5507,56 +8096,28 @@
* Update given flags based on encryption status of current user.
*/
private int updateFlags(int flags, int userId) {
- if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) {
- // Caller expressed an explicit opinion about what encryption
- // aware/unaware components they want to see, so fall through and
- // give them what they want
- } else {
- // Caller expressed no opinion, so match based on user state
- if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
- flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
- } else {
- flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE;
- }
- }
- return flags;
+ return computer(true).updateFlags(flags, userId);
}
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
private int updateFlagsForPackage(int flags, int userId) {
- final boolean isCallerSystemUser = UserHandle.getCallingUserId() == UserHandle.USER_SYSTEM;
- if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
- // require the permission to be held; the calling uid and given user id referring
- // to the same user is not sufficient
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
- !isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId),
- "MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission");
- } else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0 && isCallerSystemUser
- && mUserManager.hasManagedProfile(UserHandle.USER_SYSTEM)) {
- // If the caller wants all packages and has a restricted profile associated with it,
- // then match all users. This is to make sure that launchers that need to access work
- // profile apps don't start breaking. TODO: Remove this hack when launchers stop using
- // MATCH_UNINSTALLED_PACKAGES to query apps in other profiles. b/31000380
- flags |= PackageManager.MATCH_ANY_USER;
- }
- return updateFlags(flags, userId);
+ return computer(true).updateFlagsForPackage(flags, userId);
}
/**
* Update given flags when being used to request {@link ApplicationInfo}.
*/
private int updateFlagsForApplication(int flags, int userId) {
- return updateFlagsForPackage(flags, userId);
+ return computer(true).updateFlagsForApplication(flags, userId);
}
/**
* Update given flags when being used to request {@link ComponentInfo}.
*/
private int updateFlagsForComponent(int flags, int userId) {
- return updateFlags(flags, userId);
+ return computer(true).updateFlagsForComponent(flags, userId);
}
/**
@@ -5584,38 +8145,18 @@
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
- int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
- boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
- return updateFlagsForResolve(flags, userId, callingUid,
- wantInstantApps, false /*onlyExposedExplicitly*/,
- isImplicitImageCaptureIntentAndNotSetByDpc);
+ private int updateFlagsForResolve(int flags, int userId, int callingUid,
+ boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
+ return computer(true).updateFlagsForResolve(flags, userId, callingUid,
+ wantInstantApps, isImplicitImageCaptureIntentAndNotSetByDpc);
}
- int updateFlagsForResolve(int flags, int userId, int callingUid,
+ private int updateFlagsForResolve(int flags, int userId, int callingUid,
boolean wantInstantApps, boolean onlyExposedExplicitly,
boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
- // Safe mode means we shouldn't match any third-party components
- if (mSafeMode || isImplicitImageCaptureIntentAndNotSetByDpc) {
- flags |= PackageManager.MATCH_SYSTEM_ONLY;
- }
- if (getInstantAppPackageName(callingUid) != null) {
- // But, ephemeral apps see both ephemeral and exposed, non-ephemeral components
- if (onlyExposedExplicitly) {
- flags |= PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY;
- }
- flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY;
- flags |= PackageManager.MATCH_INSTANT;
- } else {
- final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0;
- final boolean allowMatchInstant = wantInstantApps
- || (wantMatchInstant && canViewInstantApps(callingUid, userId));
- flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY
- | PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY);
- if (!allowMatchInstant) {
- flags &= ~PackageManager.MATCH_INSTANT;
- }
- }
- return updateFlagsForComponent(flags, userId);
+ return computer(true).updateFlagsForResolve(flags, userId, callingUid,
+ wantInstantApps, onlyExposedExplicitly,
+ isImplicitImageCaptureIntentAndNotSetByDpc);
}
@Override
@@ -5637,69 +8178,30 @@
@Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
- return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
+ // SNAPSHOT
+ return computer(false).getActivityInfo(component, flags, userId);
}
/**
* Important: The provided filterCallingUid is used exclusively to filter out activities
* that can be seen based on user state. It's typically the original caller uid prior
- * to clearing. Because it can only be provided by trusted code, it's value can be
+ * to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
int filterCallingUid, int userId) {
- if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForComponent(flags, userId);
-
- if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
- false /* requireFullPermission */, false /* checkShell */, "get activity info");
- }
-
- return getActivityInfoInternalBody(component, flags, filterCallingUid, userId);
+ return computer(true).getActivityInfoInternal(component, flags,
+ filterCallingUid, userId);
}
private ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
int filterCallingUid, int userId) {
- synchronized (mLock) {
- ParsedActivity a = mComponentResolver.getActivity(component);
-
- if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
-
- AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
- if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
- PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
- if (ps == null) return null;
- if (shouldFilterApplicationLocked(
- ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
- return null;
- }
- return PackageInfoUtils.generateActivityInfo(pkg,
- a, flags, ps.readUserState(userId), userId, ps);
- }
- if (mResolveComponentName.equals(component)) {
- return PackageParser.generateActivityInfo(
- mResolveActivity, flags, new PackageUserState(), userId);
- }
- }
- return null;
+ return computer(true).getActivityInfoInternalBody(component, flags,
+ filterCallingUid, userId);
}
private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
- if (!mInjector.getLocalService(ActivityTaskManagerInternal.class)
- .isCallerRecents(callingUid)) {
- return false;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- final int callingUserId = UserHandle.getUserId(callingUid);
- if (ActivityManager.getCurrentUser() != callingUserId) {
- return false;
- }
- return mUserManager.isSameProfileGroup(callingUserId, targetUserId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ return computer(true).isRecentsAccessingChildProfiles(callingUid, targetUserId);
}
@Override
@@ -5962,37 +8464,14 @@
@Override
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
- if (!mUserManager.exists(userId)) return null;
- final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForComponent(flags, userId);
- enforceCrossUserOrProfilePermission(callingUid, userId, false /* requireFullPermission */,
- false /* checkShell */, "get service info");
- return getServiceInfoBody(component, flags, userId, callingUid);
+ // SNAPSHOT
+ return computer(false).getServiceInfo(component, flags, userId);
}
private ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
int callingUid) {
- synchronized (mLock) {
- ParsedService s = mComponentResolver.getService(component);
- if (DEBUG_PACKAGE_INFO) Log.v(
- TAG, "getServiceInfo " + component + ": " + s);
- if (s == null) {
- return null;
- }
-
- AndroidPackage pkg = mPackages.get(s.getPackageName());
- if (mSettings.isEnabledAndMatchLPr(pkg, s, flags, userId)) {
- PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
- if (ps == null) return null;
- if (shouldFilterApplicationLocked(
- ps, callingUid, component, TYPE_SERVICE, userId)) {
- return null;
- }
- return PackageInfoUtils.generateServiceInfo(pkg,
- s, flags, ps.readUserState(userId), userId, ps);
- }
- }
- return null;
+ return computer(true).getServiceInfoBody(component, flags, userId,
+ callingUid);
}
@Override
@@ -6203,7 +8682,8 @@
// NOTE: Can't remove without a major refactor. Keep around for now.
@Override
public int checkUidPermission(String permName, int uid) {
- return mPermissionManager.checkUidPermission(uid, permName);
+ // SNAPSHOT
+ return computer(false).checkUidPermission(permName, uid);
}
@Override
@@ -6518,45 +8998,18 @@
*/
@Override
public String[] getPackagesForUid(int uid) {
- return getPackagesForUidInternal(uid, Binder.getCallingUid());
+ // SNAPSHOT
+ return computer(false).getPackagesForUid(uid);
}
private String[] getPackagesForUidInternal(int uid, int callingUid) {
- final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
- final int userId = UserHandle.getUserId(uid);
- final int appId = UserHandle.getAppId(uid);
- return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
+ return computer(true).getPackagesForUidInternal(uid, callingUid);
}
private String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
boolean isCallerInstantApp) {
- // reader
- synchronized (mLock) {
- final Object obj = mSettings.getSettingLPr(appId);
- if (obj instanceof SharedUserSetting) {
- if (isCallerInstantApp) {
- return null;
- }
- final SharedUserSetting sus = (SharedUserSetting) obj;
- final int N = sus.packages.size();
- String[] res = new String[N];
- int i = 0;
- for (int index = 0; index < N; index++) {
- final PackageSetting ps = sus.packages.valueAt(index);
- if (ps.getInstalled(userId)) {
- res[i++] = ps.name;
- }
- }
- return ArrayUtils.trimToSize(res, i);
- } else if (obj instanceof PackageSetting) {
- final PackageSetting ps = (PackageSetting) obj;
- if (ps.getInstalled(userId)
- && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return new String[]{ps.name};
- }
- }
- }
- return null;
+ return computer(true).getPackagesForUidInternalBody(callingUid, userId, appId,
+ isCallerInstantApp);
}
@Override
@@ -6844,45 +9297,15 @@
* Returns whether or not instant apps have been disabled remotely.
*/
private boolean areWebInstantAppsDisabled(int userId) {
- return mWebInstantAppsDisabled.get(userId);
+ return computer(true).areWebInstantAppsDisabled(userId);
}
private boolean isInstantAppResolutionAllowed(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
boolean skipPackageCheck) {
- if (mInstantAppResolverConnection == null) {
- return false;
- }
- if (mInstantAppInstallerActivity == null) {
- return false;
- }
- if (intent.getComponent() != null) {
- return false;
- }
- if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) {
- return false;
- }
- if (!skipPackageCheck && intent.getPackage() != null) {
- return false;
- }
- if (!intent.isWebIntent()) {
- // for non web intents, we should not resolve externally if an app already exists to
- // handle it or if the caller didn't explicitly request it.
- if ((resolvedActivities != null && resolvedActivities.size() != 0)
- || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) {
- return false;
- }
- } else {
- if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
- return false;
- } else if (areWebInstantAppsDisabled(userId)) {
- return false;
- }
- }
- // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
- // Or if there's already an ephemeral app installed that handles the action
- return isInstantAppResolutionAllowedBody(intent, resolvedActivities, userId,
- skipPackageCheck);
+ return computer(true).isInstantAppResolutionAllowed(
+ intent, resolvedActivities, userId,
+ skipPackageCheck);
}
// Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
@@ -6890,39 +9313,9 @@
private boolean isInstantAppResolutionAllowedBody(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
boolean skipPackageCheck) {
- synchronized (mLock) {
- final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
- for (int n = 0; n < count; n++) {
- final ResolveInfo info = resolvedActivities.get(n);
- final String packageName = info.activityInfo.packageName;
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null) {
- // only check domain verification status if the app is not a browser
- if (!info.handleAllWebDataURI) {
- // Try to get the status from User settings first
- final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
- final int status = (int) (packedStatus >> 32);
- if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
- || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
- if (DEBUG_INSTANT) {
- Slog.v(TAG, "DENY instant app;"
- + " pkg: " + packageName + ", status: " + status);
- }
- return false;
- }
- }
- if (ps.getInstantApp(userId)) {
- if (DEBUG_INSTANT) {
- Slog.v(TAG, "DENY instant app installed;"
- + " pkg: " + packageName);
- }
- return false;
- }
- }
- }
- }
- // We've exhausted all ways to deny ephemeral application; let the system look for them.
- return true;
+ return computer(true).isInstantAppResolutionAllowedBody(
+ intent, resolvedActivities, userId,
+ skipPackageCheck);
}
private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
@@ -7059,25 +9452,14 @@
@GuardedBy("mLock")
private boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
String resolvedType, int flags) {
- return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
- intent, userId, resolvedType, flags);
+ return computer(true).isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+ resolvedType, flags);
}
private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
String resolvedType, int flags) {
- PersistentPreferredIntentResolver ppir = mSettings.getPersistentPreferredActivities(userId);
- //TODO(b/158003772): Remove double query
- List<PersistentPreferredActivity> pprefs = ppir != null
- ? ppir.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
- userId)
- : new ArrayList<>();
- for (PersistentPreferredActivity ppa : pprefs) {
- if (ppa.mIsSetByDpm) {
- return true;
- }
- }
- return false;
+ return computer(true).isPersistentPreferredActivitySetByDpm(intent, userId,
+ resolvedType, flags);
}
@GuardedBy("mLock")
@@ -7401,21 +9783,13 @@
}
private UserInfo getProfileParent(int userId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mUserManager.getProfileParent(userId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ return computer(true).getProfileParent(userId);
}
private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
String resolvedType, int userId) {
- CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
- if (resolver != null) {
- return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
- }
- return null;
+ return computer(true).getMatchingCrossProfileIntentFilters(intent,
+ resolvedType, userId);
}
@Override
@@ -7436,27 +9810,14 @@
* instant, returns {@code null}.
*/
private String getInstantAppPackageName(int callingUid) {
- synchronized (mLock) {
- // If the caller is an isolated app use the owner's uid for the lookup.
- if (Process.isIsolated(callingUid)) {
- callingUid = mIsolatedOwners.get(callingUid);
- }
- final int appId = UserHandle.getAppId(callingUid);
- final Object obj = mSettings.getSettingLPr(appId);
- if (obj instanceof PackageSetting) {
- final PackageSetting ps = (PackageSetting) obj;
- final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
- return isInstantApp ? ps.pkg.getPackageName() : null;
- }
- }
- return null;
+ // SNAPSHOT
+ return computer(false).getInstantAppPackageName(callingUid);
}
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
- return queryIntentActivitiesInternal(
- intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(),
- userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
+ return computer(true).queryIntentActivitiesInternal(intent,
+ resolvedType, flags, userId);
}
// Collect the results of queryIntentActivitiesInternalBody into a single class
@@ -7479,306 +9840,27 @@
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {
- if (!mUserManager.exists(userId)) return Collections.emptyList();
- final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
- false /* requireFullPermission */, false /* checkShell */,
- "query intent activities");
- final String pkgName = intent.getPackage();
- ComponentName comp = intent.getComponent();
- if (comp == null) {
- if (intent.getSelector() != null) {
- intent = intent.getSelector();
- comp = intent.getComponent();
- }
- }
-
- flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
- comp != null || pkgName != null /*onlyExposedExplicitly*/,
- isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
- flags));
- if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<>(1);
- final ActivityInfo ai = getActivityInfo(comp, flags, userId);
- if (ai != null) {
- // When specifying an explicit component, we prevent the activity from being
- // used when either 1) the calling package is normal and the activity is within
- // an ephemeral application or 2) the calling package is ephemeral and the
- // activity is not visible to ephemeral applications.
- final boolean matchInstantApp =
- (flags & PackageManager.MATCH_INSTANT) != 0;
- final boolean matchVisibleToInstantAppOnly =
- (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
- final boolean matchExplicitlyVisibleOnly =
- (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
- final boolean isCallerInstantApp =
- instantAppPkgName != null;
- final boolean isTargetSameInstantApp =
- comp.getPackageName().equals(instantAppPkgName);
- final boolean isTargetInstantApp =
- (ai.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
- final boolean isTargetVisibleToInstantApp =
- (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
- final boolean isTargetExplicitlyVisibleToInstantApp =
- isTargetVisibleToInstantApp
- && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
- final boolean isTargetHiddenFromInstantApp =
- !isTargetVisibleToInstantApp
- || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
- final boolean blockInstantResolution =
- !isTargetSameInstantApp
- && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
- || (matchVisibleToInstantAppOnly && isCallerInstantApp
- && isTargetHiddenFromInstantApp));
- final boolean blockNormalResolution =
- !resolveForStart && !isTargetInstantApp && !isCallerInstantApp
- && shouldFilterApplicationLocked(
- getPackageSettingInternal(ai.applicationInfo.packageName,
- Process.SYSTEM_UID), filterCallingUid, userId);
- if (!blockInstantResolution && !blockNormalResolution) {
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = ai;
- list.add(ri);
- }
- }
-
- List<ResolveInfo> result = applyPostResolutionFilter(
- list, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,
- userId, intent);
- return result;
- }
-
- QueryIntentActivitiesResult lockedResult =
- queryIntentActivitiesInternalBody(
- intent, resolvedType, flags, filterCallingUid, userId, resolveForStart,
- allowDynamicSplits, pkgName, instantAppPkgName);
- if (lockedResult.answer != null) {
- return lockedResult.answer;
- }
-
- if (lockedResult.addInstant) {
- String callingPkgName = getInstantAppPackageName(filterCallingUid);
- boolean isRequesterInstantApp = isInstantApp(callingPkgName, userId);
- lockedResult.result = maybeAddInstantAppInstaller(lockedResult.result, intent,
- resolvedType, flags, userId, resolveForStart, isRequesterInstantApp);
- }
- if (lockedResult.sortResult) {
- Collections.sort(lockedResult.result, RESOLVE_PRIORITY_SORTER);
- }
- return applyPostResolutionFilter(
- lockedResult.result, instantAppPkgName, allowDynamicSplits, filterCallingUid,
- resolveForStart, userId, intent);
+ return computer(true).queryIntentActivitiesInternal(intent,
+ resolvedType, flags, privateResolveFlags,
+ filterCallingUid, userId, resolveForStart, allowDynamicSplits);
}
private @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
Intent intent, String resolvedType, int flags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
String instantAppPkgName) {
- // reader
- synchronized (mLock) {
- boolean sortResult = false;
- boolean addInstant = false;
- List<ResolveInfo> result = null;
- if (pkgName == null) {
- List<CrossProfileIntentFilter> matchingFilters =
- getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
- // Check for results that need to skip the current profile.
- ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
- resolvedType, flags, userId);
- if (xpResolveInfo != null) {
- List<ResolveInfo> xpResult = new ArrayList<>(1);
- xpResult.add(xpResolveInfo);
- return new QueryIntentActivitiesResult(
- applyPostResolutionFilter(
- filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
- allowDynamicSplits, filterCallingUid, resolveForStart, userId,
- intent));
- }
-
- // Check for results in the current profile.
- result = filterIfNotSystemUser(mComponentResolver.queryActivities(
- intent, resolvedType, flags, userId), userId);
- addInstant = isInstantAppResolutionAllowed(intent, result, userId,
- false /*skipPackageCheck*/);
- // Check for cross profile results.
- boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
- xpResolveInfo = queryCrossProfileIntents(
- matchingFilters, intent, resolvedType, flags, userId,
- hasNonNegativePriorityResult);
- if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {
- boolean isVisibleToUser = filterIfNotSystemUser(
- Collections.singletonList(xpResolveInfo), userId).size() > 0;
- if (isVisibleToUser) {
- result.add(xpResolveInfo);
- sortResult = true;
- }
- }
- if (intent.hasWebURI()) {
- CrossProfileDomainInfo xpDomainInfo = null;
- final UserInfo parent = getProfileParent(userId);
- if (parent != null) {
- xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType,
- flags, userId, parent.id);
- }
- if (xpDomainInfo != null) {
- if (xpResolveInfo != null) {
- // If we didn't remove it, the cross-profile ResolveInfo would be twice
- // in the result.
- result.remove(xpResolveInfo);
- }
- if (result.size() == 0 && !addInstant) {
- // No result in current profile, but found candidate in parent user.
- // And we are not going to add emphemeral app, so we can return the
- // result straight away.
- result.add(xpDomainInfo.resolveInfo);
- return new QueryIntentActivitiesResult(
- applyPostResolutionFilter(result, instantAppPkgName,
- allowDynamicSplits, filterCallingUid, resolveForStart,
- userId, intent));
- }
- } else if (result.size() <= 1 && !addInstant) {
- // No result in parent user and <= 1 result in current profile, and we
- // are not going to add emphemeral app, so we can return the result without
- // further processing.
- return new QueryIntentActivitiesResult(
- applyPostResolutionFilter(result, instantAppPkgName,
- allowDynamicSplits, filterCallingUid, resolveForStart, userId,
- intent));
- }
- // We have more than one candidate (combining results from current and parent
- // profile), so we need filtering and sorting.
- result = filterCandidatesWithDomainPreferredActivitiesLPr(
- intent, flags, result, xpDomainInfo, userId);
- sortResult = true;
- }
- } else {
- final PackageSetting setting =
- getPackageSettingInternal(pkgName, Process.SYSTEM_UID);
- result = null;
- if (setting != null && setting.pkg != null && (resolveForStart
- || !shouldFilterApplicationLocked(setting, filterCallingUid, userId))) {
- result = filterIfNotSystemUser(mComponentResolver.queryActivities(
- intent, resolvedType, flags, setting.pkg.getActivities(), userId),
- userId);
- }
- if (result == null || result.size() == 0) {
- // the caller wants to resolve for a particular package; however, there
- // were no installed results, so, try to find an ephemeral result
- addInstant = isInstantAppResolutionAllowed(
- intent, null /*result*/, userId, true /*skipPackageCheck*/);
- if (result == null) {
- result = new ArrayList<>();
- }
- }
- }
- return new QueryIntentActivitiesResult(sortResult, addInstant, result);
- }
+ return computer(true).queryIntentActivitiesInternalBody(
+ intent, resolvedType, flags, filterCallingUid, userId,
+ resolveForStart, allowDynamicSplits, pkgName,
+ instantAppPkgName);
}
private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent,
String resolvedType, int flags, int userId, boolean resolveForStart,
boolean isRequesterInstantApp) {
- // first, check to see if we've got an instant app already installed
- final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
- ResolveInfo localInstantApp = null;
- boolean blockResolution = false;
- if (!alreadyResolvedLocally) {
- final List<ResolveInfo> instantApps = mComponentResolver.queryActivities(
- intent,
- resolvedType,
- flags
- | PackageManager.GET_RESOLVED_FILTER
- | PackageManager.MATCH_INSTANT
- | PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY,
- userId);
- for (int i = instantApps.size() - 1; i >= 0; --i) {
- final ResolveInfo info = instantApps.get(i);
- final String packageName = info.activityInfo.packageName;
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps.getInstantApp(userId)) {
- final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
- final int status = (int)(packedStatus >> 32);
- if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
- // there's a local instant application installed, but, the user has
- // chosen to never use it; skip resolution and don't acknowledge
- // an instant application is even available
- if (DEBUG_INSTANT) {
- Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName);
- }
- blockResolution = true;
- break;
- } else {
- // we have a locally installed instant application; skip resolution
- // but acknowledge there's an instant application available
- if (DEBUG_INSTANT) {
- Slog.v(TAG, "Found installed instant app; pkg: " + packageName);
- }
- localInstantApp = info;
- break;
- }
- }
- }
- }
- // no app installed, let's see if one's available
- AuxiliaryResolveInfo auxiliaryResponse = null;
- if (!blockResolution) {
- if (localInstantApp == null) {
- // we don't have an instant app locally, resolve externally
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
- String token = UUID.randomUUID().toString();
- InstantAppDigest digest = InstantAppResolver.parseDigest(intent);
- final InstantAppRequest requestObject = new InstantAppRequest(null /*responseObj*/,
- intent /*origIntent*/, resolvedType, null /*callingPackage*/,
- null /*callingFeatureId*/, isRequesterInstantApp, userId,
- null /*verificationBundle*/, resolveForStart,
- digest.getDigestPrefixSecure(), token);
- auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
- mInstantAppResolverConnection, requestObject);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- } else {
- // we have an instant application locally, but, we can't admit that since
- // callers shouldn't be able to determine prior browsing. create a placeholder
- // auxiliary response so the downstream code behaves as if there's an
- // instant application available externally. when it comes time to start
- // the instant application, we'll do the right thing.
- final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo;
- auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */,
- ai.packageName, ai.longVersionCode, null /* splitName */);
- }
- }
- if (intent.isWebIntent() && auxiliaryResponse == null) {
- return result;
- }
- final PackageSetting ps = mSettings.getPackageLPr(mInstantAppInstallerActivity.packageName);
- if (ps == null
- || !ps.readUserState(userId).isEnabled(mInstantAppInstallerActivity, 0)) {
- return result;
- }
- final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
- ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
- mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId);
- ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
- | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
- // add a non-generic filter
- ephemeralInstaller.filter = new IntentFilter();
- if (intent.getAction() != null) {
- ephemeralInstaller.filter.addAction(intent.getAction());
- }
- if (intent.getData() != null && intent.getData().getPath() != null) {
- ephemeralInstaller.filter.addDataPath(
- intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
- }
- ephemeralInstaller.isInstantAppAvailable = true;
- // make sure this resolver is the default
- ephemeralInstaller.isDefault = true;
- ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
- if (DEBUG_INSTANT) {
- Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
- }
-
- result.add(ephemeralInstaller);
- return result;
+ return computer(true).maybeAddInstantAppInstaller(result, intent,
+ resolvedType, flags, userId, resolveForStart,
+ isRequesterInstantApp);
}
private static class CrossProfileDomainInfo {
@@ -7790,48 +9872,8 @@
private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
String resolvedType, int flags, int sourceUserId, int parentUserId) {
- if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
- sourceUserId)) {
- return null;
- }
- List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
- resolvedType, flags, parentUserId);
-
- if (resultTargetUser == null || resultTargetUser.isEmpty()) {
- return null;
- }
- CrossProfileDomainInfo result = null;
- int size = resultTargetUser.size();
- for (int i = 0; i < size; i++) {
- ResolveInfo riTargetUser = resultTargetUser.get(i);
- // Intent filter verification is only for filters that specify a host. So don't return
- // those that handle all web uris.
- if (riTargetUser.handleAllWebDataURI) {
- continue;
- }
- String packageName = riTargetUser.activityInfo.packageName;
- PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps == null) {
- continue;
- }
- long verificationState = getDomainVerificationStatusLPr(ps, parentUserId);
- int status = (int)(verificationState >> 32);
- if (result == null) {
- result = new CrossProfileDomainInfo();
- result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
- sourceUserId, parentUserId);
- result.bestDomainVerificationStatus = status;
- } else {
- result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
- result.bestDomainVerificationStatus);
- }
- }
- // Don't consider matches with status NEVER across profiles.
- if (result != null && result.bestDomainVerificationStatus
- == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
- return null;
- }
- return result;
+ return computer(true).getCrossProfileDomainPreferredLpr(intent,
+ resolvedType, flags, sourceUserId, parentUserId);
}
/**
@@ -7839,23 +9881,11 @@
* INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
*/
private int bestDomainVerificationStatus(int status1, int status2) {
- if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
- return status2;
- }
- if (status2 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
- return status1;
- }
- return (int) MathUtils.max(status1, status2);
+ return computer(true).bestDomainVerificationStatus(status1, status2);
}
private boolean isUserEnabled(int userId) {
- final long callingId = Binder.clearCallingIdentity();
- try {
- UserInfo userInfo = mUserManager.getUserInfo(userId);
- return userInfo != null && userInfo.isEnabled();
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
+ return computer(true).isUserEnabled(userId);
}
/**
@@ -7864,16 +9894,7 @@
* @return filtered list
*/
private List<ResolveInfo> filterIfNotSystemUser(List<ResolveInfo> resolveInfos, int userId) {
- if (userId == UserHandle.USER_SYSTEM) {
- return resolveInfos;
- }
- for (int i = resolveInfos.size() - 1; i >= 0; i--) {
- ResolveInfo info = resolveInfos.get(i);
- if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
- resolveInfos.remove(i);
- }
- }
- return resolveInfos;
+ return computer(true).filterIfNotSystemUser(resolveInfos, userId);
}
/**
@@ -7890,87 +9911,9 @@
private List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent) {
- final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
- for (int i = resolveInfos.size() - 1; i >= 0; i--) {
- final ResolveInfo info = resolveInfos.get(i);
- // remove locally resolved instant app web results when disabled
- if (info.isInstantAppAvailable && blockInstant) {
- resolveInfos.remove(i);
- continue;
- }
- // allow activities that are defined in the provided package
- if (allowDynamicSplits
- && info.activityInfo != null
- && info.activityInfo.splitName != null
- && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
- info.activityInfo.splitName)) {
- if (mInstantAppInstallerActivity == null) {
- if (DEBUG_INSTALL) {
- Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
- }
- resolveInfos.remove(i);
- continue;
- }
- if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) {
- resolveInfos.remove(i);
- continue;
- }
- // requested activity is defined in a split that hasn't been installed yet.
- // add the installer to the resolve list
- if (DEBUG_INSTALL) {
- Slog.v(TAG, "Adding installer to the ResolveInfo list");
- }
- final ResolveInfo installerInfo = new ResolveInfo(
- mInstantAppInstallerInfo);
- final ComponentName installFailureActivity = findInstallFailureActivity(
- info.activityInfo.packageName, filterCallingUid, userId);
- installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
- installFailureActivity,
- info.activityInfo.packageName,
- info.activityInfo.applicationInfo.longVersionCode,
- info.activityInfo.splitName);
- // add a non-generic filter
- installerInfo.filter = new IntentFilter();
-
- // This resolve info may appear in the chooser UI, so let us make it
- // look as the one it replaces as far as the user is concerned which
- // requires loading the correct label and icon for the resolve info.
- installerInfo.resolvePackageName = info.getComponentInfo().packageName;
- installerInfo.labelRes = info.resolveLabelResId();
- installerInfo.icon = info.resolveIconResId();
- installerInfo.isInstantAppAvailable = true;
- resolveInfos.set(i, installerInfo);
- continue;
- }
- if (ephemeralPkgName == null) {
- // caller is a full app
- SettingBase callingSetting =
- mSettings.getSettingLPr(UserHandle.getAppId(filterCallingUid));
- PackageSetting resolvedSetting =
- getPackageSettingInternal(info.activityInfo.packageName, 0);
- if (resolveForStart
- || !mAppsFilter.shouldFilterApplication(
- filterCallingUid, callingSetting, resolvedSetting, userId)) {
- continue;
- }
- } else if (ephemeralPkgName.equals(info.activityInfo.packageName)) {
- // caller is same app; don't need to apply any other filtering
- continue;
- } else if (resolveForStart
- && (intent.isWebIntent()
- || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0)
- && intent.getPackage() == null
- && intent.getComponent() == null) {
- // ephemeral apps can launch other ephemeral apps indirectly
- continue;
- } else if (((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)
- && !info.activityInfo.applicationInfo.isInstantApp()) {
- // allow activities that have been explicitly exposed to ephemeral apps
- continue;
- }
- resolveInfos.remove(i);
- }
- return resolveInfos;
+ return computer(true).applyPostResolutionFilter(resolveInfos,
+ ephemeralPkgName, allowDynamicSplits, filterCallingUid,
+ resolveForStart, userId, intent);
}
/**
@@ -7982,24 +9925,8 @@
*/
private @Nullable ComponentName findInstallFailureActivity(
String packageName, int filterCallingUid, int userId) {
- final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE);
- failureActivityIntent.setPackage(packageName);
- // IMPORTANT: disallow dynamic splits to avoid an infinite loop
- final List<ResolveInfo> result = queryIntentActivitiesInternal(
- failureActivityIntent, null /*resolvedType*/, 0 /*flags*/,
- 0 /*privateResolveFlags*/, filterCallingUid, userId, false /*resolveForStart*/,
- false /*allowDynamicSplits*/);
- final int NR = result.size();
- if (NR > 0) {
- for (int i = 0; i < NR; i++) {
- final ResolveInfo info = result.get(i);
- if (info.activityInfo.splitName != null) {
- continue;
- }
- return new ComponentName(packageName, info.activityInfo.name);
- }
- }
- return null;
+ return computer(true).findInstallFailureActivity(
+ packageName, filterCallingUid, userId);
}
/**
@@ -8007,180 +9934,23 @@
* @return if the list contains a resolve info with non-negative priority
*/
private boolean hasNonNegativePriority(List<ResolveInfo> resolveInfos) {
- return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0;
+ return computer(true).hasNonNegativePriority(resolveInfos);
}
private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
int userId) {
- final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
-
- if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
- Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " +
- candidates.size());
- }
-
- final ArrayList<ResolveInfo> result =
- filterCandidatesWithDomainPreferredActivitiesLPrBody(
- intent, matchFlags, candidates, xpDomainInfo, userId, debug);
-
- if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
- Slog.v(TAG, "Filtered results with preferred activities. New candidates count: "
- + result.size());
- for (ResolveInfo info : result) {
- Slog.v(TAG, " + " + info.activityInfo);
- }
- }
- return result;
+ return computer(true).filterCandidatesWithDomainPreferredActivitiesLPr(intent,
+ matchFlags, candidates, xpDomainInfo,
+ userId);
}
private ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
Intent intent, int matchFlags, List<ResolveInfo> candidates,
CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
- synchronized (mLock) {
- final ArrayList<ResolveInfo> result = new ArrayList<>();
- final ArrayList<ResolveInfo> alwaysList = new ArrayList<>();
- final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
- final ArrayList<ResolveInfo> alwaysAskList = new ArrayList<>();
- final ArrayList<ResolveInfo> neverList = new ArrayList<>();
- final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
- final int count = candidates.size();
- // First, try to use linked apps. Partition the candidates into four lists:
- // one for the final results, one for the "do not use ever", one for "undefined status"
- // and finally one for "browser app type".
- for (int n=0; n<count; n++) {
- ResolveInfo info = candidates.get(n);
- String packageName = info.activityInfo.packageName;
- PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null) {
- // Add to the special match all list (Browser use case)
- if (info.handleAllWebDataURI) {
- matchAllList.add(info);
- continue;
- }
- // Try to get the status from User settings first
- long packedStatus = getDomainVerificationStatusLPr(ps, userId);
- int status = (int)(packedStatus >> 32);
- int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
- if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
- if (DEBUG_DOMAIN_VERIFICATION || debug) {
- Slog.i(TAG, " + always: " + info.activityInfo.packageName
- + " : linkgen=" + linkGeneration);
- }
-
- if (!intent.hasCategory(CATEGORY_BROWSABLE)
- || !intent.hasCategory(CATEGORY_DEFAULT)) {
- undefinedList.add(info);
- continue;
- }
-
- // Use link-enabled generation as preferredOrder, i.e.
- // prefer newly-enabled over earlier-enabled.
- info.preferredOrder = linkGeneration;
- alwaysList.add(info);
- } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
- if (DEBUG_DOMAIN_VERIFICATION || debug) {
- Slog.i(TAG, " + never: " + info.activityInfo.packageName);
- }
- neverList.add(info);
- } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
- if (DEBUG_DOMAIN_VERIFICATION || debug) {
- Slog.i(TAG, " + always-ask: " + info.activityInfo.packageName);
- }
- alwaysAskList.add(info);
- } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED ||
- status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK) {
- if (DEBUG_DOMAIN_VERIFICATION || debug) {
- Slog.i(TAG, " + ask: " + info.activityInfo.packageName);
- }
- undefinedList.add(info);
- }
- }
- }
-
- // We'll want to include browser possibilities in a few cases
- boolean includeBrowser = false;
-
- // First try to add the "always" resolution(s) for the current user, if any
- if (alwaysList.size() > 0) {
- result.addAll(alwaysList);
- } else {
- // Add all undefined apps as we want them to appear in the disambiguation dialog.
- result.addAll(undefinedList);
- // Maybe add one for the other profile.
- if (xpDomainInfo != null && (
- xpDomainInfo.bestDomainVerificationStatus
- != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) {
- result.add(xpDomainInfo.resolveInfo);
- }
- includeBrowser = true;
- }
-
- // The presence of any 'always ask' alternatives means we'll also offer browsers.
- // If there were 'always' entries their preferred order has been set, so we also
- // back that off to make the alternatives equivalent
- if (alwaysAskList.size() > 0) {
- for (ResolveInfo i : result) {
- i.preferredOrder = 0;
- }
- result.addAll(alwaysAskList);
- includeBrowser = true;
- }
-
- if (includeBrowser) {
- // Also add browsers (all of them or only the default one)
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.v(TAG, " ...including browsers in candidate set");
- }
- if ((matchFlags & MATCH_ALL) != 0) {
- result.addAll(matchAllList);
- } else {
- // Browser/generic handling case. If there's a default browser, go straight
- // to that (but only if there is no other higher-priority match).
- final String defaultBrowserPackageName = mDefaultAppProvider.getDefaultBrowser(
- userId);
- int maxMatchPrio = 0;
- ResolveInfo defaultBrowserMatch = null;
- final int numCandidates = matchAllList.size();
- for (int n = 0; n < numCandidates; n++) {
- ResolveInfo info = matchAllList.get(n);
- // track the highest overall match priority...
- if (info.priority > maxMatchPrio) {
- maxMatchPrio = info.priority;
- }
- // ...and the highest-priority default browser match
- if (info.activityInfo.packageName.equals(defaultBrowserPackageName)) {
- if (defaultBrowserMatch == null
- || (defaultBrowserMatch.priority < info.priority)) {
- if (debug) {
- Slog.v(TAG, "Considering default browser match " + info);
- }
- defaultBrowserMatch = info;
- }
- }
- }
- if (defaultBrowserMatch != null
- && defaultBrowserMatch.priority >= maxMatchPrio
- && !TextUtils.isEmpty(defaultBrowserPackageName))
- {
- if (debug) {
- Slog.v(TAG, "Default browser match " + defaultBrowserMatch);
- }
- result.add(defaultBrowserMatch);
- } else {
- result.addAll(matchAllList);
- }
- }
-
- // If there is nothing selected, add all candidates and remove the ones that the user
- // has explicitly put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state
- if (result.size() == 0) {
- result.addAll(candidates);
- result.removeAll(neverList);
- }
- }
- return result;
- }
+ return computer(true).filterCandidatesWithDomainPreferredActivitiesLPrBody(
+ intent, matchFlags, candidates,
+ xpDomainInfo, userId, debug);
}
// Returns a packed value as a long:
@@ -8188,66 +9958,24 @@
// high 'int'-sized word: link status: undefined/ask/never/always.
// low 'int'-sized word: relative priority among 'always' results.
private long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
- long result = ps.getDomainVerificationStatusForUser(userId);
- // if none available, get the status
- if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
- if (ps.getIntentFilterVerificationInfo() != null) {
- result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32;
- }
- }
- return result;
+ return computer(true).getDomainVerificationStatusLPr(ps, userId);
}
private ResolveInfo querySkipCurrentProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
int flags, int sourceUserId) {
- if (matchingFilters != null) {
- int size = matchingFilters.size();
- for (int i = 0; i < size; i ++) {
- CrossProfileIntentFilter filter = matchingFilters.get(i);
- if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
- // Checking if there are activities in the target user that can handle the
- // intent.
- ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
- resolvedType, flags, sourceUserId);
- if (resolveInfo != null) {
- return resolveInfo;
- }
- }
- }
- }
- return null;
+ return computer(true).querySkipCurrentProfileIntents(
+ matchingFilters, intent, resolvedType,
+ flags, sourceUserId);
}
// Return matching ResolveInfo in target user if any.
private ResolveInfo queryCrossProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
int flags, int sourceUserId, boolean matchInCurrentProfile) {
- if (matchingFilters != null) {
- // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
- // match the same intent. For performance reasons, it is better not to
- // run queryIntent twice for the same userId
- SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray();
- int size = matchingFilters.size();
- for (int i = 0; i < size; i++) {
- CrossProfileIntentFilter filter = matchingFilters.get(i);
- int targetUserId = filter.getTargetUserId();
- boolean skipCurrentProfile =
- (filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0;
- boolean skipCurrentProfileIfNoMatchFound =
- (filter.getFlags() & PackageManager.ONLY_IF_NO_MATCH_FOUND) != 0;
- if (!skipCurrentProfile && !alreadyTriedUserIds.get(targetUserId)
- && (!skipCurrentProfileIfNoMatchFound || !matchInCurrentProfile)) {
- // Checking if there are activities in the target user that can handle the
- // intent.
- ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
- resolvedType, flags, sourceUserId);
- if (resolveInfo != null) return resolveInfo;
- alreadyTriedUserIds.put(targetUserId, true);
- }
- }
- }
- return null;
+ return computer(true).queryCrossProfileIntents(
+ matchingFilters, intent, resolvedType,
+ flags, sourceUserId, matchInCurrentProfile);
}
/**
@@ -8257,54 +9985,14 @@
*/
private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, Intent intent,
String resolvedType, int flags, int sourceUserId) {
- int targetUserId = filter.getTargetUserId();
- List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
- resolvedType, flags, targetUserId);
- if (resultTargetUser != null && isUserEnabled(targetUserId)) {
- // If all the matches in the target profile are suspended, return null.
- for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
- if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags
- & ApplicationInfo.FLAG_SUSPENDED) == 0) {
- return createForwardingResolveInfoUnchecked(filter, sourceUserId,
- targetUserId);
- }
- }
- }
- return null;
+ return computer(true).createForwardingResolveInfo(filter, intent,
+ resolvedType, flags, sourceUserId);
}
private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter,
int sourceUserId, int targetUserId) {
- ResolveInfo forwardingResolveInfo = new ResolveInfo();
- final long ident = Binder.clearCallingIdentity();
- boolean targetIsProfile;
- try {
- targetIsProfile = mUserManager.getUserInfo(targetUserId).isManagedProfile();
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- String className;
- if (targetIsProfile) {
- className = FORWARD_INTENT_TO_MANAGED_PROFILE;
- } else {
- className = FORWARD_INTENT_TO_PARENT;
- }
- ComponentName forwardingActivityComponentName = new ComponentName(
- mAndroidApplication.packageName, className);
- ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0,
- sourceUserId);
- if (!targetIsProfile) {
- forwardingActivityInfo.showUserIcon = targetUserId;
- forwardingResolveInfo.noResourceId = true;
- }
- forwardingResolveInfo.activityInfo = forwardingActivityInfo;
- forwardingResolveInfo.priority = 0;
- forwardingResolveInfo.preferredOrder = 0;
- forwardingResolveInfo.match = 0;
- forwardingResolveInfo.isDefault = true;
- forwardingResolveInfo.filter = filter;
- forwardingResolveInfo.targetUserId = targetUserId;
- return forwardingResolveInfo;
+ return computer(true).createForwardingResolveInfoUnchecked(filter,
+ sourceUserId, targetUserId);
}
@Override
@@ -8623,144 +10311,23 @@
private @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
- if (!mUserManager.exists(userId)) return Collections.emptyList();
- enforceCrossUserOrProfilePermission(callingUid,
- userId,
- false /*requireFullPermission*/,
- false /*checkShell*/,
- "query intent receivers");
- final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
- false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
- ComponentName comp = intent.getComponent();
- if (comp == null) {
- if (intent.getSelector() != null) {
- intent = intent.getSelector();
- comp = intent.getComponent();
- }
- }
- if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<>(1);
- final ServiceInfo si = getServiceInfo(comp, flags, userId);
- if (si != null) {
- // When specifying an explicit component, we prevent the service from being
- // used when either 1) the service is in an instant application and the
- // caller is not the same instant application or 2) the calling package is
- // ephemeral and the activity is not visible to ephemeral applications.
- final boolean matchInstantApp =
- (flags & PackageManager.MATCH_INSTANT) != 0;
- final boolean matchVisibleToInstantAppOnly =
- (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
- final boolean isCallerInstantApp =
- instantAppPkgName != null;
- final boolean isTargetSameInstantApp =
- comp.getPackageName().equals(instantAppPkgName);
- final boolean isTargetInstantApp =
- (si.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
- final boolean isTargetHiddenFromInstantApp =
- (si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
- final boolean blockInstantResolution =
- !isTargetSameInstantApp
- && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
- || (matchVisibleToInstantAppOnly && isCallerInstantApp
- && isTargetHiddenFromInstantApp));
-
- final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
- && shouldFilterApplicationLocked(
- getPackageSettingInternal(si.applicationInfo.packageName,
- Process.SYSTEM_UID), callingUid, userId);
- if (!blockInstantResolution && !blockNormalResolution) {
- final ResolveInfo ri = new ResolveInfo();
- ri.serviceInfo = si;
- list.add(ri);
- }
- }
- return list;
- }
-
- return queryIntentServicesInternalBody(intent, resolvedType, flags,
- userId, callingUid, instantAppPkgName);
+ return computer(true).queryIntentServicesInternal(intent,
+ resolvedType, flags, userId, callingUid,
+ includeInstantApps);
}
private @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
String instantAppPkgName) {
- // reader
- synchronized (mLock) {
- String pkgName = intent.getPackage();
- if (pkgName == null) {
- final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
- resolvedType, flags, userId);
- if (resolveInfos == null) {
- return Collections.emptyList();
- }
- return applyPostServiceResolutionFilter(
- resolveInfos, instantAppPkgName, userId, callingUid);
- }
- final AndroidPackage pkg = mPackages.get(pkgName);
- if (pkg != null) {
- final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
- resolvedType, flags, pkg.getServices(),
- userId);
- if (resolveInfos == null) {
- return Collections.emptyList();
- }
- return applyPostServiceResolutionFilter(
- resolveInfos, instantAppPkgName, userId, callingUid);
- }
- return Collections.emptyList();
- }
+ return computer(true).queryIntentServicesInternalBody(intent,
+ resolvedType, flags, userId, callingUid,
+ instantAppPkgName);
}
private List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
String instantAppPkgName, @UserIdInt int userId, int filterCallingUid) {
- for (int i = resolveInfos.size() - 1; i >= 0; i--) {
- final ResolveInfo info = resolveInfos.get(i);
- if (instantAppPkgName == null) {
- SettingBase callingSetting =
- mSettings.getSettingLPr(UserHandle.getAppId(filterCallingUid));
- PackageSetting resolvedSetting =
- getPackageSettingInternal(info.serviceInfo.packageName, 0);
- if (!mAppsFilter.shouldFilterApplication(
- filterCallingUid, callingSetting, resolvedSetting, userId)) {
- continue;
- }
- }
- final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp();
- // allow services that are defined in the provided package
- if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) {
- if (info.serviceInfo.splitName != null
- && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames,
- info.serviceInfo.splitName)) {
- // requested service is defined in a split that hasn't been installed yet.
- // add the installer to the resolve list
- if (DEBUG_INSTANT) {
- Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
- }
- final ResolveInfo installerInfo = new ResolveInfo(
- mInstantAppInstallerInfo);
- installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
- null /* installFailureActivity */,
- info.serviceInfo.packageName,
- info.serviceInfo.applicationInfo.longVersionCode,
- info.serviceInfo.splitName);
- // add a non-generic filter
- installerInfo.filter = new IntentFilter();
- // load resources from the correct package
- installerInfo.resolvePackageName = info.getComponentInfo().packageName;
- resolveInfos.set(i, installerInfo);
- }
- continue;
- }
- // allow services that have been explicitly exposed to ephemeral apps
- if (!isEphemeralApp
- && ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
- continue;
- }
- resolveInfos.remove(i);
- }
- return resolveInfos;
+ return computer(true).applyPostServiceResolutionFilter(resolveInfos,
+ instantAppPkgName, userId, filterCallingUid);
}
@Override
@@ -8905,88 +10472,14 @@
@Override
public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
- final int callingUid = Binder.getCallingUid();
- if (getInstantAppPackageName(callingUid) != null) {
- return ParceledListSlice.emptyList();
- }
- if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
- flags = updateFlagsForPackage(flags, userId);
-
- enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
- false /* checkShell */, "get installed packages");
-
- return getInstalledPackagesBody(flags, userId, callingUid);
+ // SNAPSHOT
+ return computer(false).getInstalledPackages(flags, userId);
}
private ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
int callingUid) {
- // writer
- synchronized (mLock) {
- final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
- final boolean listApex = (flags & MATCH_APEX) != 0;
- final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0;
-
- ArrayList<PackageInfo> list;
- if (listUninstalled) {
- list = new ArrayList<>(mSettings.getPackagesLocked().size());
- for (PackageSetting ps : mSettings.getPackagesLocked().values()) {
- if (listFactory) {
- if (!ps.isSystem()) {
- continue;
- }
- PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
- if (psDisabled != null) {
- ps = psDisabled;
- }
- }
- if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
- continue;
- }
- if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
- continue;
- }
- final PackageInfo pi = generatePackageInfo(ps, flags, userId);
- if (pi != null) {
- list.add(pi);
- }
- }
- } else {
- list = new ArrayList<>(mPackages.size());
- for (AndroidPackage p : mPackages.values()) {
- PackageSetting ps = getPackageSetting(p.getPackageName());
- if (listFactory) {
- if (!p.isSystem()) {
- continue;
- }
- PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
- if (psDisabled != null) {
- ps = psDisabled;
- }
- }
- if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
- continue;
- }
- if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
- continue;
- }
- final PackageInfo pi = generatePackageInfo(ps, flags, userId);
- if (pi != null) {
- list.add(pi);
- }
- }
- }
- if (listApex) {
- if (listFactory) {
- list.addAll(mApexManager.getFactoryPackages());
- } else {
- list.addAll(mApexManager.getActivePackages());
- }
- if (listUninstalled) {
- list.addAll(mApexManager.getInactivePackages());
- }
- }
- return new ParceledListSlice<>(list);
- }
+ return computer(true).getInstalledPackagesBody(flags, userId,
+ callingUid);
}
private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
@@ -9163,39 +10656,20 @@
@Override
public boolean isInstantApp(String packageName, int userId) {
- final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
- false /* checkShell */, "isInstantApp");
-
- return isInstantAppInternal(packageName, userId, callingUid);
+ // SNAPSHOT
+ return computer(false).isInstantApp(packageName, userId);
}
private boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
int callingUid) {
- if (HIDE_EPHEMERAL_APIS) {
- return false;
- }
- return isInstantAppInternalBody(packageName, userId, callingUid);
+ return computer(true).isInstantAppInternal(packageName, userId,
+ callingUid);
}
private boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
int callingUid) {
- synchronized (mLock) {
- if (Process.isIsolated(callingUid)) {
- callingUid = mIsolatedOwners.get(callingUid);
- }
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- final boolean returnAllowed =
- ps != null
- && (isCallerSameApp(packageName, callingUid)
- || canViewInstantApps(callingUid, userId)
- || mInstantAppRegistry.isInstantAccessGranted(
- userId, UserHandle.getAppId(callingUid), ps.appId));
- if (returnAllowed) {
- return ps.getInstantApp(userId);
- }
- }
- return false;
+ return computer(true).isInstantAppInternalBody(packageName, userId,
+ callingUid);
}
@Override
@@ -9252,9 +10726,7 @@
}
private boolean isCallerSameApp(String packageName, int uid) {
- AndroidPackage pkg = mPackages.get(packageName);
- return pkg != null
- && UserHandle.getAppId(uid) == pkg.getUid();
+ return computer(true).isCallerSameApp(packageName, uid);
}
@Override
@@ -10012,8 +11484,8 @@
*/
void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message) {
- enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false,
- message);
+ computer(true).enforceCrossUserPermission(callingUid, userId,
+ requireFullPermission, checkShell, message);
}
/**
@@ -10029,23 +11501,9 @@
private void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell,
boolean requirePermissionWhenSameUser, String message) {
- if (userId < 0) {
- throw new IllegalArgumentException("Invalid userId " + userId);
- }
- if (checkShell) {
- PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
- UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
- }
- final int callingUserId = UserHandle.getUserId(callingUid);
- if (hasCrossUserPermission(
- callingUid, callingUserId, userId, requireFullPermission,
- requirePermissionWhenSameUser)) {
- return;
- }
- String errorMessage = buildInvalidCrossUserPermissionMessage(
- callingUid, userId, message, requireFullPermission);
- Slog.w(TAG, errorMessage);
- throw new SecurityException(errorMessage);
+ computer(true).enforceCrossUserPermission(callingUid, userId,
+ requireFullPermission, checkShell,
+ requirePermissionWhenSameUser, message);
}
/**
@@ -10064,62 +11522,24 @@
*/
private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message) {
- if (userId < 0) {
- throw new IllegalArgumentException("Invalid userId " + userId);
- }
- if (checkShell) {
- PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
- UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
- }
- final int callingUserId = UserHandle.getUserId(callingUid);
- if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
- /*requirePermissionWhenSameUser= */ false)) {
- return;
- }
- final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
- if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
- mContext,
- android.Manifest.permission.INTERACT_ACROSS_PROFILES,
- PermissionChecker.PID_UNKNOWN,
- callingUid,
- getPackage(callingUid).getPackageName())
- == PermissionChecker.PERMISSION_GRANTED) {
- return;
- }
- String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
- callingUid, userId, message, requireFullPermission, isSameProfileGroup);
- Slog.w(TAG, errorMessage);
- throw new SecurityException(errorMessage);
+ computer(true).enforceCrossUserOrProfilePermission(callingUid, userId,
+ requireFullPermission, checkShell, message);
}
private boolean hasCrossUserPermission(
int callingUid, int callingUserId, int userId, boolean requireFullPermission,
boolean requirePermissionWhenSameUser) {
- if (!requirePermissionWhenSameUser && userId == callingUserId) {
- return true;
- }
- if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) {
- return true;
- }
- if (requireFullPermission) {
- return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- }
- return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ return computer(true).hasCrossUserPermission(
+ callingUid, callingUserId, userId, requireFullPermission,
+ requirePermissionWhenSameUser);
}
private boolean hasPermission(String permission) {
- return mContext.checkCallingOrSelfPermission(permission)
- == PackageManager.PERMISSION_GRANTED;
+ return computer(true).hasPermission(permission);
}
private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ return computer(true).isSameProfileGroup(callerUserId, userId);
}
private static String buildInvalidCrossUserPermissionMessage(int callingUid,
@@ -10757,7 +12177,7 @@
@Nullable
private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
- return getSharedLibraryInfo(name, version, mSharedLibraries, null);
+ return computer(true).getSharedLibraryInfoLPr(name, version);
}
@Nullable
@@ -12971,6 +14391,7 @@
mResolveComponentName = new ComponentName(
mAndroidApplication.packageName, mResolveActivity.name);
}
+ onChanged();
}
}
@@ -13107,6 +14528,7 @@
mResolveInfo.preferredOrder = 0;
mResolveInfo.match = 0;
mResolveComponentName = mCustomResolverComponentName;
+ onChanged();
Slog.i(TAG, "Replacing default ResolverActivity with custom activity: " +
mResolveComponentName);
}
@@ -13118,6 +14540,7 @@
Slog.d(TAG, "Clear ephemeral installer activity");
}
mInstantAppInstallerActivity = null;
+ onChanged();
return;
}
@@ -13137,6 +14560,7 @@
mInstantAppInstallerInfo.isDefault = true;
mInstantAppInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ onChanged();
}
private void killApplication(String pkgName, @AppIdInt int appId, String reason) {
@@ -19184,84 +20608,18 @@
}
private String resolveExternalPackageNameLPr(AndroidPackage pkg) {
- if (pkg.getStaticSharedLibName() != null) {
- return pkg.getManifestPackageName();
- }
- return pkg.getPackageName();
+ return computer(true).resolveExternalPackageNameLPr(pkg);
}
@GuardedBy("mLock")
private String resolveInternalPackageNameLPr(String packageName, long versionCode) {
- final int callingUid = Binder.getCallingUid();
- return resolveInternalPackageNameInternalLocked(packageName, versionCode,
- callingUid);
+ return computer(true).resolveInternalPackageNameLPr(packageName, versionCode);
}
private String resolveInternalPackageNameInternalLocked(
String packageName, long versionCode, int callingUid) {
- // Handle renamed packages
- String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
- packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
-
- // Is this a static library?
- LongSparseArray<SharedLibraryInfo> versionedLib =
- mStaticLibsByDeclaringPackage.get(packageName);
- if (versionedLib == null || versionedLib.size() <= 0) {
- return packageName;
- }
-
- // Figure out which lib versions the caller can see
- LongSparseLongArray versionsCallerCanSee = null;
- final int callingAppId = UserHandle.getAppId(callingUid);
- if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
- && callingAppId != Process.ROOT_UID) {
- versionsCallerCanSee = new LongSparseLongArray();
- String libName = versionedLib.valueAt(0).getName();
- String[] uidPackages = getPackagesForUidInternal(callingUid, callingUid);
- if (uidPackages != null) {
- for (String uidPackage : uidPackages) {
- PackageSetting ps = mSettings.getPackageLPr(uidPackage);
- final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
- if (libIdx >= 0) {
- final long libVersion = ps.usesStaticLibrariesVersions[libIdx];
- versionsCallerCanSee.append(libVersion, libVersion);
- }
- }
- }
- }
-
- // Caller can see nothing - done
- if (versionsCallerCanSee != null && versionsCallerCanSee.size() <= 0) {
- return packageName;
- }
-
- // Find the version the caller can see and the app version code
- SharedLibraryInfo highestVersion = null;
- final int versionCount = versionedLib.size();
- for (int i = 0; i < versionCount; i++) {
- SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
- if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
- libraryInfo.getLongVersion()) < 0) {
- continue;
- }
- final long libVersionCode = libraryInfo.getDeclaringPackage().getLongVersionCode();
- if (versionCode != PackageManager.VERSION_CODE_HIGHEST) {
- if (libVersionCode == versionCode) {
- return libraryInfo.getPackageName();
- }
- } else if (highestVersion == null) {
- highestVersion = libraryInfo;
- } else if (libVersionCode > highestVersion
- .getDeclaringPackage().getLongVersionCode()) {
- highestVersion = libraryInfo;
- }
- }
-
- if (highestVersion != null) {
- return highestVersion.getPackageName();
- }
-
- return packageName;
+ return computer(true).resolveInternalPackageNameInternalLocked(
+ packageName, versionCode, callingUid);
}
boolean isCallerVerifier(int callingUid) {
@@ -21311,38 +22669,11 @@
* then reports the most likely home activity or null if there are more than one.
*/
private ComponentName getDefaultHomeActivity(int userId) {
- List<ResolveInfo> allHomeCandidates = new ArrayList<>();
- ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId);
- if (cn != null) {
- return cn;
- }
- // TODO: This should not happen since there should always be a default package set for
- // ROLE_HOME in RoleManager. Continue with a warning log for now.
- Slog.w(TAG, "Default package for ROLE_HOME is not set in RoleManager");
-
- // Find the launcher with the highest priority and return that component if there are no
- // other home activity with the same priority.
- int lastPriority = Integer.MIN_VALUE;
- ComponentName lastComponent = null;
- final int size = allHomeCandidates.size();
- for (int i = 0; i < size; i++) {
- final ResolveInfo ri = allHomeCandidates.get(i);
- if (ri.priority > lastPriority) {
- lastComponent = ri.activityInfo.getComponentName();
- lastPriority = ri.priority;
- } else if (ri.priority == lastPriority) {
- // Two components found with same priority.
- lastComponent = null;
- }
- }
- return lastComponent;
+ return computer(true).getDefaultHomeActivity(userId);
}
private Intent getHomeIntent() {
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_HOME);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- return intent;
+ return computer(true).getHomeIntent();
}
private IntentFilter getHomeFilter() {
@@ -21354,31 +22685,8 @@
ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
int userId) {
- Intent intent = getHomeIntent();
- List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
- PackageManager.GET_META_DATA, userId);
- allHomeCandidates.clear();
- if (resolveInfos == null) {
- return null;
- }
- allHomeCandidates.addAll(resolveInfos);
-
- final String packageName = mDefaultAppProvider.getDefaultHome(userId);
- if (packageName == null) {
- return null;
- }
-
- int resolveInfosSize = resolveInfos.size();
- for (int i = 0; i < resolveInfosSize; i++) {
- ResolveInfo resolveInfo = resolveInfos.get(i);
-
- if (resolveInfo.activityInfo != null && TextUtils.equals(
- resolveInfo.activityInfo.packageName, packageName)) {
- return new ComponentName(resolveInfo.activityInfo.packageName,
- resolveInfo.activityInfo.name);
- }
- }
- return null;
+ return computer(true).getHomeActivitiesAsUser(allHomeCandidates,
+ userId);
}
/** <b>must not hold {@link #mLock}</b> */
@@ -24959,23 +26267,13 @@
}
private AndroidPackage getPackage(String packageName) {
- synchronized (mLock) {
- packageName = resolveInternalPackageNameLPr(
- packageName, PackageManager.VERSION_CODE_HIGHEST);
- return mPackages.get(packageName);
- }
+ // SNAPSHOT
+ return computer(false).getPackage(packageName);
}
private AndroidPackage getPackage(int uid) {
- synchronized (mLock) {
- final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID);
- AndroidPackage pkg = null;
- final int numPackages = packageNames == null ? 0 : packageNames.length;
- for (int i = 0; pkg == null && i < numPackages; i++) {
- pkg = mPackages.get(packageNames[i]);
- }
- return pkg;
- }
+ // SNAPSHOT
+ return computer(false).getPackage(uid);
}
private class PackageManagerInternalImpl extends PackageManagerInternal {
@@ -26226,15 +27524,12 @@
@Nullable
public PackageSetting getPackageSetting(String packageName) {
- return getPackageSettingInternal(packageName, Binder.getCallingUid());
+ // SNAPSHOT
+ return computer(false).getPackageSetting(packageName);
}
private PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
- synchronized (mLock) {
- packageName = resolveInternalPackageNameInternalLocked(
- packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
- return mSettings.getPackageLPr(packageName);
- }
+ return computer(true).getPackageSettingInternal(packageName, callingUid);
}
void forEachPackage(Consumer<AndroidPackage> actionLocked) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 9347ce1..e20ed05 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3646,6 +3646,7 @@
//...then external ones
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
+ addedIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
// Also, add the UserHandle for mainline modules which can't use the @hide
// EXTRA_USER_HANDLE.
@@ -4048,6 +4049,7 @@
final long ident = Binder.clearCallingIdentity();
try {
Intent removedIntent = new Intent(Intent.ACTION_USER_REMOVED);
+ removedIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
// Also, add the UserHandle for mainline modules which can't use the @hide
// EXTRA_USER_HANDLE.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6e4806f..aa2e1ff 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3686,6 +3686,32 @@
break;
}
+ case KeyEvent.KEYCODE_TV_POWER: {
+ result &= ~ACTION_PASS_TO_USER;
+ isWakeKey = false; // wake-up will be handled separately
+ HdmiControlManager hdmiControlManager = getHdmiControlManager();
+ if (hdmiControlManager != null && hdmiControlManager.shouldHandleTvPowerKey()) {
+ if (down) {
+ hdmiControlManager.toggleAndFollowTvPower();
+ }
+ } else if (mHasFeatureLeanback) {
+ KeyEvent fallbackEvent = KeyEvent.obtain(
+ event.getDownTime(), event.getEventTime(),
+ event.getAction(), KeyEvent.KEYCODE_POWER,
+ event.getRepeatCount(), event.getMetaState(),
+ event.getDeviceId(), event.getScanCode(),
+ event.getFlags(), event.getSource(), event.getDisplayId(), null);
+ if (down) {
+ interceptPowerKeyDown(fallbackEvent, interactive);
+ } else {
+ interceptPowerKeyUp(fallbackEvent, interactive, canceled);
+ }
+ }
+ // Ignore this key for any device that is not connected to a TV via HDMI and
+ // not an Android TV device.
+ break;
+ }
+
case KeyEvent.KEYCODE_POWER: {
EventLogTags.writeInterceptPower(
KeyEvent.actionToString(event.getAction()),
diff --git a/services/core/java/com/android/server/powerstats/BatteryTrigger.java b/services/core/java/com/android/server/powerstats/BatteryTrigger.java
index 6500523..b35cb52 100644
--- a/services/core/java/com/android/server/powerstats/BatteryTrigger.java
+++ b/services/core/java/com/android/server/powerstats/BatteryTrigger.java
@@ -43,7 +43,7 @@
if (newBatteryLevel < mBatteryLevel) {
if (DEBUG) Slog.d(TAG, "Battery level dropped. Log rail data");
- logPowerStatsData();
+ logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP);
}
mBatteryLevel = newBatteryLevel;
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index c9595c2..6d9cb75 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -218,7 +218,7 @@
* array and written to on-device storage.
*/
public void write(byte[] data) {
- if (data.length > 0) {
+ if (data != null && data.length > 0) {
mLock.lock();
long currentTimeMillis = System.currentTimeMillis();
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java
index 1754185..e3672a8 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java
@@ -31,10 +31,8 @@
protected Context mContext;
private PowerStatsLogger mPowerStatsLogger;
- protected void logPowerStatsData() {
- Message.obtain(
- mPowerStatsLogger,
- PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE).sendToTarget();
+ protected void logPowerStatsData(int msgType) {
+ Message.obtain(mPowerStatsLogger, msgType).sendToTarget();
}
public PowerStatsLogTrigger(Context context, PowerStatsLogger powerStatsLogger) {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 409cd82..9ee3429 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -20,6 +20,8 @@
import android.hardware.power.stats.ChannelInfo;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyMeasurement;
+import android.hardware.power.stats.PowerEntityInfo;
+import android.hardware.power.stats.StateResidencyResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -32,6 +34,8 @@
import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils;
import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
import com.android.server.powerstats.ProtoStreamUtils.EnergyMeasurementUtils;
+import com.android.server.powerstats.ProtoStreamUtils.PowerEntityInfoUtils;
+import com.android.server.powerstats.ProtoStreamUtils.StateResidencyResultUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -42,23 +46,25 @@
* PowerStatsLogger is responsible for logging model and meter energy data to on-device storage.
* Messages are sent to its message handler to request that energy data be logged, at which time it
* queries the PowerStats HAL and logs the data to on-device storage. The on-device storage is
- * dumped to file by calling writeModelDataToFile or writeMeterDataToFile with a file descriptor
- * that points to the output file.
+ * dumped to file by calling writeModelDataToFile, writeMeterDataToFile, or writeResidencyDataToFile
+ * with a file descriptor that points to the output file.
*/
public final class PowerStatsLogger extends Handler {
private static final String TAG = PowerStatsLogger.class.getSimpleName();
private static final boolean DEBUG = false;
- protected static final int MSG_LOG_TO_DATA_STORAGE = 0;
+ protected static final int MSG_LOG_TO_DATA_STORAGE_TIMER = 0;
+ protected static final int MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP = 1;
private final PowerStatsDataStorage mPowerStatsMeterStorage;
private final PowerStatsDataStorage mPowerStatsModelStorage;
+ private final PowerStatsDataStorage mPowerStatsResidencyStorage;
private final IPowerStatsHALWrapper mPowerStatsHALWrapper;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_LOG_TO_DATA_STORAGE:
- if (DEBUG) Slog.d(TAG, "Logging to data storage");
+ case MSG_LOG_TO_DATA_STORAGE_TIMER:
+ if (DEBUG) Slog.d(TAG, "Logging to data storage on timer");
// Log power meter data.
EnergyMeasurement[] energyMeasurements =
@@ -74,6 +80,17 @@
EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults));
if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults);
break;
+
+ case MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP:
+ if (DEBUG) Slog.d(TAG, "Logging to data storage on battery drop");
+
+ // Log state residency data.
+ StateResidencyResult[] stateResidencyResults =
+ mPowerStatsHALWrapper.getStateResidency(new int[0]);
+ mPowerStatsResidencyStorage.write(
+ StateResidencyResultUtils.getProtoBytes(stateResidencyResults));
+ if (DEBUG) StateResidencyResultUtils.print(stateResidencyResults);
+ break;
}
}
@@ -159,13 +176,57 @@
pos.flush();
}
+ /**
+ * Writes residency data stored in PowerStatsDataStorage to a file descriptor.
+ *
+ * @param fd FileDescriptor where residency data stored in PowerStatsDataStorage is written.
+ * Data is written in protobuf format as defined by powerstatsservice.proto.
+ */
+ public void writeResidencyDataToFile(FileDescriptor fd) {
+ if (DEBUG) Slog.d(TAG, "Writing residency data to file");
+
+ final ProtoOutputStream pos = new ProtoOutputStream(fd);
+
+ try {
+ PowerEntityInfo[] powerEntityInfo = mPowerStatsHALWrapper.getPowerEntityInfo();
+ PowerEntityInfoUtils.packProtoMessage(powerEntityInfo, pos);
+ if (DEBUG) PowerEntityInfoUtils.print(powerEntityInfo);
+
+ mPowerStatsResidencyStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
+ @Override
+ public void onReadDataElement(byte[] data) {
+ try {
+ final ProtoInputStream pis =
+ new ProtoInputStream(new ByteArrayInputStream(data));
+ // TODO(b/166535853): ProtoOutputStream doesn't provide a method to write
+ // a byte array that already contains a serialized proto, so I have to
+ // deserialize, then re-serialize. This is computationally inefficient.
+ StateResidencyResult[] stateResidencyResult =
+ StateResidencyResultUtils.unpackProtoMessage(data);
+ StateResidencyResultUtils.packProtoMessage(stateResidencyResult, pos);
+ if (DEBUG) StateResidencyResultUtils.print(stateResidencyResult);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write residency data to incident report.");
+ }
+ }
+ });
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write residency data to incident report.");
+ }
+
+ pos.flush();
+ }
+
public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename,
- String modelFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
+ String modelFilename, String residencyFilename,
+ IPowerStatsHALWrapper powerStatsHALWrapper) {
super(Looper.getMainLooper());
mPowerStatsHALWrapper = powerStatsHALWrapper;
mPowerStatsMeterStorage = new PowerStatsDataStorage(context, dataStoragePath,
meterFilename);
mPowerStatsModelStorage = new PowerStatsDataStorage(context, dataStoragePath,
modelFilename);
+ mPowerStatsResidencyStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ residencyFilename);
}
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index ce50e583..7778572 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -49,6 +49,8 @@
private static final int DATA_STORAGE_VERSION = 0;
private static final String METER_FILENAME = "log.powerstats.meter." + DATA_STORAGE_VERSION;
private static final String MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION;
+ private static final String RESIDENCY_FILENAME =
+ "log.powerstats.residency." + DATA_STORAGE_VERSION;
private final Injector mInjector;
@@ -76,15 +78,19 @@
return MODEL_FILENAME;
}
+ String createResidencyFilename() {
+ return RESIDENCY_FILENAME;
+ }
+
IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
return PowerStatsHALWrapper.getPowerStatsHalImpl();
}
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String meterFilename, String modelFilename,
+ String meterFilename, String modelFilename, String residencyFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
return new PowerStatsLogger(context, dataStoragePath, meterFilename,
- modelFilename, powerStatsHALWrapper);
+ modelFilename, residencyFilename, powerStatsHALWrapper);
}
BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
@@ -109,6 +115,8 @@
mPowerStatsLogger.writeModelDataToFile(fd);
} else if ("meter".equals(args[1])) {
mPowerStatsLogger.writeMeterDataToFile(fd);
+ } else if ("residency".equals(args[1])) {
+ mPowerStatsLogger.writeResidencyDataToFile(fd);
}
} else if (args.length == 0) {
pw.println("PowerStatsService dumpsys: available PowerEntityInfos");
@@ -148,7 +156,8 @@
// Only start logger and triggers if initialization is successful.
mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
mInjector.createDataStoragePath(), mInjector.createMeterFilename(),
- mInjector.createModelFilename(), mPowerStatsHALWrapper);
+ mInjector.createModelFilename(), mInjector.createResidencyFilename(),
+ mPowerStatsHALWrapper);
mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
} else {
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
index 5e23b86..ab9b3e0 100644
--- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -20,6 +20,8 @@
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyMeasurement;
import android.hardware.power.stats.PowerEntityInfo;
+import android.hardware.power.stats.StateInfo;
+import android.hardware.power.stats.StateResidency;
import android.hardware.power.stats.StateResidencyResult;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
@@ -45,6 +47,29 @@
private static final String TAG = ProtoStreamUtils.class.getSimpleName();
static class PowerEntityInfoUtils {
+ public static void packProtoMessage(PowerEntityInfo[] powerEntityInfo,
+ ProtoOutputStream pos) {
+ if (powerEntityInfo == null) return;
+
+ for (int i = 0; i < powerEntityInfo.length; i++) {
+ long peiToken = pos.start(PowerStatsServiceResidencyProto.POWER_ENTITY_INFO);
+ pos.write(PowerEntityInfoProto.POWER_ENTITY_ID, powerEntityInfo[i].powerEntityId);
+ pos.write(PowerEntityInfoProto.POWER_ENTITY_NAME,
+ powerEntityInfo[i].powerEntityName);
+ if (powerEntityInfo[i].states != null) {
+ final int statesLength = powerEntityInfo[i].states.length;
+ for (int j = 0; j < statesLength; j++) {
+ final StateInfo state = powerEntityInfo[i].states[j];
+ long siToken = pos.start(PowerEntityInfoProto.STATES);
+ pos.write(StateInfoProto.STATE_ID, state.stateId);
+ pos.write(StateInfoProto.STATE_NAME, state.stateName);
+ pos.end(siToken);
+ }
+ }
+ pos.end(peiToken);
+ }
+ }
+
public static void print(PowerEntityInfo[] powerEntityInfo) {
if (powerEntityInfo == null) return;
@@ -77,6 +102,144 @@
}
static class StateResidencyResultUtils {
+ public static byte[] getProtoBytes(StateResidencyResult[] stateResidencyResult) {
+ ProtoOutputStream pos = new ProtoOutputStream();
+ packProtoMessage(stateResidencyResult, pos);
+ return pos.getBytes();
+ }
+
+ public static void packProtoMessage(StateResidencyResult[] stateResidencyResult,
+ ProtoOutputStream pos) {
+ if (stateResidencyResult == null) return;
+
+ for (int i = 0; i < stateResidencyResult.length; i++) {
+ final int stateLength = stateResidencyResult[i].stateResidencyData.length;
+ long srrToken = pos.start(PowerStatsServiceResidencyProto.STATE_RESIDENCY_RESULT);
+ pos.write(StateResidencyResultProto.POWER_ENTITY_ID,
+ stateResidencyResult[i].powerEntityId);
+ for (int j = 0; j < stateLength; j++) {
+ final StateResidency stateResidencyData =
+ stateResidencyResult[i].stateResidencyData[j];
+ long srdToken = pos.start(StateResidencyResultProto.STATE_RESIDENCY_DATA);
+ pos.write(StateResidencyProto.STATE_ID, stateResidencyData.stateId);
+ pos.write(StateResidencyProto.TOTAL_TIME_IN_STATE_MS,
+ stateResidencyData.totalTimeInStateMs);
+ pos.write(StateResidencyProto.TOTAL_STATE_ENTRY_COUNT,
+ stateResidencyData.totalStateEntryCount);
+ pos.write(StateResidencyProto.LAST_ENTRY_TIMESTAMP_MS,
+ stateResidencyData.lastEntryTimestampMs);
+ pos.end(srdToken);
+ }
+ pos.end(srrToken);
+ }
+ }
+
+ public static StateResidencyResult[] unpackProtoMessage(byte[] data) throws IOException {
+ final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
+ List<StateResidencyResult> stateResidencyResultList =
+ new ArrayList<StateResidencyResult>();
+ while (true) {
+ try {
+ int nextField = pis.nextField();
+ StateResidencyResult stateResidencyResult = new StateResidencyResult();
+
+ if (nextField == (int) PowerStatsServiceResidencyProto.STATE_RESIDENCY_RESULT) {
+ long token =
+ pis.start(PowerStatsServiceResidencyProto.STATE_RESIDENCY_RESULT);
+ stateResidencyResultList.add(unpackStateResidencyResultProto(pis));
+ pis.end(token);
+ } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
+ return stateResidencyResultList.toArray(
+ new StateResidencyResult[stateResidencyResultList.size()]);
+ } else {
+ Slog.e(TAG, "Unhandled field in PowerStatsServiceResidencyProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Slog.e(TAG, "Wire Type mismatch in PowerStatsServiceResidencyProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ private static StateResidencyResult unpackStateResidencyResultProto(ProtoInputStream pis)
+ throws IOException {
+ StateResidencyResult stateResidencyResult = new StateResidencyResult();
+ List<StateResidency> stateResidencyList = new ArrayList<StateResidency>();
+
+ while (true) {
+ try {
+ switch (pis.nextField()) {
+ case (int) StateResidencyResultProto.POWER_ENTITY_ID:
+ stateResidencyResult.powerEntityId =
+ pis.readInt(StateResidencyResultProto.POWER_ENTITY_ID);
+ break;
+
+ case (int) StateResidencyResultProto.STATE_RESIDENCY_DATA:
+ long token = pis.start(StateResidencyResultProto.STATE_RESIDENCY_DATA);
+ stateResidencyList.add(unpackStateResidencyProto(pis));
+ pis.end(token);
+ break;
+
+ case ProtoInputStream.NO_MORE_FIELDS:
+ stateResidencyResult.stateResidencyData = stateResidencyList.toArray(
+ new StateResidency[stateResidencyList.size()]);
+ return stateResidencyResult;
+
+ default:
+ Slog.e(TAG, "Unhandled field in StateResidencyResultProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ break;
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Slog.e(TAG, "Wire Type mismatch in StateResidencyResultProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ private static StateResidency unpackStateResidencyProto(ProtoInputStream pis)
+ throws IOException {
+ StateResidency stateResidency = new StateResidency();
+
+ while (true) {
+ try {
+ switch (pis.nextField()) {
+ case (int) StateResidencyProto.STATE_ID:
+ stateResidency.stateId = pis.readInt(StateResidencyProto.STATE_ID);
+ break;
+
+ case (int) StateResidencyProto.TOTAL_TIME_IN_STATE_MS:
+ stateResidency.totalTimeInStateMs =
+ pis.readLong(StateResidencyProto.TOTAL_TIME_IN_STATE_MS);
+ break;
+
+ case (int) StateResidencyProto.TOTAL_STATE_ENTRY_COUNT:
+ stateResidency.totalStateEntryCount =
+ pis.readLong(StateResidencyProto.TOTAL_STATE_ENTRY_COUNT);
+ break;
+
+ case (int) StateResidencyProto.LAST_ENTRY_TIMESTAMP_MS:
+ stateResidency.lastEntryTimestampMs =
+ pis.readLong(StateResidencyProto.LAST_ENTRY_TIMESTAMP_MS);
+ break;
+
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return stateResidency;
+
+ default:
+ Slog.e(TAG, "Unhandled field in StateResidencyProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ break;
+
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Slog.e(TAG, "Wire Type mismatch in StateResidencyProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
public static void print(StateResidencyResult[] stateResidencyResult) {
if (stateResidencyResult == null) return;
@@ -98,17 +261,14 @@
static class ChannelInfoUtils {
public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) {
- long token;
-
if (channelInfo == null) return;
for (int i = 0; i < channelInfo.length; i++) {
- token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO);
+ long token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO);
pos.write(ChannelInfoProto.CHANNEL_ID, channelInfo[i].channelId);
pos.write(ChannelInfoProto.CHANNEL_NAME, channelInfo[i].channelName);
pos.end(token);
}
-
}
public static void print(ChannelInfo[] channelInfo) {
@@ -139,12 +299,10 @@
public static void packProtoMessage(EnergyMeasurement[] energyMeasurement,
ProtoOutputStream pos) {
- long token;
-
if (energyMeasurement == null) return;
for (int i = 0; i < energyMeasurement.length; i++) {
- token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
+ long token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
pos.write(EnergyMeasurementProto.CHANNEL_ID, energyMeasurement[i].channelId);
pos.write(EnergyMeasurementProto.TIMESTAMP_MS, energyMeasurement[i].timestampMs);
pos.write(EnergyMeasurementProto.ENERGY_UWS, energyMeasurement[i].energyUWs);
@@ -155,7 +313,6 @@
public static EnergyMeasurement[] unpackProtoMessage(byte[] data) throws IOException {
final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
List<EnergyMeasurement> energyMeasurementList = new ArrayList<EnergyMeasurement>();
- long token;
while (true) {
try {
@@ -163,8 +320,8 @@
EnergyMeasurement energyMeasurement = new EnergyMeasurement();
if (nextField == (int) PowerStatsServiceMeterProto.ENERGY_MEASUREMENT) {
- token = pis.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
- energyMeasurementList.add(unpackProtoMessage(pis));
+ long token = pis.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
+ energyMeasurementList.add(unpackEnergyMeasurementProto(pis));
pis.end(token);
} else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
return energyMeasurementList.toArray(
@@ -180,7 +337,7 @@
}
}
- private static EnergyMeasurement unpackProtoMessage(ProtoInputStream pis)
+ private static EnergyMeasurement unpackEnergyMeasurementProto(ProtoInputStream pis)
throws IOException {
EnergyMeasurement energyMeasurement = new EnergyMeasurement();
@@ -230,12 +387,10 @@
static class EnergyConsumerIdUtils {
public static void packProtoMessage(int[] energyConsumerId, ProtoOutputStream pos) {
- long token;
-
if (energyConsumerId == null) return;
for (int i = 0; i < energyConsumerId.length; i++) {
- token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID);
+ long token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID);
pos.write(EnergyConsumerIdProto.ENERGY_CONSUMER_ID, energyConsumerId[i]);
pos.end(token);
}
@@ -267,12 +422,10 @@
public static void packProtoMessage(EnergyConsumerResult[] energyConsumerResult,
ProtoOutputStream pos) {
- long token;
-
if (energyConsumerResult == null) return;
for (int i = 0; i < energyConsumerResult.length; i++) {
- token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+ long token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
pos.write(EnergyConsumerResultProto.ENERGY_CONSUMER_ID,
energyConsumerResult[i].energyConsumerId);
pos.write(EnergyConsumerResultProto.TIMESTAMP_MS,
@@ -286,16 +439,14 @@
final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
List<EnergyConsumerResult> energyConsumerResultList =
new ArrayList<EnergyConsumerResult>();
- long token;
-
while (true) {
try {
int nextField = pis.nextField();
EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
if (nextField == (int) PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT) {
- token = pis.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
- energyConsumerResultList.add(unpackProtoMessage(pis));
+ long token = pis.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+ energyConsumerResultList.add(unpackEnergyConsumerResultProto(pis));
pis.end(token);
} else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
return energyConsumerResultList.toArray(
@@ -311,7 +462,7 @@
}
}
- private static EnergyConsumerResult unpackProtoMessage(ProtoInputStream pis)
+ private static EnergyConsumerResult unpackEnergyConsumerResultProto(ProtoInputStream pis)
throws IOException {
EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java
index 4b59295..7cba00f 100644
--- a/services/core/java/com/android/server/powerstats/TimerTrigger.java
+++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java
@@ -29,7 +29,7 @@
private static final String TAG = TimerTrigger.class.getSimpleName();
private static final boolean DEBUG = false;
// TODO(b/166689029): Make configurable through global settings.
- private static final long LOG_PERIOD_MS = 60 * 1000;
+ private static final long LOG_PERIOD_MS = 120 * 1000;
private final Handler mHandler;
@@ -40,7 +40,7 @@
// LOG_PERIOD_MS.
mHandler.postDelayed(mLogData, LOG_PERIOD_MS);
if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data");
- logPowerStatsData();
+ logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER);
}
};
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index db79ce4..9088d7b 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -614,6 +614,7 @@
return pullRoleHolderLocked(atomTag, data);
}
case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE:
+ // fall-through - same call covers two cases
case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED:
synchronized (mDangerousPermissionStateLock) {
return pullDangerousPermissionStateLocked(atomTag, data);
@@ -2077,7 +2078,8 @@
metrics.pageTablesKb,
metrics.kernelStackKb,
metrics.totalIonKb,
- metrics.unaccountedKb));
+ metrics.unaccountedKb,
+ metrics.gpuTotalUsageKb));
return StatsManager.PULL_SUCCESS;
}
@@ -3014,7 +3016,7 @@
}
int numPerms = pkg.requestedPermissions.length;
- for (int permNum = 0; permNum < numPerms; permNum++) {
+ for (int permNum = 0; permNum < numPerms; permNum++) {
String permName = pkg.requestedPermissions[permNum];
PermissionInfo permissionInfo;
@@ -3027,10 +3029,6 @@
continue;
}
- if (permissionInfo.getProtection() != PROTECTION_DANGEROUS) {
- continue;
- }
-
if (permName.startsWith(COMMON_PERMISSION_PREFIX)) {
permName = permName.substring(COMMON_PERMISSION_PREFIX.length());
}
@@ -3042,15 +3040,17 @@
(pkg.requestedPermissionsFlags[permNum]
& REQUESTED_PERMISSION_GRANTED)
!= 0,
- permissionFlags);
+ permissionFlags, permissionInfo.getProtection()
+ | permissionInfo.getProtectionFlags());
} else {
- // DangerousPermissionStateSampled atom.
+ // DangeorusPermissionStateSampled atom.
e = FrameworkStatsLog.buildStatsEvent(atomTag, permName,
pkg.applicationInfo.uid,
(pkg.requestedPermissionsFlags[permNum]
& REQUESTED_PERMISSION_GRANTED)
!= 0,
- permissionFlags);
+ permissionFlags, permissionInfo.getProtection()
+ | permissionInfo.getProtectionFlags());
}
pulledData.add(e);
}
diff --git a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
index 99fc7c1..1e80c4f 100644
--- a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
@@ -27,6 +27,7 @@
static Metrics getMetrics() {
int totalIonKb = (int) Debug.getIonHeapsSizeKb();
+ int gpuTotalUsageKb = (int) Debug.getGpuTotalUsageKb();
long[] mInfos = new long[Debug.MEMINFO_COUNT];
Debug.getMemInfo(mInfos);
@@ -62,6 +63,7 @@
result.pageTablesKb = (int) mInfos[Debug.MEMINFO_PAGE_TABLES];
result.kernelStackKb = (int) mInfos[Debug.MEMINFO_KERNEL_STACK];
result.totalIonKb = totalIonKb;
+ result.gpuTotalUsageKb = gpuTotalUsageKb;
result.unaccountedKb = (int) (mInfos[Debug.MEMINFO_TOTAL] - accountedKb);
return result;
}
@@ -72,6 +74,7 @@
public int pageTablesKb;
public int kernelStackKb;
public int totalIonKb;
+ public int gpuTotalUsageKb;
public int unaccountedKb;
}
}
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index c060807..d8a145d9 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -36,6 +36,8 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -79,6 +81,7 @@
@NonNull private final TelephonySubscriptionTrackerCallback mCallback;
@NonNull private final Dependencies mDeps;
+ @NonNull private final TelephonyManager mTelephonyManager;
@NonNull private final SubscriptionManager mSubscriptionManager;
@NonNull private final CarrierConfigManager mCarrierConfigManager;
@@ -106,6 +109,7 @@
mCallback = Objects.requireNonNull(callback, "Missing callback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
@@ -139,7 +143,7 @@
* so callbacks & broadcasts are all serialized on mHandler, avoiding the need for locking.
*/
public void handleSubscriptionsChanged() {
- final Set<ParcelUuid> activeSubGroups = new ArraySet<>();
+ final Map<ParcelUuid, Set<String>> privilegedPackages = new HashMap<>();
final Map<Integer, ParcelUuid> newSubIdToGroupMap = new HashMap<>();
final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList();
@@ -166,12 +170,22 @@
// group.
if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX
&& mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) {
- activeSubGroups.add(subInfo.getGroupUuid());
+ // TODO (b/172619301): Cache based on callbacks from CarrierPrivilegesTracker
+
+ final TelephonyManager subIdSpecificTelephonyManager =
+ mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
+
+ final ParcelUuid subGroup = subInfo.getGroupUuid();
+ final Set<String> pkgs =
+ privilegedPackages.getOrDefault(subGroup, new ArraySet<>());
+ pkgs.addAll(subIdSpecificTelephonyManager.getPackagesWithCarrierPrivileges());
+
+ privilegedPackages.put(subGroup, pkgs);
}
}
final TelephonySubscriptionSnapshot newSnapshot =
- new TelephonySubscriptionSnapshot(newSubIdToGroupMap, activeSubGroups);
+ new TelephonySubscriptionSnapshot(newSubIdToGroupMap, privilegedPackages);
// If snapshot was meaningfully updated, fire the callback
if (!newSnapshot.equals(mCurrentSnapshot)) {
@@ -231,22 +245,40 @@
/** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */
public static class TelephonySubscriptionSnapshot {
private final Map<Integer, ParcelUuid> mSubIdToGroupMap;
- private final Set<ParcelUuid> mActiveGroups;
+ private final Map<ParcelUuid, Set<String>> mPrivilegedPackages;
+
+ public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT =
+ new TelephonySubscriptionSnapshot(Collections.emptyMap(), Collections.emptyMap());
@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"));
+ @NonNull Map<ParcelUuid, Set<String>> privilegedPackages) {
+ Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null");
+ Objects.requireNonNull(privilegedPackages, "privilegedPackages was null");
+
+ mSubIdToGroupMap = Collections.unmodifiableMap(subIdToGroupMap);
+
+ final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>();
+ for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) {
+ unmodifiableInnerSets.put(
+ entry.getKey(), Collections.unmodifiableSet(entry.getValue()));
+ }
+ mPrivilegedPackages = Collections.unmodifiableMap(unmodifiableInnerSets);
}
/** Returns the active subscription groups */
@NonNull
public Set<ParcelUuid> getActiveSubscriptionGroups() {
- return mActiveGroups;
+ return mPrivilegedPackages.keySet();
+ }
+
+ /** Checks if the provided package is carrier privileged for the specified sub group. */
+ public boolean packageHasPermissionsForSubscriptionGroup(
+ @NonNull ParcelUuid subGrp, @NonNull String packageName) {
+ final Set<String> privilegedPackages = mPrivilegedPackages.get(subGrp);
+
+ return privilegedPackages != null && privilegedPackages.contains(packageName);
}
/** Returns the Subscription Group for a given subId. */
@@ -273,7 +305,7 @@
@Override
public int hashCode() {
- return Objects.hash(mSubIdToGroupMap, mActiveGroups);
+ return Objects.hash(mSubIdToGroupMap, mPrivilegedPackages);
}
@Override
@@ -285,7 +317,15 @@
final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj;
return mSubIdToGroupMap.equals(other.mSubIdToGroupMap)
- && mActiveGroups.equals(other.mActiveGroups);
+ && mPrivilegedPackages.equals(other.mPrivilegedPackages);
+ }
+
+ @Override
+ public String toString() {
+ return "TelephonySubscriptionSnapshot{ "
+ + "mSubIdToGroupMap=" + mSubIdToGroupMap
+ + ", mPrivilegedPackages=" + mPrivilegedPackages
+ + " }";
}
}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index d51d16b..9d21b92 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -16,32 +16,69 @@
package com.android.server.vcn;
+
import android.annotation.NonNull;
+import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnGatewayConnectionConfig;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
+import android.util.Slog;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
/**
* Represents an single instance of a VCN.
*
- * <p>Each Vcn instance manages all tunnels for a given subscription group, including per-capability
- * networks, network selection, and multi-homing.
+ * <p>Each Vcn instance manages all {@link VcnGatewayConnection}(s) for a given subscription group,
+ * including per-capability networks, network selection, and multi-homing.
*
* @hide
*/
public class Vcn extends Handler {
private static final String TAG = Vcn.class.getSimpleName();
+ private static final int MSG_EVENT_BASE = 0;
+ private static final int MSG_CMD_BASE = 100;
+
+ /**
+ * A carrier app updated the configuration.
+ *
+ * <p>Triggers update of config, re-evaluating all active and underlying networks.
+ *
+ * @param obj VcnConfig
+ */
+ private static final int MSG_EVENT_CONFIG_UPDATED = MSG_EVENT_BASE;
+
+ /**
+ * A NetworkRequest was added or updated.
+ *
+ * <p>Triggers an evaluation of all active networks, bringing up a new one if necessary.
+ *
+ * @param obj NetworkRequest
+ */
+ private static final int MSG_EVENT_NETWORK_REQUESTED = MSG_EVENT_BASE + 1;
+
+ /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
+ private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
+
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
+ @NonNull private final VcnNetworkRequestListener mRequestListener;
+
+ @NonNull
+ private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
+ new HashMap<>();
@NonNull private VcnConfig mConfig;
+ private boolean mIsRunning = true;
+
public Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@@ -58,31 +95,123 @@
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mDeps = Objects.requireNonNull(deps, "Missing deps");
+ mRequestListener = new VcnNetworkRequestListener();
mConfig = Objects.requireNonNull(config, "Missing config");
+
+ // Register to receive cached and future NetworkRequests
+ mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
}
/** Asynchronously updates the configuration and triggers a re-evaluation of Networks */
public void updateConfig(@NonNull VcnConfig config) {
Objects.requireNonNull(config, "Missing config");
- // TODO: Proxy to handler, and make config there.
+
+ sendMessage(obtainMessage(MSG_EVENT_CONFIG_UPDATED, config));
}
- /** Asynchronously tears down this Vcn instance, along with all tunnels and Networks */
- public void teardown() {
- // TODO: Proxy to handler, and teardown there.
+ /** Asynchronously tears down this Vcn instance, including VcnGatewayConnection(s) */
+ public void teardownAsynchronously() {
+ sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
}
- /** Notifies this Vcn instance of a new NetworkRequest */
- public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
- Objects.requireNonNull(request, "Missing request");
+ private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
+ @Override
+ public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
+ Objects.requireNonNull(request, "Missing request");
- // TODO: Proxy to handler, and handle there.
+ sendMessage(obtainMessage(MSG_EVENT_NETWORK_REQUESTED, score, providerId, request));
+ }
}
@Override
public void handleMessage(@NonNull Message msg) {
- // TODO: Do something
+ if (!mIsRunning) {
+ return;
+ }
+
+ switch (msg.what) {
+ case MSG_EVENT_CONFIG_UPDATED:
+ handleConfigUpdated((VcnConfig) msg.obj);
+ break;
+ case MSG_EVENT_NETWORK_REQUESTED:
+ handleNetworkRequested((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
+ break;
+ case MSG_CMD_TEARDOWN:
+ handleTeardown();
+ break;
+ default:
+ Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
+ }
+ }
+
+ private void handleConfigUpdated(@NonNull VcnConfig config) {
+ // TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode()
+ Slog.v(getLogTag(), String.format("Config updated: config = %s", config.hashCode()));
+
+ mConfig = config;
+
+ // TODO: Reevaluate active VcnGatewayConnection(s)
+ }
+
+ private void handleTeardown() {
+ mVcnContext.getVcnNetworkProvider().unregisterListener(mRequestListener);
+
+ for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
+ gatewayConnection.teardownAsynchronously();
+ }
+
+ mIsRunning = false;
+ }
+
+ private void handleNetworkRequested(
+ @NonNull NetworkRequest request, int score, int providerId) {
+ if (score > getNetworkScore()) {
+ Slog.v(getLogTag(),
+ "Request " + request.requestId + " already satisfied by higher-scoring ("
+ + score + ") network from provider " + providerId);
+ return;
+ }
+
+ // If preexisting VcnGatewayConnection(s) satisfy request, return
+ for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
+ if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
+ Slog.v(getLogTag(),
+ "Request " + request.requestId
+ + " satisfied by existing VcnGatewayConnection");
+ return;
+ }
+ }
+
+ // If any supported (but not running) VcnGatewayConnection(s) can satisfy request, bring it
+ // up
+ for (VcnGatewayConnectionConfig gatewayConnectionConfig :
+ mConfig.getGatewayConnectionConfigs()) {
+ if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
+ Slog.v(
+ getLogTag(),
+ "Bringing up new VcnGatewayConnection for request " + request.requestId);
+
+ final VcnGatewayConnection vcnGatewayConnection =
+ new VcnGatewayConnection(
+ mVcnContext, mSubscriptionGroup, gatewayConnectionConfig);
+ mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
+ }
+ }
+ }
+
+ private boolean requestSatisfiedByGatewayConnectionConfig(
+ @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
+ final NetworkCapabilities configCaps = new NetworkCapabilities();
+ for (int cap : config.getAllExposedCapabilities()) {
+ configCaps.addCapability(cap);
+ }
+
+ return request.networkCapabilities.satisfiedByNetworkCapabilities(configCaps);
+ }
+
+ private String getLogTag() {
+ return String.format("%s [%d]", TAG, mSubscriptionGroup.hashCode());
}
/** Retrieves the network score for a VCN Network */
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index 8ab52931..dba59bd 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -20,8 +20,6 @@
import android.content.Context;
import android.os.Looper;
-import com.android.server.VcnManagementService.VcnNetworkProvider;
-
import java.util.Objects;
/**
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 49c9b32..7ea8e04 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -65,8 +65,8 @@
mDeps.newUnderlyingNetworkTracker(mVcnContext, subscriptionGroup, this);
}
- /** Tears down this GatewayConnection, and any resources used */
- public void teardown() {
+ /** Asynchronously tears down this GatewayConnection, and any resources used */
+ public void teardownAsynchronously() {
mUnderlyingNetworkTracker.teardown();
}
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
new file mode 100644
index 0000000..7f5b23c
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -0,0 +1,108 @@
+/*
+ * 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 android.annotation.NonNull;
+import android.content.Context;
+import android.net.NetworkProvider;
+import android.net.NetworkRequest;
+import android.os.Looper;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * VCN Network Provider routes NetworkRequests to listeners to bring up tunnels as needed.
+ *
+ * <p>The VcnNetworkProvider provides a caching layer to ensure that all listeners receive all
+ * active NetworkRequest(s), including ones that were filed prior to listener registration.
+ *
+ * @hide
+ */
+public class VcnNetworkProvider extends NetworkProvider {
+ private static final String TAG = VcnNetworkProvider.class.getSimpleName();
+
+ private final Set<NetworkRequestListener> mListeners = new ArraySet<>();
+ private final SparseArray<NetworkRequestEntry> mRequests = new SparseArray<>();
+
+ public VcnNetworkProvider(Context context, Looper looper) {
+ super(context, looper, VcnNetworkProvider.class.getSimpleName());
+ }
+
+ // Package-private
+ void registerListener(@NonNull NetworkRequestListener listener) {
+ mListeners.add(listener);
+
+ // Send listener all cached requests
+ for (int i = 0; i < mRequests.size(); i++) {
+ notifyListenerForEvent(listener, mRequests.valueAt(i));
+ }
+ }
+
+ // Package-private
+ void unregisterListener(@NonNull NetworkRequestListener listener) {
+ mListeners.remove(listener);
+ }
+
+ private void notifyListenerForEvent(
+ @NonNull NetworkRequestListener listener, @NonNull NetworkRequestEntry entry) {
+ listener.onNetworkRequested(entry.mRequest, entry.mScore, entry.mProviderId);
+ }
+
+ @Override
+ public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
+ Slog.v(
+ TAG,
+ String.format(
+ "Network requested: Request = %s, score = %d, providerId = %d",
+ request, score, providerId));
+
+ final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId);
+ mRequests.put(request.requestId, entry);
+
+ // TODO(b/176939047): Intelligently route requests to prioritized VcnInstances (based on
+ // Default Data Sub, or similar)
+ for (NetworkRequestListener listener : mListeners) {
+ notifyListenerForEvent(listener, entry);
+ }
+ }
+
+ @Override
+ public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {
+ mRequests.remove(request.requestId);
+ }
+
+ private static class NetworkRequestEntry {
+ public final NetworkRequest mRequest;
+ public final int mScore;
+ public final int mProviderId;
+
+ private NetworkRequestEntry(@NonNull NetworkRequest request, int score, int providerId) {
+ mRequest = Objects.requireNonNull(request, "Missing request");
+ mScore = score;
+ mProviderId = providerId;
+ }
+ }
+
+ // package-private
+ interface NetworkRequestListener {
+ void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9cbe277..461bbfb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3204,33 +3204,6 @@
}
/**
- * Moves the top activity in the input rootTaskId to the pinned root task.
- *
- * @param rootTaskId Id of root task to move the top activity to pinned root task.
- * @param bounds Bounds to use for pinned root task.
- * @return True if the top activity of the input stack was successfully moved to the pinned root
- * task.
- */
- @Override
- public boolean moveTopActivityToPinnedRootTask(int rootTaskId, Rect bounds) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS,
- "moveTopActivityToPinnedRootTask()");
- synchronized (mGlobalLock) {
- if (!mSupportsPictureInPicture) {
- throw new IllegalStateException("moveTopActivityToPinnedRootTask:"
- + "Device doesn't support picture-in-picture mode");
- }
-
- final long ident = Binder.clearCallingIdentity();
- try {
- return mRootWindowContainer.moveTopRootTaskActivityToPinnedRootTask(rootTaskId);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- /**
* Puts the given activity in picture in picture mode if possible.
*
* @return true if the activity is now in picture-in-picture mode, or false if it could not
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 5952164..eeb7fac 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -24,11 +24,13 @@
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
import static android.window.DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT;
import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
+import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
@@ -131,14 +133,19 @@
.all()
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,
TYPE_NOTIFICATION_SHADE)
- .build());
+ .build())
+ .addFeature(new Feature.Builder(wmService.mPolicy,
+ "OneHandedBackgroundPanel",
+ FEATURE_ONE_HANDED_BACKGROUND_PANEL)
+ .upTo(TYPE_WALLPAPER)
+ .build())
+ .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
+ FEATURE_ONE_HANDED)
+ .all()
+ .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
+ .build());
}
rootHierarchy
- .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
- FEATURE_ONE_HANDED)
- .all()
- .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
- .build())
.addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification",
FEATURE_FULLSCREEN_MAGNIFICATION)
.all()
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index ff5b356..40e7a8e 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.os.Build.IS_DEBUGGABLE;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
@@ -552,6 +553,11 @@
// TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed.
t.setAlpha(animationLeash, 1 /* alpha */);
t.hide(animationLeash);
+
+ // TODO(b/175954493): Remove this after finding root cause.
+ if (IS_DEBUGGABLE) {
+ animationLeash.setDebugRelease(true);
+ }
}
ProtoLog.i(WM_DEBUG_IME,
"ControlAdapter startAnimation mSource: %s controlTarget: %s", mSource,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index aa88657..1c41978 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6102,13 +6102,13 @@
mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
ActivityRecord lastResumed = null;
- final Task lastFocusedStack = taskDisplayArea.getLastFocusedRootTask();
- if (lastFocusedStack != null && lastFocusedStack != this) {
+ final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();
+ if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTask()) {
// So, why aren't we using prev here??? See the param comment on the method. prev
// doesn't represent the last resumed activity. However, the last focus stack does if
// it isn't null.
- lastResumed = lastFocusedStack.getResumedActivity();
- if (userLeaving && inMultiWindowMode() && lastFocusedStack.shouldBeVisible(next)) {
+ lastResumed = lastFocusedRootTask.getResumedActivity();
+ if (userLeaving && inMultiWindowMode() && lastFocusedRootTask.shouldBeVisible(next)) {
// The user isn't leaving if this stack is the multi-window mode and the last
// focused stack should still be visible.
if(DEBUG_USER_LEAVING) Slog.i(TAG_USER_LEAVING, "Overriding userLeaving to false"
@@ -6262,10 +6262,10 @@
// Launcher is already visible in this case. If we don't add it to opening
// apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
// TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
- final boolean lastActivityTranslucent = lastFocusedStack != null
- && (lastFocusedStack.inMultiWindowMode()
- || (lastFocusedStack.mLastPausedActivity != null
- && !lastFocusedStack.mLastPausedActivity.occludesParent()));
+ final boolean lastActivityTranslucent = lastFocusedRootTask != null
+ && (lastFocusedRootTask.inMultiWindowMode()
+ || (lastFocusedRootTask.mLastPausedActivity != null
+ && !lastFocusedRootTask.mLastPausedActivity.occludesParent()));
// This activity is now becoming visible.
if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
@@ -6276,7 +6276,7 @@
next.startLaunchTickingLocked();
ActivityRecord lastResumedActivity =
- lastFocusedStack == null ? null : lastFocusedStack.getResumedActivity();
+ lastFocusedRootTask == null ? null : lastFocusedRootTask.getResumedActivity();
final ActivityState lastState = next.getState();
mAtmService.updateCpuStats();
@@ -6373,8 +6373,8 @@
Slog.i(TAG, "Restarting because process died: " + next);
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedStack != null
- && lastFocusedStack.isTopRootTaskInDisplayArea()) {
+ } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
+ && lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
next.showStartingWindow(null /* prev */, false /* newTask */,
false /* taskSwitch */);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3bdc16f..4eeae6c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2332,9 +2332,15 @@
}
}
- // Create surfaceControl before surface placement otherwise layout will be skipped
- // (because WS.isGoneForLayout() is true when there is no surface.
+ // We may be deferring layout passes at the moment, but since the client is interested
+ // in the new out values right now we need to force a layout.
+ mWindowPlacerLocked.performSurfacePlacement(true /* force */);
+
if (shouldRelayout) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
+
+ result = win.relayoutVisibleWindow(result, attrChanges);
+
try {
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
@@ -2346,17 +2352,6 @@
Binder.restoreCallingIdentity(origId);
return 0;
}
- }
-
- // We may be deferring layout passes at the moment, but since the client is interested
- // in the new out values right now we need to force a layout.
- mWindowPlacerLocked.performSurfacePlacement(true /* force */);
-
- if (shouldRelayout) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
-
- result = win.relayoutVisibleWindow(result, attrChanges);
-
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = true;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fdbd85a..3e51b75 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8421,7 +8421,10 @@
return null;
}
Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ return getProfileOwnerNameUnchecked(userHandle);
+ }
+ private String getProfileOwnerNameUnchecked(int userHandle) {
ComponentName profileOwner = getProfileOwnerAsUser(userHandle);
if (profileOwner == null) {
return null;
@@ -9731,7 +9734,8 @@
// Set admin.
setActiveAdmin(profileOwner, /* refreshing= */ true, userId);
- final String ownerName = getProfileOwnerName(Process.myUserHandle().getIdentifier());
+ final String ownerName = getProfileOwnerNameUnchecked(
+ Process.myUserHandle().getIdentifier());
setProfileOwner(profileOwner, ownerName, userId);
synchronized (getLockObject()) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index fff7f80..4d15ced 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -680,7 +680,9 @@
// Load preinstalled system fonts for system server, so that WindowManagerService, etc
// can start using Typeface. Note that fonts are required not only for text rendering,
// but also for some text operations (e.g. TextUtils.makeSafeForPresentation()).
- Typeface.loadPreinstalledSystemFontMap();
+ if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
+ Typeface.loadPreinstalledSystemFontMap();
+ }
// Attach JVMTI agent if this is a debuggable build and the system property is set.
if (Build.IS_DEBUGGABLE) {
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
index 3531512..0cb729d 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
@@ -16,6 +16,7 @@
package com.android.server.musicrecognition;
+import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_AUDIO_UNAVAILABLE;
import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_KILLED;
import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_UNAVAILABLE;
import static android.media.musicrecognition.MusicRecognitionManager.RecognitionFailureCode;
@@ -64,10 +65,6 @@
@GuardedBy("mLock")
private RemoteMusicRecognitionService mRemoteService;
- private MusicRecognitionServiceCallback mRemoteServiceCallback =
- new MusicRecognitionServiceCallback();
- private IMusicRecognitionManagerCallback mCallback;
-
MusicRecognitionManagerPerUserService(
@NonNull MusicRecognitionManagerService primary,
@NonNull Object lock, int userId) {
@@ -100,7 +97,8 @@
@GuardedBy("mLock")
@Nullable
- private RemoteMusicRecognitionService ensureRemoteServiceLocked() {
+ private RemoteMusicRecognitionService ensureRemoteServiceLocked(
+ IMusicRecognitionManagerCallback clientCallback) {
if (mRemoteService == null) {
final String serviceName = getComponentNameLocked();
if (serviceName == null) {
@@ -113,7 +111,8 @@
mRemoteService = new RemoteMusicRecognitionService(getContext(),
serviceComponent, mUserId, this,
- mRemoteServiceCallback, mMaster.isBindInstantServiceAllowed(),
+ new MusicRecognitionServiceCallback(clientCallback),
+ mMaster.isBindInstantServiceAllowed(),
mMaster.verbose);
}
@@ -130,13 +129,14 @@
@NonNull IBinder callback) {
int maxAudioLengthSeconds = Math.min(recognitionRequest.getMaxAudioLengthSeconds(),
MAX_STREAMING_SECONDS);
- mCallback = IMusicRecognitionManagerCallback.Stub.asInterface(callback);
+ IMusicRecognitionManagerCallback clientCallback =
+ IMusicRecognitionManagerCallback.Stub.asInterface(callback);
AudioRecord audioRecord = createAudioRecord(recognitionRequest, maxAudioLengthSeconds);
- mRemoteService = ensureRemoteServiceLocked();
+ mRemoteService = ensureRemoteServiceLocked(clientCallback);
if (mRemoteService == null) {
try {
- mCallback.onRecognitionFailed(
+ clientCallback.onRecognitionFailed(
RECOGNITION_FAILED_SERVICE_UNAVAILABLE);
} catch (RemoteException e) {
// Ignored.
@@ -147,7 +147,8 @@
Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
if (clientPipe == null) {
try {
- mCallback.onAudioStreamClosed();
+ clientCallback.onRecognitionFailed(
+ RECOGNITION_FAILED_AUDIO_UNAVAILABLE);
} catch (RemoteException ignored) {
// Ignored.
}
@@ -192,11 +193,10 @@
} finally {
audioRecord.release();
try {
- mCallback.onAudioStreamClosed();
+ clientCallback.onAudioStreamClosed();
} catch (RemoteException ignored) {
// Ignored.
}
-
}
});
// Send the pipe down to the lookup service while we write to it asynchronously.
@@ -207,13 +207,20 @@
* Callback invoked by {@link android.service.musicrecognition.MusicRecognitionService} to pass
* back the music search result.
*/
- private final class MusicRecognitionServiceCallback extends
+ final class MusicRecognitionServiceCallback extends
IMusicRecognitionServiceCallback.Stub {
+
+ private final IMusicRecognitionManagerCallback mClientCallback;
+
+ private MusicRecognitionServiceCallback(IMusicRecognitionManagerCallback clientCallback) {
+ mClientCallback = clientCallback;
+ }
+
@Override
public void onRecognitionSucceeded(MediaMetadata result, Bundle extras) {
try {
sanitizeBundle(extras);
- mCallback.onRecognitionSucceeded(result, extras);
+ mClientCallback.onRecognitionSucceeded(result, extras);
} catch (RemoteException ignored) {
// Ignored.
}
@@ -223,18 +230,23 @@
@Override
public void onRecognitionFailed(@RecognitionFailureCode int failureCode) {
try {
- mCallback.onRecognitionFailed(failureCode);
+ mClientCallback.onRecognitionFailed(failureCode);
} catch (RemoteException ignored) {
// Ignored.
}
destroyService();
}
+
+ private IMusicRecognitionManagerCallback getClientCallback() {
+ return mClientCallback;
+ }
}
@Override
public void onServiceDied(@NonNull RemoteMusicRecognitionService service) {
try {
- mCallback.onRecognitionFailed(RECOGNITION_FAILED_SERVICE_KILLED);
+ service.getServerCallback().getClientCallback().onRecognitionFailed(
+ RECOGNITION_FAILED_SERVICE_KILLED);
} catch (RemoteException e) {
// Ignored.
}
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java
index 9123daf..38f43138 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java
@@ -22,7 +22,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.media.musicrecognition.IMusicRecognitionManager;
import android.media.musicrecognition.IMusicRecognitionManagerCallback;
import android.media.musicrecognition.RecognitionRequest;
@@ -32,7 +34,9 @@
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
+import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
@@ -113,9 +117,11 @@
enforceCaller("beginRecognition");
synchronized (mLock) {
+ int userId = UserHandle.getCallingUserId();
final MusicRecognitionManagerPerUserService service = getServiceForUserLocked(
- UserHandle.getCallingUserId());
- if (service != null) {
+ userId);
+ if (service != null && (isDefaultServiceLocked(userId)
+ || isCalledByServiceAppLocked("beginRecognition"))) {
service.beginRecognitionLocked(recognitionRequest, callback);
} else {
try {
@@ -139,5 +145,55 @@
MusicRecognitionManagerService.this).exec(this, in, out, err, args, callback,
resultReceiver);
}
+
+ /** True if the currently set handler service is not overridden by the shell. */
+ @GuardedBy("mLock")
+ private boolean isDefaultServiceLocked(int userId) {
+ final String defaultServiceName = mServiceNameResolver.getDefaultServiceName(userId);
+ if (defaultServiceName == null) {
+ return false;
+ }
+
+ final String currentServiceName = mServiceNameResolver.getServiceName(userId);
+ return defaultServiceName.equals(currentServiceName);
+ }
+
+ /** True if the caller of the api is the same app which hosts the default service. */
+ @GuardedBy("mLock")
+ private boolean isCalledByServiceAppLocked(@NonNull String methodName) {
+ final int userId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
+ final String serviceName = mServiceNameResolver.getServiceName(userId);
+ if (serviceName == null) {
+ Slog.e(TAG, methodName + ": called by UID " + callingUid
+ + ", but there's no service set for user " + userId);
+ return false;
+ }
+
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+ if (serviceComponent == null) {
+ Slog.w(TAG, methodName + ": invalid service name: " + serviceName);
+ return false;
+ }
+
+ final String servicePackageName = serviceComponent.getPackageName();
+
+ final PackageManager pm = getContext().getPackageManager();
+ final int serviceUid;
+ try {
+ serviceUid = pm.getPackageUidAsUser(servicePackageName,
+ UserHandle.getCallingUserId());
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, methodName + ": could not verify UID for " + serviceName);
+ return false;
+ }
+ if (callingUid != serviceUid) {
+ Slog.e(TAG, methodName + ": called by UID " + callingUid + ", but service UID is "
+ + serviceUid);
+ return false;
+ }
+
+ return true;
+ }
}
}
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
index 4814a82..6c7d673 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
@@ -21,13 +21,13 @@
import android.content.Context;
import android.media.AudioFormat;
import android.media.musicrecognition.IMusicRecognitionService;
-import android.media.musicrecognition.IMusicRecognitionServiceCallback;
import android.media.musicrecognition.MusicRecognitionService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.text.format.DateUtils;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+import com.android.server.musicrecognition.MusicRecognitionManagerPerUserService.MusicRecognitionServiceCallback;
/** Remote connection to an instance of {@link MusicRecognitionService}. */
public class RemoteMusicRecognitionService extends
@@ -39,11 +39,12 @@
private static final long TIMEOUT_IDLE_BIND_MILLIS = 40 * DateUtils.SECOND_IN_MILLIS;
// Allows the remote service to send back a result.
- private final IMusicRecognitionServiceCallback mServerCallback;
+ private final MusicRecognitionServiceCallback
+ mServerCallback;
public RemoteMusicRecognitionService(Context context, ComponentName serviceName,
int userId, MusicRecognitionManagerPerUserService perUserService,
- IMusicRecognitionServiceCallback callback,
+ MusicRecognitionServiceCallback callback,
boolean bindInstantServiceAllowed, boolean verbose) {
super(context, MusicRecognitionService.ACTION_MUSIC_SEARCH_LOOKUP, serviceName, userId,
perUserService,
@@ -66,6 +67,10 @@
return TIMEOUT_IDLE_BIND_MILLIS;
}
+ MusicRecognitionServiceCallback getServerCallback() {
+ return mServerCallback;
+ }
+
/**
* Required, but empty since we don't need to notify the callback implementation of the request
* results.
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 2a29674..7036ccf 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -13,7 +13,7 @@
":services.net-sources",
],
static_libs: [
- "netd_aidl_interfaces-platform-java",
+ "netd-client",
"netlink-client",
"networkstack-client",
"net-utils-services-common",
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 49a41f0..16b9165 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -19,7 +19,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.people.ConversationChannel;
+import android.app.people.ConversationStatus;
import android.app.people.IPeopleManager;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
@@ -27,6 +29,7 @@
import android.app.prediction.AppTargetEvent;
import android.app.prediction.IPredictionCallback;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.CancellationSignal;
@@ -38,9 +41,11 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.people.data.DataManager;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -54,6 +59,8 @@
private final DataManager mDataManager;
+ private PackageManagerInternal mPackageManagerInternal;
+
/**
* Initializes the system service.
*
@@ -83,6 +90,7 @@
publishBinderService(Context.PEOPLE_SERVICE, mService);
}
publishLocalService(PeopleServiceInternal.class, new LocalService());
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
}
@Override
@@ -112,6 +120,26 @@
return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || uid == Process.ROOT_UID;
}
+ private int handleIncomingUser(int userId) {
+ try {
+ return ActivityManager.getService().handleIncomingUser(
+ Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
+ } catch (RemoteException re) {
+ // Shouldn't happen, local.
+ }
+ return userId;
+ }
+
+ private void checkCallerIsSameApp(String pkg) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+
+ if (mPackageManagerInternal.getPackageUid(pkg, /*flags=*/ 0,
+ callingUserId) != callingUid) {
+ throw new SecurityException("Calling uid " + callingUid + " cannot query events"
+ + "for package " + pkg);
+ }
+ }
/**
* Enforces that only the system, root UID or SystemUI can make certain calls.
@@ -154,6 +182,40 @@
enforceSystemRootOrSystemUI(getContext(), "get last interaction");
return mDataManager.getLastInteraction(packageName, userId, shortcutId);
}
+
+ @Override
+ public void addOrUpdateStatus(String packageName, int userId, String conversationId,
+ ConversationStatus status) {
+ handleIncomingUser(userId);
+ checkCallerIsSameApp(packageName);
+ mDataManager.addOrUpdateStatus(packageName, userId, conversationId, status);
+ }
+
+ @Override
+ public void clearStatus(String packageName, int userId, String conversationId,
+ String statusId) {
+ handleIncomingUser(userId);
+ checkCallerIsSameApp(packageName);
+ mDataManager.clearStatus(packageName, userId, conversationId, statusId);
+ }
+
+ @Override
+ public void clearStatuses(String packageName, int userId, String conversationId) {
+ handleIncomingUser(userId);
+ checkCallerIsSameApp(packageName);
+ mDataManager.clearStatuses(packageName, userId, conversationId);
+ }
+
+ @Override
+ public ParceledListSlice<ConversationStatus> getStatuses(String packageName, int userId,
+ String conversationId) {
+ handleIncomingUser(userId);
+ if (!isSystemOrRoot()) {
+ checkCallerIsSameApp(packageName);
+ }
+ return new ParceledListSlice<>(
+ mDataManager.getStatuses(packageName, userId, conversationId));
+ }
};
@VisibleForTesting
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index 45f389c..16c4c29 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.people.ConversationStatus;
import android.content.LocusId;
import android.content.LocusIdProto;
import android.content.pm.ShortcutInfo;
@@ -39,6 +40,10 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -101,6 +106,8 @@
@ConversationFlags
private int mConversationFlags;
+ private Map<String, ConversationStatus> mCurrStatuses;
+
private ConversationInfo(Builder builder) {
mShortcutId = builder.mShortcutId;
mLocusId = builder.mLocusId;
@@ -111,6 +118,7 @@
mLastEventTimestamp = builder.mLastEventTimestamp;
mShortcutFlags = builder.mShortcutFlags;
mConversationFlags = builder.mConversationFlags;
+ mCurrStatuses = builder.mCurrStatuses;
}
@NonNull
@@ -213,6 +221,10 @@
return hasConversationFlags(FLAG_CONTACT_STARRED);
}
+ public Collection<ConversationStatus> getStatuses() {
+ return mCurrStatuses.values();
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -230,14 +242,15 @@
&& Objects.equals(mParentNotificationChannelId, other.mParentNotificationChannelId)
&& Objects.equals(mLastEventTimestamp, other.mLastEventTimestamp)
&& mShortcutFlags == other.mShortcutFlags
- && mConversationFlags == other.mConversationFlags;
+ && mConversationFlags == other.mConversationFlags
+ && Objects.equals(mCurrStatuses, other.mCurrStatuses);
}
@Override
public int hashCode() {
return Objects.hash(mShortcutId, mLocusId, mContactUri, mContactPhoneNumber,
mNotificationChannelId, mParentNotificationChannelId, mLastEventTimestamp,
- mShortcutFlags, mConversationFlags);
+ mShortcutFlags, mConversationFlags, mCurrStatuses);
}
@Override
@@ -251,6 +264,7 @@
sb.append(", notificationChannelId=").append(mNotificationChannelId);
sb.append(", parentNotificationChannelId=").append(mParentNotificationChannelId);
sb.append(", lastEventTimestamp=").append(mLastEventTimestamp);
+ sb.append(", statuses=").append(mCurrStatuses);
sb.append(", shortcutFlags=0x").append(Integer.toHexString(mShortcutFlags));
sb.append(" [");
if (isShortcutLongLived()) {
@@ -321,6 +335,7 @@
protoOutputStream.write(ConversationInfoProto.CONTACT_PHONE_NUMBER,
mContactPhoneNumber);
}
+ // ConversationStatus is a transient object and not persisted
}
@Nullable
@@ -337,6 +352,7 @@
out.writeUTF(mContactPhoneNumber != null ? mContactPhoneNumber : "");
out.writeUTF(mParentNotificationChannelId != null ? mParentNotificationChannelId : "");
out.writeLong(mLastEventTimestamp);
+ // ConversationStatus is a transient object and not persisted
} catch (IOException e) {
Slog.e(TAG, "Failed to write fields to backup payload.", e);
return null;
@@ -469,6 +485,8 @@
@ConversationFlags
private int mConversationFlags;
+ private Map<String, ConversationStatus> mCurrStatuses = new HashMap<>();
+
Builder() {
}
@@ -486,6 +504,7 @@
mLastEventTimestamp = conversationInfo.mLastEventTimestamp;
mShortcutFlags = conversationInfo.mShortcutFlags;
mConversationFlags = conversationInfo.mConversationFlags;
+ mCurrStatuses = conversationInfo.mCurrStatuses;
}
Builder setShortcutId(@NonNull String shortcutId) {
@@ -579,6 +598,26 @@
return this;
}
+ Builder setStatuses(List<ConversationStatus> statuses) {
+ mCurrStatuses.clear();
+ if (statuses != null) {
+ for (ConversationStatus status : statuses) {
+ mCurrStatuses.put(status.getId(), status);
+ }
+ }
+ return this;
+ }
+
+ Builder addOrUpdateStatus(ConversationStatus status) {
+ mCurrStatuses.put(status.getId(), status);
+ return this;
+ }
+
+ Builder clearStatus(String statusId) {
+ mCurrStatuses.remove(statusId);
+ return this;
+ }
+
ConversationInfo build() {
Objects.requireNonNull(mShortcutId);
return new ConversationInfo(this);
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index b5e595a..e04e287 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -26,6 +26,7 @@
import android.app.NotificationManager;
import android.app.Person;
import android.app.people.ConversationChannel;
+import android.app.people.ConversationStatus;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.usage.UsageEvents;
@@ -75,6 +76,7 @@
import com.android.server.notification.ShortcutHelper;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
@@ -308,6 +310,73 @@
return 0L;
}
+ public void addOrUpdateStatus(String packageName, int userId, String conversationId,
+ ConversationStatus status) {
+ ConversationStore cs = getConversationStoreOrThrow(packageName, userId);
+ ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId);
+ ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
+ builder.addOrUpdateStatus(status);
+ cs.addOrUpdate(builder.build());
+ }
+
+ public void clearStatus(String packageName, int userId, String conversationId,
+ String statusId) {
+ ConversationStore cs = getConversationStoreOrThrow(packageName, userId);
+ ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId);
+ ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
+ builder.clearStatus(statusId);
+ cs.addOrUpdate(builder.build());
+ }
+
+ public void clearStatuses(String packageName, int userId, String conversationId) {
+ ConversationStore cs = getConversationStoreOrThrow(packageName, userId);
+ ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId);
+ ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
+ builder.setStatuses(null);
+ cs.addOrUpdate(builder.build());
+ }
+
+ public @NonNull List<ConversationStatus> getStatuses(String packageName, int userId,
+ String conversationId) {
+ ConversationStore cs = getConversationStoreOrThrow(packageName, userId);
+ ConversationInfo conversationInfo = getConversationInfoOrThrow(cs, conversationId);
+ Collection<ConversationStatus> statuses = conversationInfo.getStatuses();
+ if (statuses != null) {
+ final ArrayList<ConversationStatus> list = new ArrayList<>(statuses.size());
+ list.addAll(statuses);
+ return list;
+ }
+ return new ArrayList<>();
+ }
+
+ /**
+ * Returns a conversation store for a package, if it exists.
+ */
+ private @NonNull ConversationStore getConversationStoreOrThrow(String packageName, int userId) {
+ final PackageData packageData = getPackage(packageName, userId);
+ if (packageData == null) {
+ throw new IllegalArgumentException("No settings exist for package " + packageName);
+ }
+ ConversationStore cs = packageData.getConversationStore();
+ if (cs == null) {
+ throw new IllegalArgumentException("No conversations exist for package " + packageName);
+ }
+ return cs;
+ }
+
+ /**
+ * Returns a conversation store for a package, if it exists.
+ */
+ private @NonNull ConversationInfo getConversationInfoOrThrow(ConversationStore cs,
+ String conversationId) {
+ ConversationInfo ci = cs.getConversation(conversationId);
+
+ if (ci == null) {
+ throw new IllegalArgumentException("Conversation does not exist");
+ }
+ return ci;
+ }
+
/** Reports the sharing related {@link AppTargetEvent} from App Prediction Manager. */
public void reportShareTargetEvent(@NonNull AppTargetEvent event,
@NonNull IntentFilter intentFilter) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index df7a445..84bfc9b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -19,10 +19,8 @@
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-import static android.location.Criteria.ACCURACY_COARSE;
-import static android.location.Criteria.ACCURACY_FINE;
-import static android.location.Criteria.POWER_HIGH;
import static android.location.LocationRequest.PASSIVE_INTERVAL;
+import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
import static androidx.test.ext.truth.location.LocationSubject.assertThat;
@@ -66,7 +64,8 @@
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
import android.os.ICancellationSignal;
@@ -81,7 +80,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.location.ProviderRequest;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.location.injector.FakeUserInfoHelper;
@@ -115,8 +113,13 @@
private static final int OTHER_USER = CURRENT_USER + 10;
private static final String NAME = "test";
- private static final ProviderProperties PROPERTIES = new ProviderProperties(false, false, false,
- false, true, true, true, POWER_HIGH, ACCURACY_FINE);
+ private static final ProviderProperties PROPERTIES = new ProviderProperties.Builder()
+ .setHasAltitudeSupport(true)
+ .setHasSpeedSupport(true)
+ .setHasBearingSupport(true)
+ .setPowerUsage(POWER_USAGE_HIGH)
+ .setAccuracy(ProviderProperties.ACCURACY_FINE)
+ .build();
private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1,
"mypackage",
"attribution");
@@ -189,8 +192,14 @@
assertThat(mManager.getIdentity()).isEqualTo(IDENTITY);
assertThat(mManager.hasProvider()).isTrue();
- ProviderProperties newProperties = new ProviderProperties(true, true, true,
- true, false, false, false, POWER_HIGH, ACCURACY_COARSE);
+ ProviderProperties newProperties = new ProviderProperties.Builder()
+ .setHasNetworkRequirement(true)
+ .setHasSatelliteRequirement(true)
+ .setHasCellRequirement(true)
+ .setHasMonetaryCost(true)
+ .setPowerUsage(POWER_USAGE_HIGH)
+ .setAccuracy(ProviderProperties.ACCURACY_COARSE)
+ .build();
mProvider.setProperties(newProperties);
assertThat(mManager.getProperties()).isEqualTo(newProperties);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index daa8a22..99846c5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -15,7 +15,9 @@
*/
package com.android.server.location.provider;
-import static com.android.internal.location.ProviderRequest.EMPTY_REQUEST;
+import static android.location.provider.ProviderProperties.ACCURACY_FINE;
+import static android.location.provider.ProviderProperties.POWER_USAGE_LOW;
+import static android.location.provider.ProviderRequest.EMPTY_REQUEST;
import static com.google.common.truth.Truth.assertThat;
@@ -24,17 +26,16 @@
import static org.mockito.Mockito.verify;
import static org.testng.Assert.assertThrows;
-import android.location.Criteria;
import android.location.Location;
import android.location.LocationResult;
-import android.location.ProviderProperties;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.location.ProviderRequest;
import com.android.server.location.test.FakeProvider;
import com.android.server.location.test.ProviderListenerCapture;
@@ -62,16 +63,14 @@
mRealMock = mock(FakeProvider.FakeProviderInterface.class);
mRealProvider = new FakeProvider(mRealMock);
- mMockProvider = new MockLocationProvider(new ProviderProperties(
- false,
- false,
- false,
- false,
- true,
- true,
- true,
- Criteria.POWER_LOW,
- Criteria.ACCURACY_FINE),
+ mMockProvider = new MockLocationProvider(
+ new ProviderProperties.Builder()
+ .setHasAltitudeSupport(true)
+ .setHasSpeedSupport(true)
+ .setHasBearingSupport(true)
+ .setPowerUsage(POWER_USAGE_LOW)
+ .setAccuracy(ACCURACY_FINE)
+ .build(),
CallerIdentity.forTest(0, 1, "testpackage", "test"));
mProvider = new MockableLocationProvider(lock);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
index 1eb0386..775bdd5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
@@ -16,9 +16,9 @@
package com.android.server.location.test;
+import android.location.provider.ProviderRequest;
import android.os.Bundle;
-import com.android.internal.location.ProviderRequest;
import com.android.server.location.provider.AbstractLocationProvider;
import java.io.FileDescriptor;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 1fc0751..24e7d7d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -218,7 +218,7 @@
@NonNull LazyDaemon<Object> lazyDaemon, int cookie) {
super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */,
TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */,
- 0 /* statsAction */, 0 /* statsClient */, true /* shouldLogMetrics */);
+ 0 /* statsAction */, 0 /* statsClient */);
}
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 3f65a46..a70c510 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -27,6 +27,7 @@
import static org.mockito.internal.verification.VerificationModeFactory.times;
import static org.testng.Assert.assertThrows;
+import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -35,6 +36,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.compat.AndroidBuildClassifier;
+import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.LocalServices;
@@ -44,6 +46,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.HashSet;
+import java.util.Set;
+
@RunWith(AndroidJUnit4.class)
public class PlatformCompatTest {
private static final String PACKAGE_NAME = "my.package";
@@ -70,6 +75,8 @@
new PackageManager.NameNotFoundException());
when(mPackageManagerInternal.getPackageUid(eq(PACKAGE_NAME), eq(0), anyInt()))
.thenReturn(-1);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenThrow(new PackageManager.NameNotFoundException());
mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
// Assume userdebug/eng non-final build
@@ -125,6 +132,38 @@
}
@Test
+ public void testOverrideAtInstallTime() throws Exception {
+ mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addEnabledChangeWithId(1L)
+ .addDisabledChangeWithId(2L)
+ .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.O, 3L)
+ .build();
+ mCompatConfig.forceNonDebuggableFinalForTest(true);
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+
+ // Before adding overrides.
+ assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isTrue();
+ assertThat(mPlatformCompat.isChangeEnabledByPackageName(2, PACKAGE_NAME, 0)).isFalse();
+ assertThat(mPlatformCompat.isChangeEnabledByPackageName(3, PACKAGE_NAME, 0)).isTrue();
+
+ // Add overrides.
+ Set<Long> enabled = new HashSet<>();
+ enabled.add(2L);
+ Set<Long> disabled = new HashSet<>();
+ disabled.add(1L);
+ disabled.add(3L);
+ ChangeConfig changeConfig = new ChangeConfig(enabled, disabled);
+ CompatibilityChangeConfig compatibilityChangeConfig =
+ new CompatibilityChangeConfig(changeConfig);
+ mPlatformCompat.setOverridesForTest(compatibilityChangeConfig, PACKAGE_NAME);
+
+ // After adding overrides.
+ assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isFalse();
+ assertThat(mPlatformCompat.isChangeEnabledByPackageName(2, PACKAGE_NAME, 0)).isTrue();
+ assertThat(mPlatformCompat.isChangeEnabledByPackageName(3, PACKAGE_NAME, 0)).isFalse();
+ }
+
+ @Test
public void testRegisterListenerToSameIdThrows() throws Exception {
// Registering a listener to change 1 is successful.
mPlatformCompat.registerListener(1, mListener1);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index ae59609..eb2f960 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -24,6 +24,7 @@
import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
import static com.android.server.hdmi.Constants.MESSAGE_DEVICE_VENDOR_ID;
import static com.android.server.hdmi.Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.google.common.truth.Truth.assertThat;
@@ -32,6 +33,7 @@
import static junit.framework.Assert.assertTrue;
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPortInfo;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -43,6 +45,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -53,6 +56,8 @@
/** Tests for {@link HdmiCecLocalDevice} class. */
public class HdmiCecLocalDeviceTest {
+ private FakeNativeWrapper mNativeWrapper;
+
private static int SendCecCommandFactory(int srcAddress, int dstAddress, byte[] body) {
switch (body[0] & 0xFF) {
/** {@link Constants#MESSAGE_GIVE_PHYSICAL_ADDRESS} */
@@ -104,6 +109,7 @@
private MyHdmiCecLocalDevice mHdmiLocalDevice;
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private TestLooper mTestLooper = new TestLooper();
private static int mDesAddr = -1;
private static int mSrcAddr = -1;
@@ -139,6 +145,10 @@
}
@Override
+ protected void writeStringSystemProperty(String key, String value) {
+ }
+
+ @Override
void standby() {
mStandbyMessageReceived = true;
}
@@ -149,8 +159,9 @@
}
};
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
+ mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, new FakeNativeWrapper(), mHdmiControlService.getAtomWriter());
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiLocalDevice = new MyHdmiCecLocalDevice(mHdmiControlService, DEVICE_TV);
mMessageValidator =
@@ -161,14 +172,25 @@
}
};
mHdmiControlService.setMessageValidator(mMessageValidator);
+
+ mLocalDevices.add(mHdmiLocalDevice);
+ HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
+ hdmiPortInfos[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+ mNativeWrapper.setPortInfo(hdmiPortInfos);
+ mNativeWrapper.setPortConnectionStatus(1, true);
+ mHdmiControlService.initService();
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mNativeWrapper.setPhysicalAddress(0x2000);
+ mTestLooper.dispatchAll();
}
@Test
- public void dispatchMessage_desNotValid() {
+ public void dispatchMessage_logicalAddressDoesNotMatch() {
HdmiCecMessage msg =
new HdmiCecMessage(
ADDR_TV,
- ADDR_TV,
+ ADDR_PLAYBACK_1,
Constants.MESSAGE_CEC_VERSION,
HdmiCecMessage.EMPTY_PARAM);
boolean handleResult = mHdmiLocalDevice.dispatchMessage(msg);
@@ -384,4 +406,26 @@
assertThat(mWakeupMessageReceived).isFalse();
assertThat(mStandbyMessageReceived).isTrue();
}
+
+ @Test
+ public void handleVendorCommand_notHandled() {
+ HdmiCecMessage vendorCommand = HdmiCecMessageBuilder.buildVendorCommand(ADDR_TV,
+ ADDR_PLAYBACK_1, new byte[]{0});
+ mNativeWrapper.onCecMessage(vendorCommand);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(ADDR_PLAYBACK_1, ADDR_TV,
+ vendorCommand.getOpcode(), Constants.ABORT_REFUSED);
+ }
+
+ @Test
+ public void handleVendorCommandWithId_notHandled_Cec14() {
+ HdmiCecMessage vendorCommand = HdmiCecMessageBuilder.buildVendorCommandWithId(ADDR_TV,
+ ADDR_PLAYBACK_1, 0x1234, new byte[]{0});
+ mNativeWrapper.onCecMessage(vendorCommand);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(ADDR_PLAYBACK_1, ADDR_TV,
+ vendorCommand.getOpcode(), Constants.ABORT_REFUSED);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index be8e569..99ecb86 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -41,6 +41,7 @@
import android.util.Log;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -131,6 +132,7 @@
}
@Test
+ @FlakyTest
public void testPowerWhiteList() throws Exception {
scheduleAndAssertJobStarted();
setAppOpsModeAllowed(false);
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
index 5cff208..972b3bb 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
@@ -89,6 +89,81 @@
}
@Test
+ public void initializationFailure_primary() {
+ ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+ mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout()
+ .plus(testEnvironment.getProviderInitializationTimeoutFuzz());
+
+ mTestPrimaryLocationTimeZoneProvider.setFailDuringInitialization(true);
+
+ // Initialize. After initialization the providers must be initialized and one should be
+ // started.
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestPrimaryLocationTimeZoneProvider.assertInitialized();
+ mTestSecondaryLocationTimeZoneProvider.assertInitialized();
+
+ mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+ PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestSecondaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout);
+ mTestCallback.assertNoSuggestionMade();
+ assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ }
+
+ @Test
+ public void initializationFailure_secondary() {
+ ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+ mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout()
+ .plus(testEnvironment.getProviderInitializationTimeoutFuzz());
+
+ mTestSecondaryLocationTimeZoneProvider.setFailDuringInitialization(true);
+
+ // Initialize. After initialization the providers must be initialized and one should be
+ // started.
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestPrimaryLocationTimeZoneProvider.assertInitialized();
+ mTestSecondaryLocationTimeZoneProvider.assertInitialized();
+
+ mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+ PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestPrimaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout);
+ mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestCallback.assertNoSuggestionMade();
+ assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ }
+
+ @Test
+ public void initializationFailure_both() {
+ ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+ mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+ mTestPrimaryLocationTimeZoneProvider.setFailDuringInitialization(true);
+ mTestSecondaryLocationTimeZoneProvider.setFailDuringInitialization(true);
+
+ // Initialize. After initialization the providers must be initialized and one should be
+ // started.
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestPrimaryLocationTimeZoneProvider.assertInitialized();
+ mTestSecondaryLocationTimeZoneProvider.assertInitialized();
+
+ mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ }
+
+ @Test
public void initialState_started() {
ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
@@ -1097,6 +1172,7 @@
/** Used to track historic provider states for tests. */
private final TestState<ProviderState> mTestProviderState = new TestState<>();
+ private boolean mFailDuringInitialization;
private boolean mInitialized;
private boolean mDestroyed;
@@ -1107,9 +1183,16 @@
super(threadingDomain, providerName);
}
+ public void setFailDuringInitialization(boolean failInitialization) {
+ mFailDuringInitialization = failInitialization;
+ }
+
@Override
void onInitialize() {
mInitialized = true;
+ if (mFailDuringInitialization) {
+ throw new RuntimeException("Simulated initialization failure");
+ }
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
index c6823eb..8139310 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
@@ -16,11 +16,17 @@
package com.android.server.people.data;
+import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
+import static android.app.people.ConversationStatus.ACTIVITY_GAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import android.app.people.ConversationStatus;
import android.content.LocusId;
import android.content.pm.ShortcutInfo;
import android.net.Uri;
@@ -41,6 +47,9 @@
@Test
public void testBuild() {
+ ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
+ ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
+
ConversationInfo conversationInfo = new ConversationInfo.Builder()
.setShortcutId(SHORTCUT_ID)
.setLocusId(LOCUS_ID)
@@ -58,6 +67,8 @@
.setPersonImportant(true)
.setPersonBot(true)
.setContactStarred(true)
+ .addOrUpdateStatus(cs)
+ .addOrUpdateStatus(cs2)
.build();
assertEquals(SHORTCUT_ID, conversationInfo.getShortcutId());
@@ -77,6 +88,8 @@
assertTrue(conversationInfo.isPersonImportant());
assertTrue(conversationInfo.isPersonBot());
assertTrue(conversationInfo.isContactStarred());
+ assertThat(conversationInfo.getStatuses()).contains(cs);
+ assertThat(conversationInfo.getStatuses()).contains(cs2);
}
@Test
@@ -101,10 +114,15 @@
assertFalse(conversationInfo.isPersonImportant());
assertFalse(conversationInfo.isPersonBot());
assertFalse(conversationInfo.isContactStarred());
+ assertThat(conversationInfo.getStatuses()).isNotNull();
+ assertThat(conversationInfo.getStatuses()).isEmpty();
}
@Test
public void testBuildFromAnotherConversationInfo() {
+ ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
+ ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
+
ConversationInfo source = new ConversationInfo.Builder()
.setShortcutId(SHORTCUT_ID)
.setLocusId(LOCUS_ID)
@@ -120,6 +138,8 @@
.setPersonImportant(true)
.setPersonBot(true)
.setContactStarred(true)
+ .addOrUpdateStatus(cs)
+ .addOrUpdateStatus(cs2)
.build();
ConversationInfo destination = new ConversationInfo.Builder(source)
@@ -141,5 +161,7 @@
assertTrue(destination.isPersonImportant());
assertTrue(destination.isPersonBot());
assertFalse(destination.isContactStarred());
+ assertThat(destination.getStatuses()).contains(cs);
+ assertThat(destination.getStatuses()).contains(cs2);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 2471210..be8a99c 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -16,6 +16,8 @@
package com.android.server.people.data;
+import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
+import static android.app.people.ConversationStatus.ACTIVITY_GAME;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
@@ -24,6 +26,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.fail;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -50,6 +54,7 @@
import android.app.Person;
import android.app.job.JobScheduler;
import android.app.people.ConversationChannel;
+import android.app.people.ConversationStatus;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
@@ -937,6 +942,83 @@
}
@Test
+ public void testAddOrUpdateStatus_noCachedShortcut() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
+
+ try {
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
+ fail("Updated a conversation info that didn't previously exist");
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
+
+ @Test
+ public void testAddOrUpdateStatus() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
+
+ assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
+ .contains(cs);
+
+ ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
+
+ assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
+ .contains(cs);
+ assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
+ .contains(cs2);
+ }
+
+ @Test
+ public void testClearStatus() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
+ ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
+
+ mDataManager.clearStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2.getId());
+
+ assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
+ .contains(cs);
+ assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
+ .doesNotContain(cs2);
+ }
+
+ @Test
+ public void testClearStatuses() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
+ ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
+
+ mDataManager.clearStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID);
+
+ assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
+ .isEmpty();
+ }
+
+ @Test
public void testNonCachedShortcutNotInRecentList() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 08d4caa..5f65440 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -32,8 +32,13 @@
import com.android.server.SystemService;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
+import com.android.server.powerstats.nano.PowerEntityInfoProto;
import com.android.server.powerstats.nano.PowerStatsServiceMeterProto;
import com.android.server.powerstats.nano.PowerStatsServiceModelProto;
+import com.android.server.powerstats.nano.PowerStatsServiceResidencyProto;
+import com.android.server.powerstats.nano.StateInfoProto;
+import com.android.server.powerstats.nano.StateResidencyProto;
+import com.android.server.powerstats.nano.StateResidencyResultProto;
import org.junit.Before;
import org.junit.Test;
@@ -58,6 +63,7 @@
private static final String DATA_STORAGE_SUBDIR = "powerstatstest";
private static final String METER_FILENAME = "metertest";
private static final String MODEL_FILENAME = "modeltest";
+ private static final String RESIDENCY_FILENAME = "residencytest";
private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto";
private static final String CHANNEL_NAME = "channelname";
private static final String POWER_ENTITY_NAME = "powerentityinfo";
@@ -72,6 +78,7 @@
private PowerStatsService mService;
private File mDataStorageDir;
private TimerTrigger mTimerTrigger;
+ private BatteryTrigger mBatteryTrigger;
private PowerStatsLogger mPowerStatsLogger;
private final PowerStatsService.Injector mInjector = new PowerStatsService.Injector() {
@@ -99,22 +106,29 @@
}
@Override
+ String createResidencyFilename() {
+ return RESIDENCY_FILENAME;
+ }
+
+ @Override
IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
return new TestPowerStatsHALWrapper();
}
@Override
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String meterFilename, String modelFilename,
+ String meterFilename, String modelFilename, String residencyFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, meterFilename,
- modelFilename, powerStatsHALWrapper);
+ modelFilename, residencyFilename, powerStatsHALWrapper);
return mPowerStatsLogger;
}
@Override
BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
- return new BatteryTrigger(context, powerStatsLogger, false /* trigger enabled */);
+ mBatteryTrigger = new BatteryTrigger(context, powerStatsLogger,
+ false /* trigger enabled */);
+ return mBatteryTrigger;
}
@Override
@@ -137,7 +151,7 @@
for (int j = 0; j < powerEntityInfoList[i].states.length; j++) {
powerEntityInfoList[i].states[j] = new StateInfo();
powerEntityInfoList[i].states[j].stateId = j;
- powerEntityInfoList[i].states[j].stateName = new String(STATE_NAME + i);
+ powerEntityInfoList[i].states[j].stateName = new String(STATE_NAME + j);
}
}
return powerEntityInfoList;
@@ -154,6 +168,7 @@
new StateResidency[STATE_RESIDENCY_COUNT];
for (int j = 0; j < stateResidencyResultList[i].stateResidencyData.length; j++) {
stateResidencyResultList[i].stateResidencyData[j] = new StateResidency();
+ stateResidencyResultList[i].stateResidencyData[j].stateId = j;
stateResidencyResultList[i].stateResidencyData[j].totalTimeInStateMs = j;
stateResidencyResultList[i].stateResidencyData[j].totalStateEntryCount = j;
stateResidencyResultList[i].stateResidencyData[j].lastEntryTimestampMs = j;
@@ -225,7 +240,7 @@
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
// Write data to on-device storage.
- mTimerTrigger.logPowerStatsData();
+ mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER);
// The above call puts a message on a handler. Wait for
// it to be processed.
@@ -266,7 +281,7 @@
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
// Write data to on-device storage.
- mTimerTrigger.logPowerStatsData();
+ mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER);
// The above call puts a message on a handler. Wait for
// it to be processed.
@@ -301,6 +316,61 @@
}
@Test
+ public void testWrittenResidencyDataMatchesReadIncidentReportData()
+ throws InterruptedException, IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Write data to on-device storage.
+ mBatteryTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP);
+
+ // The above call puts a message on a handler. Wait for
+ // it to be processed.
+ Thread.sleep(100);
+
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream fos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeResidencyDataToFile(fos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceResidencyProto object.
+ PowerStatsServiceResidencyProto pssProto =
+ PowerStatsServiceResidencyProto.parseFrom(fileContent);
+
+ // Validate the powerEntityInfo array matches what was written to on-device storage.
+ assertTrue(pssProto.powerEntityInfo.length == POWER_ENTITY_COUNT);
+ for (int i = 0; i < pssProto.powerEntityInfo.length; i++) {
+ PowerEntityInfoProto powerEntityInfo = pssProto.powerEntityInfo[i];
+ assertTrue(powerEntityInfo.powerEntityId == i);
+ assertTrue(powerEntityInfo.powerEntityName.equals(POWER_ENTITY_NAME + i));
+ for (int j = 0; j < powerEntityInfo.states.length; j++) {
+ StateInfoProto stateInfo = powerEntityInfo.states[j];
+ assertTrue(stateInfo.stateId == j);
+ assertTrue(stateInfo.stateName.equals(STATE_NAME + j));
+ }
+ }
+
+ // Validate the stateResidencyResult array matches what was written to on-device storage.
+ assertTrue(pssProto.stateResidencyResult.length == POWER_ENTITY_COUNT);
+ for (int i = 0; i < pssProto.stateResidencyResult.length; i++) {
+ StateResidencyResultProto stateResidencyResult = pssProto.stateResidencyResult[i];
+ assertTrue(stateResidencyResult.powerEntityId == i);
+ assertTrue(stateResidencyResult.stateResidencyData.length == STATE_RESIDENCY_COUNT);
+ for (int j = 0; j < stateResidencyResult.stateResidencyData.length; j++) {
+ StateResidencyProto stateResidency = stateResidencyResult.stateResidencyData[j];
+ assertTrue(stateResidency.stateId == j);
+ assertTrue(stateResidency.totalTimeInStateMs == j);
+ assertTrue(stateResidency.totalStateEntryCount == j);
+ assertTrue(stateResidency.lastEntryTimestampMs == j);
+ }
+ }
+ }
+
+ @Test
public void testCorruptOnDeviceMeterStorage() throws IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -384,6 +454,55 @@
}
@Test
+ public void testCorruptOnDeviceResidencyStorage() throws IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Generate random array of bytes to emulate corrupt data.
+ Random rd = new Random();
+ byte[] bytes = new byte[100];
+ rd.nextBytes(bytes);
+
+ // Store corrupt data in on-device storage. Add fake timestamp to filename
+ // to match format expected by FileRotator.
+ File onDeviceStorageFile = new File(mDataStorageDir, RESIDENCY_FILENAME + ".1234-2234");
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeResidencyDataToFile(incidentReportFos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceResidencyProto object.
+ PowerStatsServiceResidencyProto pssProto =
+ PowerStatsServiceResidencyProto.parseFrom(fileContent);
+
+ // Valid powerEntityInfo data is written to the incident report in the call to
+ // mPowerStatsLogger.writeResidencyDataToFile().
+ assertTrue(pssProto.powerEntityInfo.length == POWER_ENTITY_COUNT);
+ for (int i = 0; i < pssProto.powerEntityInfo.length; i++) {
+ PowerEntityInfoProto powerEntityInfo = pssProto.powerEntityInfo[i];
+ assertTrue(powerEntityInfo.powerEntityId == i);
+ assertTrue(powerEntityInfo.powerEntityName.equals(POWER_ENTITY_NAME + i));
+ for (int j = 0; j < powerEntityInfo.states.length; j++) {
+ StateInfoProto stateInfo = powerEntityInfo.states[j];
+ assertTrue(stateInfo.stateId == j);
+ assertTrue(stateInfo.stateName.equals(STATE_NAME + j));
+ }
+ }
+
+ // No stateResidencyResults should be written to the incident report since it
+ // is all corrupt (random bytes generated above).
+ assertTrue(pssProto.stateResidencyResult.length == 0);
+ }
+
+ @Test
public void testNotEnoughBytesAfterMeterLengthField() throws IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -467,4 +586,54 @@
// input buffer had only length and no data.
assertTrue(pssProto.energyConsumerResult.length == 0);
}
+
+ @Test
+ public void testNotEnoughBytesAfterResidencyLengthField() throws IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Create corrupt data.
+ // Length field is correct, but there is no data following the length.
+ ByteArrayOutputStream data = new ByteArrayOutputStream();
+ data.write(ByteBuffer.allocate(4).putInt(50).array());
+ byte[] test = data.toByteArray();
+
+ // Store corrupt data in on-device storage. Add fake timestamp to filename
+ // to match format expected by FileRotator.
+ File onDeviceStorageFile = new File(mDataStorageDir, RESIDENCY_FILENAME + ".1234-2234");
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(data.toByteArray());
+ onDeviceStorageFos.close();
+
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeResidencyDataToFile(incidentReportFos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceResidencyProto object.
+ PowerStatsServiceResidencyProto pssProto =
+ PowerStatsServiceResidencyProto.parseFrom(fileContent);
+
+ // Valid powerEntityInfo data is written to the incident report in the call to
+ // mPowerStatsLogger.writeResidencyDataToFile().
+ assertTrue(pssProto.powerEntityInfo.length == POWER_ENTITY_COUNT);
+ for (int i = 0; i < pssProto.powerEntityInfo.length; i++) {
+ PowerEntityInfoProto powerEntityInfo = pssProto.powerEntityInfo[i];
+ assertTrue(powerEntityInfo.powerEntityId == i);
+ assertTrue(powerEntityInfo.powerEntityName.equals(POWER_ENTITY_NAME + i));
+ for (int j = 0; j < powerEntityInfo.states.length; j++) {
+ StateInfoProto stateInfo = powerEntityInfo.states[j];
+ assertTrue(stateInfo.stateId == j);
+ assertTrue(stateInfo.stateName.equals(STATE_NAME + j));
+ }
+ }
+
+ // No stateResidencyResults should be written to the incident report since the
+ // input buffer had only length and no data.
+ assertTrue(pssProto.stateResidencyResult.length == 0);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 3306e31..2f4c8e2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -15,7 +15,6 @@
*/
package com.android.server.wm;
-
import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -31,6 +30,7 @@
import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
+import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
@@ -103,6 +103,7 @@
mImeContainer = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "ImeContainer");
mDisplayContent = mock(DisplayContent.class);
doReturn(true).when(mDisplayContent).isTrusted();
+ mDisplayContent.isDefaultDisplay = true;
mDefaultTaskDisplayArea = new TaskDisplayArea(mDisplayContent, mWms, "Tasks",
FEATURE_DEFAULT_TASK_CONTAINER);
mTaskDisplayAreaList = new ArrayList<>();
@@ -183,13 +184,34 @@
final DisplayAreaPolicyBuilder.Result defaultPolicy =
(DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
mRoot, mImeContainer);
- final List<Feature> features = defaultPolicy.getFeatures();
- boolean hasOneHandedFeature = false;
- for (int i = 0; i < features.size(); i++) {
- hasOneHandedFeature |= features.get(i).getId() == FEATURE_ONE_HANDED;
- }
+ if (mDisplayContent.isDefaultDisplay) {
+ final List<Feature> features = defaultPolicy.getFeatures();
+ boolean hasOneHandedFeature = false;
+ for (Feature feature : features) {
+ hasOneHandedFeature |= feature.getId() == FEATURE_ONE_HANDED;
+ }
- assertThat(hasOneHandedFeature).isTrue();
+ assertThat(hasOneHandedFeature).isTrue();
+ }
+ }
+
+ @Test
+ public void testBuilder_defaultPolicy_hasOneHandedBackgroundFeature() {
+ final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
+ resourcesWithProvider(""));
+ final DisplayAreaPolicyBuilder.Result defaultPolicy =
+ (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
+ mRoot, mImeContainer);
+ if (mDisplayContent.isDefaultDisplay) {
+ final List<Feature> features = defaultPolicy.getFeatures();
+ boolean hasOneHandedBackgroundFeature = false;
+ for (Feature feature : features) {
+ hasOneHandedBackgroundFeature |=
+ feature.getId() == FEATURE_ONE_HANDED_BACKGROUND_PANEL;
+ }
+
+ assertThat(hasOneHandedBackgroundFeature).isTrue();
+ }
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 4b42d56..673b00f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -61,7 +61,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
-import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -1145,8 +1144,6 @@
() -> mAtm.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
assertSecurityException(expectCallable,
() -> mAtm.moveTaskToRootTask(0, INVALID_STACK_ID, true));
- assertSecurityException(expectCallable,
- () -> mAtm.moveTopActivityToPinnedRootTask(INVALID_STACK_ID, new Rect()));
assertSecurityException(expectCallable, () -> mAtm.getAllRootTaskInfos());
assertSecurityException(expectCallable,
() -> mAtm.getRootTaskInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index d585b23..afa35fe 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -175,7 +175,10 @@
// Delay for debouncing USB disconnects.
// We often get rapid connect/disconnect events when enabling USB functions,
// which need debouncing.
- private static final int UPDATE_DELAY = 1000;
+ private static final int DEVICE_STATE_UPDATE_DELAY = 3000;
+
+ // Delay for debouncing USB disconnects on Type-C ports in host mode
+ private static final int HOST_STATE_UPDATE_DELAY = 1000;
// Timeout for entering USB request mode.
// Request is cancelled if host does not configure device within 10 seconds.
@@ -636,7 +639,7 @@
msg.arg1 = connected;
msg.arg2 = configured;
// debounce disconnects to avoid problems bringing up USB tethering
- sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
+ sendMessageDelayed(msg, (connected == 0) ? DEVICE_STATE_UPDATE_DELAY : 0);
}
public void updateHostState(UsbPort port, UsbPortStatus status) {
@@ -651,7 +654,7 @@
removeMessages(MSG_UPDATE_PORT_STATE);
Message msg = obtainMessage(MSG_UPDATE_PORT_STATE, args);
// debounce rapid transitions of connect/disconnect on type-c ports
- sendMessageDelayed(msg, UPDATE_DELAY);
+ sendMessageDelayed(msg, HOST_STATE_UPDATE_DELAY);
}
private void setAdbEnabled(boolean enable) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 56345c0..7e019cc 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -743,6 +743,14 @@
public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
/**
+ * Flag specifying whether Cross SIM over IMS should be available for carrier.
+ * When {@code false} the carrier does not support cross SIM calling.
+ * When {@code true} the carrier does support cross sim calling, where available
+ */
+ public static final String KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL =
+ "carrier_cross_sim_ims_available_bool";
+
+ /**
* Specifies a map from dialstrings to replacements for roaming network service numbers which
* cannot be replaced on the carrier side.
* <p>
@@ -1497,6 +1505,31 @@
"wfc_carrier_name_override_by_pnn_bool";
/**
+ * Value for {#CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING} cnfig.
+ * specifies SPN format of displaying carrier name only.
+ *
+ */
+ public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY = 0;
+
+ /**
+ * Value for {#CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING} cnfig.
+ * specifies SPN format of displaying carrier name along with "Cross-SIM calling".
+ */
+ public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING = 1;
+
+ /**
+ * Indexes of SPN format strings in crossSimSpnFormats.
+ *
+ * <p>Available options are:
+ * <ul>
+ * <li> {#CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY}: %s</li>
+ * <li> {#CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING}: %s Cross-SIM Calling</li>
+ * </ul>
+ * %s will be filled with carrier name
+ */
+ public static final String KEY_CROSS_SIM_SPN_FORMAT_INT = "cross_sim_spn_format_int";
+
+ /**
* Override the SPN Display Condition 2 integer bits (lsb). B2, B1 is the last two bits of the
* spn display condition coding.
*
@@ -2373,7 +2406,8 @@
"show_blocking_pay_phone_option_bool";
/**
- * Flag specifying whether the carrier will use the WFC home network mode in roaming network.
+ * Flag specifying whether the carrier will use the
+ * WFC home network mode in roaming network.
* {@code false} - roaming preference can be selected separately from the home preference.
* {@code true} - roaming preference is the same as home preference and
* {@link #KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT} is used as the default value.
@@ -4621,6 +4655,7 @@
sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL, false);
@@ -4794,6 +4829,7 @@
sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
sDefaults.putBoolean(KEY_WFC_CARRIER_NAME_OVERRIDE_BY_PNN_BOOL, false);
+ sDefaults.putInt(KEY_CROSS_SIM_SPN_FORMAT_INT, 1);
sDefaults.putInt(KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT, -1);
sDefaults.putStringArray(KEY_SPDI_OVERRIDE_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_PNN_OVERRIDE_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 60389e1..a6e870f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5586,6 +5586,10 @@
*/
@Deprecated
public void listen(PhoneStateListener listener, int events) {
+ if (!listener.isExecutorSet()) {
+ throw new IllegalStateException("PhoneStateListener should be created on a thread "
+ + "with Looper.myLooper() != null");
+ }
boolean notifyNow = getITelephony() != null;
mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
if (mTelephonyRegistryMgr != null) {
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index e217e9a..ed1f3dd 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.Annotation.DataFailureCause;
@@ -276,9 +277,11 @@
}
/**
- * @return The network suggested data retry duration in milliseconds. {@code Long.MAX_VALUE}
- * indicates data retry should not occur. {@link #RETRY_DURATION_UNDEFINED} indicates network
- * did not suggest any retry duration.
+ * @return The network suggested data retry duration in milliseconds as specified in
+ * 3GPP TS 24.302 section 8.2.9.1. The APN associated to this data call will be throttled for
+ * the specified duration unless {@link DataServiceCallback#onApnUnthrottled} is called.
+ * {@code Long.MAX_VALUE} indicates data retry should not occur.
+ * {@link #RETRY_DURATION_UNDEFINED} indicates network did not suggest any retry duration.
*/
public long getRetryDurationMillis() {
return mSuggestedRetryTime;
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 52bf15f..f56c19b 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -250,7 +250,11 @@
}
/**
- * Indicates that the specified APN is no longer throttled.
+ * The APN is throttled for the duration specified in
+ * {@link DataCallResponse#getRetryDurationMillis}. Calling this method unthrottles that
+ * APN.
+ * <p/>
+ * see: {@link DataCallResponse#getRetryDurationMillis}
*
* @param apn Access Point Name defined by the carrier.
*/
diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
index 77944de..c1ef350 100644
--- a/tests/vcn/java/android/net/vcn/VcnConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
@@ -18,12 +18,17 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import android.annotation.NonNull;
+import android.content.Context;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -33,12 +38,15 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnConfigTest {
+ private static final String TEST_PACKAGE_NAME = VcnConfigTest.class.getPackage().getName();
private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS =
Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig());
+ private final Context mContext = mock(Context.class);
+
// Public visibility for VcnManagementServiceTest
- public static VcnConfig buildTestConfig() {
- VcnConfig.Builder builder = new VcnConfig.Builder();
+ public static VcnConfig buildTestConfig(@NonNull Context context) {
+ VcnConfig.Builder builder = new VcnConfig.Builder(context);
for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) {
builder.addGatewayConnectionConfig(gatewayConnectionConfig);
@@ -47,10 +55,24 @@
return builder.build();
}
+ @Before
+ public void setUp() throws Exception {
+ doReturn(TEST_PACKAGE_NAME).when(mContext).getOpPackageName();
+ }
+
+ @Test
+ public void testBuilderConstructorRequiresContext() {
+ try {
+ new VcnConfig.Builder(null);
+ fail("Expected exception due to null context");
+ } catch (NullPointerException e) {
+ }
+ }
+
@Test
public void testBuilderRequiresGatewayConnectionConfig() {
try {
- new VcnConfig.Builder().build();
+ new VcnConfig.Builder(mContext).build();
fail("Expected exception due to no VcnGatewayConnectionConfigs provided");
} catch (IllegalArgumentException e) {
}
@@ -58,21 +80,22 @@
@Test
public void testBuilderAndGetters() {
- final VcnConfig config = buildTestConfig();
+ final VcnConfig config = buildTestConfig(mContext);
+ assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName());
assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs());
}
@Test
public void testPersistableBundle() {
- final VcnConfig config = buildTestConfig();
+ final VcnConfig config = buildTestConfig(mContext);
assertEquals(config, new VcnConfig(config.toPersistableBundle()));
}
@Test
public void testParceling() {
- final VcnConfig config = buildTestConfig();
+ final VcnConfig config = buildTestConfig(mContext);
Parcel parcel = Parcel.obtain();
config.writeToParcel(parcel, 0);
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 1cc9532..696110f 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -16,16 +16,23 @@
package com.android.server;
+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.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.vcn.VcnConfig;
@@ -42,29 +49,47 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.TelephonySubscriptionTracker;
+import com.android.server.vcn.Vcn;
+import com.android.server.vcn.VcnContext;
+import com.android.server.vcn.VcnNetworkProvider;
import com.android.server.vcn.util.PersistableBundleUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.io.FileNotFoundException;
import java.util.Collections;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
/** Tests for {@link VcnManagementService}. */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnManagementServiceTest {
+ private static final String TEST_PACKAGE_NAME =
+ VcnManagementServiceTest.class.getPackage().getName();
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 VcnConfig TEST_VCN_CONFIG;
+ private static final int TEST_UID = Process.FIRST_APPLICATION_UID;
+
+ static {
+ final Context mockConfigContext = mock(Context.class);
+ doReturn(TEST_PACKAGE_NAME).when(mockConfigContext).getOpPackageName();
+
+ TEST_VCN_CONFIG = VcnConfigTest.buildTestConfig(mockConfigContext);
+ }
+
private static final Map<ParcelUuid, VcnConfig> TEST_VCN_CONFIG_MAP =
Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG));
+ private static final int TEST_SUBSCRIPTION_ID = 1;
private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO =
new SubscriptionInfo(
- 1 /* id */,
+ TEST_SUBSCRIPTION_ID /* id */,
"" /* iccId */,
0 /* simSlotIndex */,
"Carrier" /* displayName */,
@@ -92,22 +117,48 @@
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 AppOpsManager mAppOpsMgr = mock(AppOpsManager.class);
+ private final VcnContext mVcnContext = mock(VcnContext.class);
private final PersistableBundleUtils.LockingReadWriteHelper mConfigReadWriteHelper =
mock(PersistableBundleUtils.LockingReadWriteHelper.class);
+ private final TelephonySubscriptionTracker mSubscriptionTracker =
+ mock(TelephonySubscriptionTracker.class);
+
+ private final VcnManagementService mVcnMgmtSvc;
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);
+ setupSystemService(mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class);
+
+ doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName();
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
- doReturn(Process.FIRST_APPLICATION_UID).when(mMockDeps).getBinderCallingUid();
+ doReturn(TEST_UID).when(mMockDeps).getBinderCallingUid();
+ doReturn(mVcnContext)
+ .when(mMockDeps)
+ .newVcnContext(
+ eq(mMockContext),
+ eq(mTestLooper.getLooper()),
+ any(VcnNetworkProvider.class));
+ doReturn(mSubscriptionTracker)
+ .when(mMockDeps)
+ .newTelephonySubscriptionTracker(
+ eq(mMockContext),
+ eq(mTestLooper.getLooper()),
+ any(TelephonySubscriptionTrackerCallback.class));
doReturn(mConfigReadWriteHelper)
.when(mMockDeps)
.newPersistableBundleLockingReadWriteHelper(any());
+ // Setup VCN instance generation
+ doAnswer((invocation) -> {
+ // Mock-within a doAnswer is safe, because it doesn't actually run nested.
+ return mock(Vcn.class);
+ }).when(mMockDeps).newVcn(any(), any(), any());
+
final PersistableBundle bundle =
PersistableBundleUtils.fromMap(
TEST_VCN_CONFIG_MAP,
@@ -117,6 +168,9 @@
setupMockedCarrierPrivilege(true);
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
+
+ // Make sure the profiles are loaded.
+ mTestLooper.dispatchAll();
}
private void setupSystemService(Object service, String name, Class<?> serviceClass) {
@@ -137,8 +191,8 @@
public void testSystemReady() throws Exception {
mVcnMgmtSvc.systemReady();
- verify(mConnMgr)
- .registerNetworkProvider(any(VcnManagementService.VcnNetworkProvider.class));
+ verify(mConnMgr).registerNetworkProvider(any(VcnNetworkProvider.class));
+ verify(mSubscriptionTracker).register();
}
@Test
@@ -171,12 +225,110 @@
verify(mConfigReadWriteHelper).readFromDisk();
}
+ private void triggerSubscriptionTrackerCallback(Set<ParcelUuid> activeSubscriptionGroups) {
+ final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
+ doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();
+
+ final Set<String> privilegedPackages =
+ (activeSubscriptionGroups == null || activeSubscriptionGroups.isEmpty())
+ ? Collections.emptySet()
+ : Collections.singleton(TEST_PACKAGE_NAME);
+ doReturn(true)
+ .when(snapshot)
+ .packageHasPermissionsForSubscriptionGroup(
+ argThat(val -> activeSubscriptionGroups.contains(val)),
+ eq(TEST_PACKAGE_NAME));
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ cb.onNewSnapshot(snapshot);
+ }
+
+ private TelephonySubscriptionTrackerCallback getTelephonySubscriptionTrackerCallback() {
+ final ArgumentCaptor<TelephonySubscriptionTrackerCallback> captor =
+ ArgumentCaptor.forClass(TelephonySubscriptionTrackerCallback.class);
+ verify(mMockDeps)
+ .newTelephonySubscriptionTracker(
+ eq(mMockContext), eq(mTestLooper.getLooper()), captor.capture());
+ return captor.getValue();
+ }
+
+ private Vcn startAndGetVcnInstance(ParcelUuid uuid) {
+ mVcnMgmtSvc.setVcnConfig(uuid, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ return mVcnMgmtSvc.getAllVcns().get(uuid);
+ }
+
+ @Test
+ public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
+ triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_1));
+ verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG));
+ }
+
+ @Test
+ public void testTelephonyNetworkTrackerCallbackStopsInstances() throws Exception {
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+
+ triggerSubscriptionTrackerCallback(Collections.emptySet());
+
+ // Verify teardown after delay
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(vcn).teardownAsynchronously();
+ }
+
+ @Test
+ public void testTelephonyNetworkTrackerCallbackSimSwitchesDoNotKillVcnInstances()
+ throws Exception {
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate SIM unloaded
+ triggerSubscriptionTrackerCallback(Collections.emptySet());
+
+ // Simulate new SIM loaded right during teardown delay.
+ mTestLooper.moveTimeForward(
+ VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
+ mTestLooper.dispatchAll();
+ triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_2));
+
+ // Verify that even after the full timeout duration, the VCN instance is not torn down
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(vcn, never()).teardownAsynchronously();
+ }
+
+ @Test
+ public void testTelephonyNetworkTrackerCallbackDoesNotKillNewVcnInstances() throws Exception {
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate SIM unloaded
+ triggerSubscriptionTrackerCallback(Collections.emptySet());
+
+ // Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
+ // vcnInstance.
+ mTestLooper.moveTimeForward(
+ VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
+ mTestLooper.dispatchAll();
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
+ final Vcn newInstance = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Verify that new instance was different, and the old one was torn down
+ assertTrue(oldInstance != newInstance);
+ verify(oldInstance).teardownAsynchronously();
+
+ // Verify that even after the full timeout duration, the new VCN instance is not torn down
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(newInstance, never()).teardownAsynchronously();
+ }
+
@Test
public void testSetVcnConfigRequiresNonSystemServer() throws Exception {
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
try {
- mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
fail("Expected IllegalStateException exception for system server");
} catch (IllegalStateException expected) {
}
@@ -184,12 +336,12 @@
@Test
public void testSetVcnConfigRequiresSystemUser() throws Exception {
- doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID))
+ doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, TEST_UID))
.when(mMockDeps)
.getBinderCallingUid();
try {
- mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
fail("Expected security exception for non system user");
} catch (SecurityException expected) {
}
@@ -200,16 +352,25 @@
setupMockedCarrierPrivilege(false);
try {
- mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
fail("Expected security exception for missing carrier privileges");
} catch (SecurityException expected) {
}
}
@Test
+ public void testSetVcnConfigMismatchedPackages() throws Exception {
+ try {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, "IncorrectPackage");
+ fail("Expected exception due to mismatched packages in config and method call");
+ } catch (IllegalArgumentException 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);
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
assertEquals(TEST_VCN_CONFIG, mVcnMgmtSvc.getConfigs().get(TEST_UUID_2));
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
}
@@ -227,7 +388,7 @@
@Test
public void testClearVcnConfigRequiresSystemUser() throws Exception {
- doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID))
+ doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, TEST_UID))
.when(mMockDeps)
.getBinderCallingUid();
@@ -255,4 +416,26 @@
assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
}
+
+ @Test
+ public void testSetVcnConfigClearVcnConfigStartsUpdatesAndTeardsDownVcns() throws Exception {
+ // Use a different UUID to simulate a new VCN config.
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ final Map<ParcelUuid, Vcn> vcnInstances = mVcnMgmtSvc.getAllVcns();
+ final Vcn vcnInstance = vcnInstances.get(TEST_UUID_2);
+ assertEquals(1, vcnInstances.size());
+ assertEquals(TEST_VCN_CONFIG, mVcnMgmtSvc.getConfigs().get(TEST_UUID_2));
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+
+ // Verify Vcn is started
+ verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG));
+
+ // Verify Vcn is updated if it was previously started
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ verify(vcnInstance).updateConfig(TEST_VCN_CONFIG);
+
+ // Verify Vcn is stopped if it was already started
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
+ verify(vcnInstance).teardownAsynchronously();
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 17b8f64..528f240 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -30,6 +30,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -37,6 +38,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+import static java.util.Collections.singletonMap;
+
import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
@@ -49,6 +54,7 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyManager;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -63,6 +69,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -71,12 +78,16 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TelephonySubscriptionTrackerTest {
+ private static final String PACKAGE_NAME =
+ TelephonySubscriptionTrackerTest.class.getPackage().getName();
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<ParcelUuid, Set<String>> TEST_PRIVILEGED_PACKAGES =
+ Collections.singletonMap(TEST_PARCEL_UUID, Collections.singleton(PACKAGE_NAME));
private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP;
static {
@@ -91,6 +102,7 @@
@NonNull private final Handler mHandler;
@NonNull private final TelephonySubscriptionTracker.Dependencies mDeps;
+ @NonNull private final TelephonyManager mTelephonyManager;
@NonNull private final SubscriptionManager mSubscriptionManager;
@NonNull private final CarrierConfigManager mCarrierConfigManager;
@@ -103,9 +115,15 @@
mHandler = new Handler(mTestLooper.getLooper());
mDeps = mock(TelephonySubscriptionTracker.Dependencies.class);
+ mTelephonyManager = mock(TelephonyManager.class);
mSubscriptionManager = mock(SubscriptionManager.class);
mCarrierConfigManager = mock(CarrierConfigManager.class);
+ doReturn(Context.TELEPHONY_SERVICE)
+ .when(mContext)
+ .getSystemServiceName(TelephonyManager.class);
+ doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
+
doReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
.when(mContext)
.getSystemServiceName(SubscriptionManager.class);
@@ -140,6 +158,9 @@
doReturn(Arrays.asList(TEST_SUBINFO_1, TEST_SUBINFO_2))
.when(mSubscriptionManager)
.getAllSubscriptionInfoList();
+
+ doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+ setPrivilegedPackagesForMock(Collections.singletonList(PACKAGE_NAME));
}
private IntentFilter getIntentFilter() {
@@ -167,13 +188,15 @@
return intent;
}
- private TelephonySubscriptionSnapshot buildExpectedSnapshot(Set<ParcelUuid> activeSubGroups) {
- return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, activeSubGroups);
+ private TelephonySubscriptionSnapshot buildExpectedSnapshot(
+ Map<ParcelUuid, Set<String>> privilegedPackages) {
+ return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, privilegedPackages);
}
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
- Map<Integer, ParcelUuid> subIdToGroupMap, Set<ParcelUuid> activeSubGroups) {
- return new TelephonySubscriptionSnapshot(subIdToGroupMap, activeSubGroups);
+ Map<Integer, ParcelUuid> subIdToGroupMap,
+ Map<ParcelUuid, Set<String>> privilegedPackages) {
+ return new TelephonySubscriptionSnapshot(subIdToGroupMap, privilegedPackages);
}
private void verifyNoActiveSubscriptions() {
@@ -186,6 +209,10 @@
Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1));
}
+ private void setPrivilegedPackagesForMock(@NonNull List<String> privilegedPackages) {
+ doReturn(privilegedPackages).when(mTelephonyManager).getPackagesWithCarrierPrivileges();
+ }
+
@Test
public void testRegister() throws Exception {
verify(mContext)
@@ -223,15 +250,30 @@
}
@Test
- public void testOnSubscriptionsChangedFired_WithReadySubIds() throws Exception {
+ public void testOnSubscriptionsChangedFired_WithReadySubidsNoPrivilegedPackages()
+ throws Exception {
+ setupReadySubIds();
+ setPrivilegedPackagesForMock(Collections.emptyList());
+
+ final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
+ listener.onSubscriptionsChanged();
+ mTestLooper.dispatchAll();
+
+ final Map<ParcelUuid, Set<String>> privilegedPackages =
+ Collections.singletonMap(TEST_PARCEL_UUID, new ArraySet<>());
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(privilegedPackages)));
+ }
+
+ @Test
+ public void testOnSubscriptionsChangedFired_WithReadySubidsAndPrivilegedPackages()
+ 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)));
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
}
@Test
@@ -239,8 +281,7 @@
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
mTestLooper.dispatchAll();
- final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
- verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
}
@Test
@@ -253,8 +294,7 @@
mTestLooper.dispatchAll();
// Expect an empty snapshot
- verify(mCallback).onNewSnapshot(
- eq(buildExpectedSnapshot(Collections.emptyMap(), Collections.emptySet())));
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap(), emptyMap())));
}
@Test
@@ -281,41 +321,57 @@
@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)));
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
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())));
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap(), emptyMap())));
}
@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)));
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
assertNotNull(
mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
mTestLooper.dispatchAll();
- verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(Collections.emptySet())));
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap())));
assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
}
@Test
+ public void testChangingPrivilegedPackagesAfterValidTriggersCallbacks() throws Exception {
+ setupReadySubIds();
+
+ // Setup initial "valid" state
+ final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
+ listener.onSubscriptionsChanged();
+ mTestLooper.dispatchAll();
+
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
+
+ // Simulate a loss of carrier privileges
+ setPrivilegedPackagesForMock(Collections.emptyList());
+ listener.onSubscriptionsChanged();
+ mTestLooper.dispatchAll();
+
+ verify(mCallback)
+ .onNewSnapshot(
+ eq(buildExpectedSnapshot(singletonMap(TEST_PARCEL_UUID, emptySet()))));
+ }
+
+ @Test
public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
final TelephonySubscriptionSnapshot snapshot =
- new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, Collections.emptySet());
+ new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap());
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
@@ -324,7 +380,7 @@
@Test
public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
final TelephonySubscriptionSnapshot snapshot =
- new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, Collections.emptySet());
+ new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap());
assertEquals(
new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
diff --git a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
new file mode 100644
index 0000000..c2c6200
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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 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.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.os.test.TestLooper;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Tests for TelephonySubscriptionTracker */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnNetworkProviderTest {
+ private static final int TEST_SCORE_UNSATISFIED = 0;
+ private static final int TEST_SCORE_HIGH = 100;
+ private static final int TEST_PROVIDER_ID = 1;
+ private static final int TEST_LEGACY_TYPE = ConnectivityManager.TYPE_MOBILE;
+ private static final NetworkRequest.Type TEST_REQUEST_TYPE = NetworkRequest.Type.REQUEST;
+
+ @NonNull private final Context mContext;
+ @NonNull private final TestLooper mTestLooper;
+
+ @NonNull private VcnNetworkProvider mVcnNetworkProvider;
+ @NonNull private NetworkRequestListener mListener;
+
+ public VcnNetworkProviderTest() {
+ mContext = mock(Context.class);
+ mTestLooper = new TestLooper();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mVcnNetworkProvider = new VcnNetworkProvider(mContext, mTestLooper.getLooper());
+ mListener = mock(NetworkRequestListener.class);
+ }
+
+ @Test
+ public void testRequestsPassedToRegisteredListeners() throws Exception {
+ mVcnNetworkProvider.registerListener(mListener);
+
+ final NetworkRequest request = mock(NetworkRequest.class);
+ mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID);
+ verify(mListener).onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID);
+ }
+
+ @Test
+ public void testRequestsPassedToRegisteredListeners_satisfiedByHighScoringProvider()
+ throws Exception {
+ mVcnNetworkProvider.registerListener(mListener);
+
+ final NetworkRequest request = mock(NetworkRequest.class);
+ mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_HIGH, TEST_PROVIDER_ID);
+ verify(mListener).onNetworkRequested(request, TEST_SCORE_HIGH, TEST_PROVIDER_ID);
+ }
+
+ @Test
+ public void testUnregisterListener() throws Exception {
+ mVcnNetworkProvider.registerListener(mListener);
+ mVcnNetworkProvider.unregisterListener(mListener);
+
+ final NetworkRequest request = mock(NetworkRequest.class);
+ mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID);
+ verifyNoMoreInteractions(mListener);
+ }
+
+ @Test
+ public void testCachedRequestsPassedOnRegister() throws Exception {
+ final List<NetworkRequest> requests = new ArrayList<>();
+
+ for (int i = 0; i < 10; i++) {
+ final NetworkRequest request =
+ new NetworkRequest(
+ new NetworkCapabilities(),
+ TEST_LEGACY_TYPE,
+ i /* requestId */,
+ TEST_REQUEST_TYPE);
+
+ requests.add(request);
+ mVcnNetworkProvider.onNetworkRequested(request, i, i + 1);
+ }
+
+ mVcnNetworkProvider.registerListener(mListener);
+ for (int i = 0; i < requests.size(); i++) {
+ final NetworkRequest request = requests.get(i);
+ verify(mListener).onNetworkRequested(request, i, i + 1);
+ }
+ verifyNoMoreInteractions(mListener);
+ }
+}
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
index 76edd63..97a2a40 100644
--- a/tools/powerstats/PowerStatsServiceProtoParser.java
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -90,6 +90,38 @@
}
}
+ private static void printPowerEntityInfo(PowerStatsServiceResidencyProto proto) {
+ String csvHeader = new String();
+ for (int i = 0; i < proto.getPowerEntityInfoCount(); i++) {
+ PowerEntityInfoProto powerEntityInfo = proto.getPowerEntityInfo(i);
+ csvHeader += powerEntityInfo.getPowerEntityId() + ","
+ + powerEntityInfo.getPowerEntityName() + ",";
+ for (int j = 0; j < powerEntityInfo.getStatesCount(); j++) {
+ StateInfoProto stateInfo = powerEntityInfo.getStates(j);
+ csvHeader += stateInfo.getStateId() + "," + stateInfo.getStateName() + ",";
+ }
+ }
+ System.out.println(csvHeader);
+ }
+
+ private static void printStateResidencyResult(PowerStatsServiceResidencyProto proto) {
+ for (int i = 0; i < proto.getStateResidencyResultCount(); i++) {
+ String csvRow = new String();
+
+ StateResidencyResultProto stateResidencyResult = proto.getStateResidencyResult(i);
+ csvRow += stateResidencyResult.getPowerEntityId() + ",";
+
+ for (int j = 0; j < stateResidencyResult.getStateResidencyDataCount(); j++) {
+ StateResidencyProto stateResidency = stateResidencyResult.getStateResidencyData(j);
+ csvRow += stateResidency.getStateId() + ","
+ + stateResidency.getTotalTimeInStateMs() + ","
+ + stateResidency.getTotalStateEntryCount() + ","
+ + stateResidency.getLastEntryTimestampMs() + ",";
+ }
+ System.out.println(csvRow);
+ }
+ }
+
private static void generateCsvFile(String pathToIncidentReport) {
try {
// Print power meter data.
@@ -115,6 +147,21 @@
} else {
System.out.println("Model incident report not found. Exiting.");
}
+
+ // Print state residency data.
+ IncidentReportResidencyProto irResidencyProto =
+ IncidentReportResidencyProto.parseFrom(
+ new FileInputStream(pathToIncidentReport));
+
+ if (irResidencyProto.hasIncidentReport()) {
+ PowerStatsServiceResidencyProto pssResidencyProto =
+ irResidencyProto.getIncidentReport();
+ printPowerEntityInfo(pssResidencyProto);
+ printStateResidencyResult(pssResidencyProto);
+ } else {
+ System.out.println("Residency incident report not found. Exiting.");
+ }
+
} catch (IOException e) {
System.out.println("Unable to open incident report file: " + pathToIncidentReport);
System.out.println(e);