Merge "Update getGnssCapabilities() in LMS to include sub-HAL capabilities"
diff --git a/Android.bp b/Android.bp
index 05c7dbc..0acc3d6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -384,6 +384,7 @@
"core/java/android/view/IRecentsAnimationRunner.aidl",
"core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
"core/java/android/view/IRotationWatcher.aidl",
+ "core/java/android/view/ISystemGestureExclusionListener.aidl",
"core/java/android/view/IWallpaperVisibilityListener.aidl",
"core/java/android/view/IWindow.aidl",
"core/java/android/view/IWindowFocusObserver.aidl",
@@ -753,10 +754,6 @@
"android.hardware.radio-V1.2-java",
"android.hardware.radio-V1.3-java",
"android.hardware.radio-V1.4-java",
- "android.hardware.radio.config-V1.0-java",
- "android.hardware.radio.config-V1.1-java",
- "android.hardware.radio.config-V1.2-java",
- "android.hardware.radio.deprecated-V1.0-java",
"android.hardware.thermal-V1.0-java-constants",
"android.hardware.thermal-V1.0-java",
"android.hardware.thermal-V1.1-java",
diff --git a/api/current.txt b/api/current.txt
index 7228ef4..34c2c29 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11051,6 +11051,7 @@
method public void dump(android.util.Printer, String);
method public static CharSequence getCategoryTitle(android.content.Context, int);
method public boolean isProfileableByShell();
+ method public boolean isResourceOverlay();
method public boolean isVirtualPreload();
method public CharSequence loadDescription(android.content.pm.PackageManager);
field public static final int CATEGORY_AUDIO = 1; // 0x1
@@ -11676,7 +11677,6 @@
field public static final String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
field public static final String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
field public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
- field public static final String FEATURE_FOLDABLE = "android.hardware.type.foldable";
field public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
field public static final String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
@@ -25083,6 +25083,7 @@
field public static final String KEY_LANGUAGE = "language";
field public static final String KEY_LATENCY = "latency";
field public static final String KEY_LEVEL = "level";
+ field public static final String KEY_MAX_BFRAMES = "max-bframes";
field public static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
field public static final String KEY_MAX_HEIGHT = "max-height";
field public static final String KEY_MAX_INPUT_SIZE = "max-input-size";
@@ -45299,7 +45300,7 @@
field public static final int PHONE_TYPE_GSM = 1; // 0x1
field public static final int PHONE_TYPE_NONE = 0; // 0x0
field public static final int PHONE_TYPE_SIP = 3; // 0x3
- field public static final int SET_OPPORTUNISTIC_SUB_INVALID_PARAMETER = 2; // 0x2
+ field public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2; // 0x2
field public static final int SET_OPPORTUNISTIC_SUB_SUCCESS = 0; // 0x0
field public static final int SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED = 1; // 0x1
field public static final int SIM_STATE_ABSENT = 1; // 0x1
@@ -50443,6 +50444,7 @@
method protected float getLeftFadingEdgeStrength();
method protected int getLeftPaddingOffset();
method public final boolean getLocalVisibleRect(android.graphics.Rect);
+ method public void getLocationInSurface(@NonNull @Size(2) int[]);
method public void getLocationInWindow(@Size(2) int[]);
method public void getLocationOnScreen(@Size(2) int[]);
method public android.graphics.Matrix getMatrix();
@@ -53033,7 +53035,7 @@
method public int describeContents();
method @NonNull public static android.view.contentcapture.ContentCaptureContext forLocusId(@NonNull String);
method @Nullable public android.os.Bundle getExtras();
- method @NonNull public android.content.LocusId getLocusId();
+ method @Nullable public android.content.LocusId getLocusId();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureContext> CREATOR;
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 9be5c15..583dd76 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -186,6 +186,7 @@
field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
+ field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
@@ -1478,6 +1479,7 @@
}
public class OverlayManager {
+ method @Nullable public android.content.om.OverlayInfo getOverlayInfo(@NonNull String, @NonNull android.os.UserHandle);
method public java.util.List<android.content.om.OverlayInfo> getOverlayInfosForTarget(@Nullable String, int);
method public boolean setEnabled(@Nullable String, boolean, int);
method public boolean setEnabledExclusiveInCategory(@Nullable String, int);
@@ -1836,9 +1838,9 @@
}
public final class RollbackManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void commitRollback(int, @NonNull java.util.List<android.content.pm.VersionedPackage>, @NonNull android.content.IntentSender);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks();
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getRecentlyCommittedRollbacks();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) public void commitRollback(int, @NonNull java.util.List<android.content.pm.VersionedPackage>, @NonNull android.content.IntentSender);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getRecentlyCommittedRollbacks();
field public static final String EXTRA_STATUS = "android.content.rollback.extra.STATUS";
field public static final String EXTRA_STATUS_MESSAGE = "android.content.rollback.extra.STATUS_MESSAGE";
field public static final int STATUS_FAILURE = 1; // 0x1
@@ -4294,11 +4296,14 @@
package android.net.apf {
- public class ApfCapabilities {
+ public final class ApfCapabilities implements android.os.Parcelable {
ctor public ApfCapabilities(int, int, int);
+ method public int describeContents();
method public static boolean getApfDrop8023Frames(android.content.Context);
method public static int[] getApfEthTypeBlackList(android.content.Context);
method public boolean hasDataAccess();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR;
field public final int apfPacketFormat;
field public final int apfVersionSupported;
field public final int maximumApfProgramSize;
@@ -5894,6 +5899,7 @@
field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
field public static final String NAMESPACE_SCHEDULER = "scheduler";
+ field public static final String NAMESPACE_STORAGE = "storage";
field public static final String NAMESPACE_SYSTEMUI = "systemui";
field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
}
@@ -5922,11 +5928,6 @@
method @Nullable public String getString(@NonNull String, @Nullable String);
}
- public static interface DeviceConfig.Storage {
- field public static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
- field public static final String NAMESPACE = "storage";
- }
-
public static interface DeviceConfig.Telephony {
field public static final String NAMESPACE = "telephony";
field public static final String RAMPING_RINGER_DURATION = "ramping_ringer_duration";
@@ -6128,6 +6129,7 @@
field public static final String LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE = "location_permissions_upgrade_to_q_mode";
field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
+ field public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
field public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES = "theme_customization_overlay_packages";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
field public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10; // 0xa
@@ -6438,6 +6440,7 @@
method public int getEventType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.contentcapture.ActivityEvent> CREATOR;
+ field public static final int TYPE_ACTIVITY_DESTROYED = 24; // 0x18
field public static final int TYPE_ACTIVITY_PAUSED = 2; // 0x2
field public static final int TYPE_ACTIVITY_RESUMED = 1; // 0x1
field public static final int TYPE_ACTIVITY_STOPPED = 23; // 0x17
@@ -6456,6 +6459,7 @@
method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest);
method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
+ field public static final String SERVICE_META_DATA = "android.content_capture";
}
public final class SnapshotData implements android.os.Parcelable {
@@ -8584,7 +8588,7 @@
field public final java.util.HashMap<java.lang.String,android.os.Bundle> mParticipants;
}
- public class ImsException extends java.lang.Exception {
+ public final class ImsException extends java.lang.Exception {
ctor public ImsException(@Nullable String);
ctor public ImsException(@Nullable String, int);
ctor public ImsException(@Nullable String, int, @Nullable Throwable);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 18d0ec0..7e04469 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -60,14 +60,6 @@
}
-package android.content.pm {
-
- public static class PackageInstaller.SessionParams implements android.os.Parcelable {
- method @Deprecated public void setEnableRollback();
- }
-
-}
-
package android.location {
public class LocationManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index c2ee2c3..9e8b02a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -14,6 +14,7 @@
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+ field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
field public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
@@ -720,11 +721,11 @@
}
public final class RollbackManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void commitRollback(int, @NonNull java.util.List<android.content.pm.VersionedPackage>, @NonNull android.content.IntentSender);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks();
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getRecentlyCommittedRollbacks();
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void reloadPersistedData();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) public void commitRollback(int, @NonNull java.util.List<android.content.pm.VersionedPackage>, @NonNull android.content.IntentSender);
+ method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getRecentlyCommittedRollbacks();
+ method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void reloadPersistedData();
field public static final String EXTRA_STATUS = "android.content.rollback.extra.STATUS";
field public static final String EXTRA_STATUS_MESSAGE = "android.content.rollback.extra.STATUS_MESSAGE";
field public static final String PROPERTY_ROLLBACK_LIFETIME_MILLIS = "rollback_lifetime_in_millis";
@@ -1306,11 +1307,14 @@
package android.net.apf {
- public class ApfCapabilities {
+ public final class ApfCapabilities implements android.os.Parcelable {
ctor public ApfCapabilities(int, int, int);
+ method public int describeContents();
method public static boolean getApfDrop8023Frames(android.content.Context);
method public static int[] getApfEthTypeBlackList(android.content.Context);
method public boolean hasDataAccess();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR;
field public final int apfPacketFormat;
field public final int apfVersionSupported;
field public final int maximumApfProgramSize;
@@ -2390,6 +2394,7 @@
method public int getEventType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.contentcapture.ActivityEvent> CREATOR;
+ field public static final int TYPE_ACTIVITY_DESTROYED = 24; // 0x18
field public static final int TYPE_ACTIVITY_PAUSED = 2; // 0x2
field public static final int TYPE_ACTIVITY_RESUMED = 1; // 0x1
field public static final int TYPE_ACTIVITY_STOPPED = 23; // 0x17
@@ -2408,6 +2413,7 @@
method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest);
method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
+ field public static final String SERVICE_META_DATA = "android.content_capture";
}
public final class SnapshotData implements android.os.Parcelable {
diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h
index dcc69b3..718e361 100644
--- a/cmds/idmap2/idmap2/Commands.h
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -20,10 +20,12 @@
#include <string>
#include <vector>
-bool Create(const std::vector<std::string>& args, std::ostream& out_error);
-bool Dump(const std::vector<std::string>& args, std::ostream& out_error);
-bool Lookup(const std::vector<std::string>& args, std::ostream& out_error);
-bool Scan(const std::vector<std::string>& args, std::ostream& out_error);
-bool Verify(const std::vector<std::string>& args, std::ostream& out_error);
+#include "idmap2/Result.h"
+
+android::idmap2::Result<android::idmap2::Unit> Create(const std::vector<std::string>& args);
+android::idmap2::Result<android::idmap2::Unit> Dump(const std::vector<std::string>& args);
+android::idmap2::Result<android::idmap2::Unit> Lookup(const std::vector<std::string>& args);
+android::idmap2::Result<android::idmap2::Unit> Scan(const std::vector<std::string>& args);
+android::idmap2::Result<android::idmap2::Unit> Verify(const std::vector<std::string>& args);
#endif // IDMAP2_IDMAP2_COMMANDS_H_
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index c416fa1..fdbb210 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -33,14 +33,17 @@
using android::ApkAssets;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::CommandLineOptions;
+using android::idmap2::Error;
using android::idmap2::Idmap;
using android::idmap2::PoliciesToBitmask;
using android::idmap2::PolicyBitmask;
using android::idmap2::PolicyFlags;
+using android::idmap2::Result;
+using android::idmap2::Unit;
using android::idmap2::utils::kIdmapFilePermissionMask;
using android::idmap2::utils::UidHasWriteAccessToPath;
-bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
+Result<Unit> Create(const std::vector<std::string>& args) {
SYSTRACE << "Create " << args;
std::string target_apk_path;
std::string overlay_apk_path;
@@ -63,15 +66,14 @@
&policies)
.OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks",
&ignore_overlayable);
- if (!opts.Parse(args, out_error)) {
- return false;
+ const auto opts_ok = opts.Parse(args);
+ if (!opts_ok) {
+ return opts_ok.GetError();
}
const uid_t uid = getuid();
if (!UidHasWriteAccessToPath(uid, idmap_path)) {
- out_error << "error: uid " << uid << " does not have write access to " << idmap_path
- << std::endl;
- return false;
+ return Error("uid %d does not have write access to %s", uid, idmap_path.c_str());
}
PolicyBitmask fulfilled_policies = 0;
@@ -79,8 +81,7 @@
if (conv_result) {
fulfilled_policies |= *conv_result;
} else {
- out_error << "error: " << conv_result.GetErrorMessage() << std::endl;
- return false;
+ return conv_result.GetError();
}
if (fulfilled_policies == 0) {
@@ -89,36 +90,33 @@
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
- out_error << "error: failed to load apk " << target_apk_path << std::endl;
- return false;
+ return Error("failed to load apk %s", target_apk_path.c_str());
}
const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
if (!overlay_apk) {
- out_error << "error: failed to load apk " << overlay_apk_path << std::endl;
- return false;
+ return Error("failed to load apk %s", overlay_apk_path.c_str());
}
+ std::stringstream stream;
const std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- fulfilled_policies, !ignore_overlayable, out_error);
+ fulfilled_policies, !ignore_overlayable, stream);
if (!idmap) {
- return false;
+ return Error("failed to create idmap: %s", stream.str().c_str());
}
umask(kIdmapFilePermissionMask);
std::ofstream fout(idmap_path);
if (fout.fail()) {
- out_error << "failed to open idmap path " << idmap_path << std::endl;
- return false;
+ return Error("failed to open idmap path %s", idmap_path.c_str());
}
BinaryStreamVisitor visitor(fout);
idmap->accept(&visitor);
fout.close();
if (fout.fail()) {
- out_error << "failed to write to idmap path " << idmap_path << std::endl;
- return false;
+ return Error("failed to write to idmap path %s", idmap_path.c_str());
}
- return true;
+ return Unit{};
}
diff --git a/cmds/idmap2/idmap2/Dump.cpp b/cmds/idmap2/idmap2/Dump.cpp
index 3947703..fd58222 100644
--- a/cmds/idmap2/idmap2/Dump.cpp
+++ b/cmds/idmap2/idmap2/Dump.cpp
@@ -17,6 +17,7 @@
#include <fstream>
#include <iostream>
#include <memory>
+#include <sstream>
#include <string>
#include <vector>
@@ -24,14 +25,18 @@
#include "idmap2/Idmap.h"
#include "idmap2/PrettyPrintVisitor.h"
#include "idmap2/RawPrintVisitor.h"
+#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
using android::idmap2::CommandLineOptions;
+using android::idmap2::Error;
using android::idmap2::Idmap;
using android::idmap2::PrettyPrintVisitor;
using android::idmap2::RawPrintVisitor;
+using android::idmap2::Result;
+using android::idmap2::Unit;
-bool Dump(const std::vector<std::string>& args, std::ostream& out_error) {
+Result<Unit> Dump(const std::vector<std::string>& args) {
SYSTRACE << "Dump " << args;
std::string idmap_path;
bool verbose;
@@ -40,14 +45,16 @@
CommandLineOptions("idmap2 dump")
.MandatoryOption("--idmap-path", "input: path to idmap file to pretty-print", &idmap_path)
.OptionalFlag("--verbose", "annotate every byte of the idmap", &verbose);
- if (!opts.Parse(args, out_error)) {
- return false;
+ const auto opts_ok = opts.Parse(args);
+ if (!opts_ok) {
+ return opts_ok.GetError();
}
+ std::stringstream stream;
std::ifstream fin(idmap_path);
- const std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(fin, out_error);
+ const std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(fin, stream);
fin.close();
if (!idmap) {
- return false;
+ return Error("failed to load idmap: %s", stream.str().c_str());
}
if (verbose) {
@@ -58,5 +65,5 @@
idmap->accept(&visitor);
}
- return true;
+ return Unit{};
}
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 83a40ef..677c6fa 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -57,6 +57,7 @@
using android::idmap2::IdmapHeader;
using android::idmap2::ResourceId;
using android::idmap2::Result;
+using android::idmap2::Unit;
using android::idmap2::Xml;
using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;
@@ -157,7 +158,7 @@
}
} // namespace
-bool Lookup(const std::vector<std::string>& args, std::ostream& out_error) {
+Result<Unit> Lookup(const std::vector<std::string>& args) {
SYSTRACE << "Lookup " << args;
std::vector<std::string> idmap_paths;
std::string config_str;
@@ -172,14 +173,14 @@
"'[package:]type/name') to look up",
&resid_str);
- if (!opts.Parse(args, out_error)) {
- return false;
+ const auto opts_ok = opts.Parse(args);
+ if (!opts_ok) {
+ return opts_ok.GetError();
}
ConfigDescription config;
if (!ConfigDescription::Parse(config_str, &config)) {
- out_error << "error: failed to parse config" << std::endl;
- return false;
+ return Error("failed to parse config");
}
std::vector<std::unique_ptr<const ApkAssets>> apk_assets;
@@ -191,39 +192,33 @@
auto idmap_header = IdmapHeader::FromBinaryStream(fin);
fin.close();
if (!idmap_header) {
- out_error << "error: failed to read idmap from " << idmap_path << std::endl;
- return false;
+ return Error("failed to read idmap from %s", idmap_path.c_str());
}
if (i == 0) {
target_path = idmap_header->GetTargetPath().to_string();
auto target_apk = ApkAssets::Load(target_path);
if (!target_apk) {
- out_error << "error: failed to read target apk from " << target_path << std::endl;
- return false;
+ return Error("failed to read target apk from %s", target_path.c_str());
}
apk_assets.push_back(std::move(target_apk));
const Result<std::string> package_name =
GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
if (!package_name) {
- out_error << "error: failed to parse android:targetPackage from overlay manifest"
- << std::endl;
- return false;
+ return Error("failed to parse android:targetPackage from overlay manifest");
}
target_package_name = *package_name;
} else if (target_path != idmap_header->GetTargetPath()) {
- out_error << "error: different target APKs (expected target APK " << target_path << " but "
- << idmap_path << " has target APK " << idmap_header->GetTargetPath() << ")"
- << std::endl;
- return false;
+ 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());
}
auto overlay_apk = ApkAssets::LoadOverlay(idmap_path);
if (!overlay_apk) {
- out_error << "error: failed to read overlay apk from " << idmap_header->GetOverlayPath()
- << std::endl;
- return false;
+ return Error("failed to read overlay apk from %s",
+ idmap_header->GetOverlayPath().to_string().c_str());
}
apk_assets.push_back(std::move(overlay_apk));
}
@@ -238,16 +233,14 @@
const Result<ResourceId> resid = ParseResReference(am, resid_str, target_package_name);
if (!resid) {
- out_error << "error: failed to parse resource ID" << std::endl;
- return false;
+ return Error(resid.GetError(), "failed to parse resource ID");
}
const Result<std::string> value = GetValue(am, *resid);
if (!value) {
- out_error << StringPrintf("error: resource 0x%08x not found", *resid) << std::endl;
- return false;
+ return Error(value.GetError(), "resource 0x%08x not found", *resid);
}
std::cout << *value << std::endl;
- return true;
+ return Unit{};
}
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index a0ffccb..d8867fe 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -24,14 +24,17 @@
#include <vector>
#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
#include "Commands.h"
using android::idmap2::CommandLineOptions;
+using android::idmap2::Result;
+using android::idmap2::Unit;
using NameToFunctionMap =
- std::map<std::string, std::function<bool(const std::vector<std::string>&, std::ostream&)>>;
+ std::map<std::string, std::function<Result<Unit>(const std::vector<std::string>&)>>;
namespace {
@@ -69,5 +72,10 @@
PrintUsage(commands, std::cerr);
return EXIT_FAILURE;
}
- return iter->second(*args, std::cerr) ? EXIT_SUCCESS : EXIT_FAILURE;
+ const auto result = iter->second(*args);
+ if (!result) {
+ std::cerr << "error: " << result.GetErrorMessage() << std::endl;
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
}
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index e85f132..24331af 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -30,6 +30,7 @@
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
#include "idmap2/Xml.h"
#include "idmap2/ZipFile.h"
@@ -37,6 +38,7 @@
#include "Commands.h"
using android::idmap2::CommandLineOptions;
+using android::idmap2::Error;
using android::idmap2::Idmap;
using android::idmap2::kPolicyProduct;
using android::idmap2::kPolicyPublic;
@@ -45,6 +47,7 @@
using android::idmap2::PolicyBitmask;
using android::idmap2::PolicyFlags;
using android::idmap2::Result;
+using android::idmap2::Unit;
using android::idmap2::utils::ExtractOverlayManifestInfo;
using android::idmap2::utils::FindFiles;
using android::idmap2::utils::OverlayManifestInfo;
@@ -69,8 +72,8 @@
return version == "Q" || version == "q";
}
-std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::string>& dirs,
- bool recursive, std::ostream& out_error) {
+Result<std::unique_ptr<std::vector<std::string>>> FindApkFiles(const std::vector<std::string>& dirs,
+ bool recursive) {
SYSTRACE << "FindApkFiles " << dirs << " " << recursive;
const auto predicate = [](unsigned char type, const std::string& path) -> bool {
static constexpr size_t kExtLen = 4; // strlen(".apk")
@@ -82,8 +85,7 @@
for (const auto& dir : dirs) {
const auto apk_paths = FindFiles(dir, recursive, predicate);
if (!apk_paths) {
- out_error << "error: failed to open directory " << dir << std::endl;
- return nullptr;
+ return Error("failed to open directory %s", dir.c_str());
}
paths.insert(apk_paths->cbegin(), apk_paths->cend());
}
@@ -110,7 +112,7 @@
} // namespace
-bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
+Result<Unit> Scan(const std::vector<std::string>& args) {
SYSTRACE << "Scan " << args;
std::vector<std::string> input_directories;
std::string target_package_name;
@@ -135,22 +137,22 @@
"input: an overlayable policy this overlay fulfills "
"(if none or supplied, the overlays will not have their policies overriden",
&override_policies);
- if (!opts.Parse(args, out_error)) {
- return false;
+ const auto opts_ok = opts.Parse(args);
+ if (!opts_ok) {
+ return opts_ok.GetError();
}
- const auto apk_paths = FindApkFiles(input_directories, recursive, out_error);
+ const auto apk_paths = FindApkFiles(input_directories, recursive);
if (!apk_paths) {
- return false;
+ return Error(apk_paths.GetError(), "failed to find apk files");
}
std::vector<InputOverlay> interesting_apks;
- for (const std::string& path : *apk_paths) {
+ for (const std::string& path : **apk_paths) {
Result<OverlayManifestInfo> overlay_info =
ExtractOverlayManifestInfo(path, /* assert_overlay */ false);
if (!overlay_info) {
- out_error << "error: " << overlay_info.GetErrorMessage() << std::endl;
- return false;
+ return overlay_info.GetError();
}
if (!overlay_info->is_static) {
@@ -194,16 +196,13 @@
std::stringstream stream;
for (const auto& overlay : interesting_apks) {
- // Create the idmap for the overlay if it currently does not exist or if it is not up to date.
- std::stringstream dev_null;
-
std::vector<std::string> verify_args = {"--idmap-path", overlay.idmap_path};
for (const std::string& policy : overlay.policies) {
verify_args.emplace_back("--policy");
verify_args.emplace_back(policy);
}
- if (!Verify(std::vector<std::string>(verify_args), dev_null)) {
+ if (!Verify(std::vector<std::string>(verify_args))) {
std::vector<std::string> create_args = {"--target-apk-path", target_apk_path,
"--overlay-apk-path", overlay.apk_path,
"--idmap-path", overlay.idmap_path};
@@ -216,8 +215,9 @@
create_args.emplace_back(policy);
}
- if (!Create(create_args, out_error)) {
- return false;
+ const auto create_ok = Create(create_args);
+ if (!create_ok) {
+ return Error(create_ok.GetError(), "failed to create idmap");
}
}
@@ -226,5 +226,5 @@
std::cout << stream.str();
- return true;
+ return Unit{};
}
diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/Verify.cpp
index d8fe7aa..9cb67b3 100644
--- a/cmds/idmap2/idmap2/Verify.cpp
+++ b/cmds/idmap2/idmap2/Verify.cpp
@@ -21,29 +21,39 @@
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
using android::idmap2::CommandLineOptions;
+using android::idmap2::Error;
using android::idmap2::IdmapHeader;
+using android::idmap2::Result;
+using android::idmap2::Unit;
-bool Verify(const std::vector<std::string>& args, std::ostream& out_error) {
+Result<Unit> Verify(const std::vector<std::string>& args) {
SYSTRACE << "Verify " << args;
std::string idmap_path;
const CommandLineOptions opts =
CommandLineOptions("idmap2 verify")
.MandatoryOption("--idmap-path", "input: path to idmap file to verify", &idmap_path);
- if (!opts.Parse(args, out_error)) {
- return false;
+
+ const auto opts_ok = opts.Parse(args);
+ if (!opts_ok) {
+ return opts_ok.GetError();
}
std::ifstream fin(idmap_path);
const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
fin.close();
if (!header) {
- out_error << "error: failed to parse idmap header" << std::endl;
- return false;
+ return Error("failed to parse idmap header");
}
- return header->IsUpToDate(out_error);
+ const auto header_ok = header->IsUpToDate();
+ if (!header_ok) {
+ return Error(header_ok.GetError(), "idmap not up to date");
+ }
+
+ return Unit{};
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index fa94414..e03a9cc 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -104,8 +104,7 @@
std::ifstream fin(idmap_path);
const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
fin.close();
- std::stringstream dev_null;
- *_aidl_return = header && header->IsUpToDate(dev_null);
+ *_aidl_return = header && header->IsUpToDate();
// TODO(b/119328308): Check that the set of fulfilled policies of the overlay has not changed
diff --git a/cmds/idmap2/include/idmap2/CommandLineOptions.h b/cmds/idmap2/include/idmap2/CommandLineOptions.h
index 6db6bf9..52ac818 100644
--- a/cmds/idmap2/include/idmap2/CommandLineOptions.h
+++ b/cmds/idmap2/include/idmap2/CommandLineOptions.h
@@ -23,6 +23,8 @@
#include <string>
#include <vector>
+#include "idmap2/Result.h"
+
namespace android::idmap2 {
/*
@@ -46,7 +48,7 @@
std::string* value);
CommandLineOptions& OptionalOption(const std::string& name, const std::string& description,
std::vector<std::string>* value);
- bool Parse(const std::vector<std::string>& argv, std::ostream& outError) const;
+ Result<Unit> Parse(const std::vector<std::string>& argv) const;
void Usage(std::ostream& out) const;
private:
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 1666dc8..673d18d 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -115,7 +115,7 @@
// 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.
- bool IsUpToDate(std::ostream& out_error) const;
+ Result<Unit> IsUpToDate() const;
void accept(Visitor* v) const;
diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
index a49a607..d5fd2ce 100644
--- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -19,12 +19,14 @@
#include <iostream>
#include <memory>
#include <set>
+#include <sstream>
#include <string>
#include <vector>
#include "android-base/macros.h"
#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Result.h"
namespace android::idmap2 {
@@ -77,7 +79,7 @@
return *this;
}
-bool CommandLineOptions::Parse(const std::vector<std::string>& argv, std::ostream& outError) const {
+Result<Unit> CommandLineOptions::Parse(const std::vector<std::string>& argv) const {
const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
});
@@ -89,8 +91,9 @@
for (size_t i = 0; i < argv_size; i++) {
const std::string arg = argv[i];
if ("--help" == arg || "-h" == arg) {
- Usage(outError);
- return false;
+ std::stringstream stream;
+ Usage(stream);
+ return Error("%s", stream.str().c_str());
}
bool match = false;
for (const Option& opt : options_) {
@@ -100,9 +103,9 @@
if (opt.argument) {
i++;
if (i >= argv_size) {
- outError << "error: " << opt.name << ": missing argument" << std::endl;
- Usage(outError);
- return false;
+ std::stringstream stream;
+ Usage(stream);
+ return Error("%s: missing argument\n%s", opt.name.c_str(), stream.str().c_str());
}
}
opt.action(argv[i]);
@@ -111,20 +114,27 @@
}
}
if (!match) {
- outError << "error: " << arg << ": unknown option" << std::endl;
- Usage(outError);
- return false;
+ std::stringstream stream;
+ Usage(stream);
+ return Error("%s: unknown option\n%s", arg.c_str(), stream.str().c_str());
}
}
if (!mandatory_opts.empty()) {
+ std::stringstream stream;
+ bool separator = false;
for (const auto& opt : mandatory_opts) {
- outError << "error: " << opt << ": missing mandatory option" << std::endl;
+ if (separator) {
+ stream << ", ";
+ }
+ separator = true;
+ stream << opt << ": missing mandatory option";
}
- Usage(outError);
- return false;
+ stream << std::endl;
+ Usage(stream);
+ return Error("%s", stream.str().c_str());
}
- return true;
+ return Unit{};
}
void CommandLineOptions::Usage(std::ostream& out) const {
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index a7d180c..9afdd437 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -142,62 +142,46 @@
return std::move(idmap_header);
}
-bool IdmapHeader::IsUpToDate(std::ostream& out_error) const {
+Result<Unit> IdmapHeader::IsUpToDate() const {
if (magic_ != kIdmapMagic) {
- out_error << base::StringPrintf("error: bad magic: actual 0x%08x, expected 0x%08x", magic_,
- kIdmapMagic)
- << std::endl;
- return false;
+ return Error("bad magic: actual 0x%08x, expected 0x%08x", magic_, kIdmapMagic);
}
if (version_ != kIdmapCurrentVersion) {
- out_error << base::StringPrintf("error: bad version: actual 0x%08x, expected 0x%08x", version_,
- kIdmapCurrentVersion)
- << std::endl;
- return false;
+ return Error("bad version: actual 0x%08x, expected 0x%08x", version_, kIdmapCurrentVersion);
}
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path_);
if (!target_zip) {
- out_error << "error: failed to open target " << target_path_ << std::endl;
- return false;
+ return Error("failed to open target %s", GetTargetPath().to_string().c_str());
}
Result<uint32_t> target_crc = GetCrc(*target_zip);
if (!target_crc) {
- out_error << "error: failed to get target crc" << std::endl;
- return false;
+ return Error("failed to get target crc");
}
if (target_crc_ != *target_crc) {
- out_error << base::StringPrintf(
- "error: bad target crc: idmap version 0x%08x, file system version 0x%08x",
- target_crc_, *target_crc)
- << std::endl;
- return false;
+ return Error("bad target crc: idmap version 0x%08x, file system version 0x%08x", target_crc_,
+ *target_crc);
}
const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path_);
if (!overlay_zip) {
- out_error << "error: failed to open overlay " << overlay_path_ << std::endl;
- return false;
+ return Error("failed to open overlay %s", GetOverlayPath().to_string().c_str());
}
Result<uint32_t> overlay_crc = GetCrc(*overlay_zip);
if (!overlay_crc) {
- out_error << "error: failed to get overlay crc" << std::endl;
- return false;
+ return Error("failed to get overlay crc");
}
if (overlay_crc_ != *overlay_crc) {
- out_error << base::StringPrintf(
- "error: bad overlay crc: idmap version 0x%08x, file system version 0x%08x",
- overlay_crc_, *overlay_crc)
- << std::endl;
- return false;
+ return Error("bad overlay crc: idmap version 0x%08x, file system version 0x%08x", overlay_crc_,
+ *overlay_crc);
}
- return true;
+ return Unit{};
}
std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
@@ -452,7 +436,7 @@
continue;
}
- if (!enforce_overlayable) {
+ if (enforce_overlayable) {
Result<Unit> success =
CheckOverlayable(*target_pkg, *overlay_info, fulfilled_policies, target_resid);
if (!success) {
diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
index 39f18d3..d567af6 100644
--- a/cmds/idmap2/tests/CommandLineOptionsTests.cpp
+++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
@@ -46,14 +46,13 @@
CommandLineOptions opts =
CommandLineOptions("test").OptionalFlag("--foo", "", &foo).OptionalFlag("--bar", "", &bar);
- std::ostream fakeStdErr(nullptr);
- bool success = opts.Parse({"--foo", "--bar"}, fakeStdErr);
+ auto success = opts.Parse({"--foo", "--bar"});
ASSERT_TRUE(success);
ASSERT_TRUE(foo);
ASSERT_TRUE(bar);
foo = bar = false;
- success = opts.Parse({"--foo"}, fakeStdErr);
+ success = opts.Parse({"--foo"});
ASSERT_TRUE(success);
ASSERT_TRUE(foo);
ASSERT_FALSE(bar);
@@ -65,21 +64,19 @@
CommandLineOptions opts = CommandLineOptions("test")
.MandatoryOption("--foo", "", &foo)
.MandatoryOption("--bar", "", &bar);
- std::ostream fakeStdErr(nullptr);
- bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
+ auto success = opts.Parse({"--foo", "FOO", "--bar", "BAR"});
ASSERT_TRUE(success);
ASSERT_EQ(foo, "FOO");
ASSERT_EQ(bar, "BAR");
- success = opts.Parse({"--foo"}, fakeStdErr);
+ success = opts.Parse({"--foo"});
ASSERT_FALSE(success);
}
TEST(CommandLineOptionsTests, MandatoryOptionMultipleArgsButExpectedOnce) {
std::string foo;
CommandLineOptions opts = CommandLineOptions("test").MandatoryOption("--foo", "", &foo);
- std::ostream fakeStdErr(nullptr);
- bool success = opts.Parse({"--foo", "FIRST", "--foo", "SECOND"}, fakeStdErr);
+ auto success = opts.Parse({"--foo", "FIRST", "--foo", "SECOND"});
ASSERT_TRUE(success);
ASSERT_EQ(foo, "SECOND");
}
@@ -87,8 +84,7 @@
TEST(CommandLineOptionsTests, MandatoryOptionMultipleArgsAndExpectedOnceOrMore) {
std::vector<std::string> args;
CommandLineOptions opts = CommandLineOptions("test").MandatoryOption("--foo", "", &args);
- std::ostream fakeStdErr(nullptr);
- bool success = opts.Parse({"--foo", "FOO", "--foo", "BAR"}, fakeStdErr);
+ auto success = opts.Parse({"--foo", "FOO", "--foo", "BAR"});
ASSERT_TRUE(success);
ASSERT_EQ(args.size(), 2U);
ASSERT_EQ(args[0], "FOO");
@@ -101,23 +97,22 @@
CommandLineOptions opts = CommandLineOptions("test")
.OptionalOption("--foo", "", &foo)
.OptionalOption("--bar", "", &bar);
- std::ostream fakeStdErr(nullptr);
- bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
+ auto success = opts.Parse({"--foo", "FOO", "--bar", "BAR"});
ASSERT_TRUE(success);
ASSERT_EQ(foo, "FOO");
ASSERT_EQ(bar, "BAR");
- success = opts.Parse({"--foo", "BAZ"}, fakeStdErr);
+ success = opts.Parse({"--foo", "BAZ"});
ASSERT_TRUE(success);
ASSERT_EQ(foo, "BAZ");
- success = opts.Parse({"--foo"}, fakeStdErr);
+ success = opts.Parse({"--foo"});
ASSERT_FALSE(success);
- success = opts.Parse({"--foo", "--bar", "BAR"}, fakeStdErr);
+ success = opts.Parse({"--foo", "--bar", "BAR"});
ASSERT_FALSE(success);
- success = opts.Parse({"--foo", "FOO", "--bar"}, fakeStdErr);
+ success = opts.Parse({"--foo", "FOO", "--bar"});
ASSERT_FALSE(success);
}
@@ -127,8 +122,7 @@
CommandLineOptions opts = CommandLineOptions("test")
.OptionalOption("--foo", "", &foo)
.OptionalOption("--bar", "", &bar);
- std::ostream fakeStdErr(nullptr);
- bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
+ auto success = opts.Parse({"--foo", "FOO", "--bar", "BAR"});
ASSERT_TRUE(success);
ASSERT_EQ(foo.size(), 1U);
ASSERT_EQ(foo[0], "FOO");
@@ -137,7 +131,7 @@
foo.clear();
bar.clear();
- success = opts.Parse({"--foo", "BAZ"}, fakeStdErr);
+ success = opts.Parse({"--foo", "BAZ"});
ASSERT_TRUE(success);
ASSERT_EQ(foo.size(), 1U);
ASSERT_EQ(foo[0], "BAZ");
@@ -145,8 +139,7 @@
foo.clear();
bar.clear();
- success =
- opts.Parse({"--foo", "BAZ", "--foo", "BIZ", "--bar", "FIZ", "--bar", "FUZZ"}, fakeStdErr);
+ success = opts.Parse({"--foo", "BAZ", "--foo", "BIZ", "--bar", "FIZ", "--bar", "FUZZ"});
ASSERT_TRUE(success);
ASSERT_EQ(foo.size(), 2U);
ASSERT_EQ(foo[0], "BAZ");
@@ -157,17 +150,17 @@
foo.clear();
bar.clear();
- success = opts.Parse({"--foo"}, fakeStdErr);
+ success = opts.Parse({"--foo"});
ASSERT_FALSE(success);
foo.clear();
bar.clear();
- success = opts.Parse({"--foo", "--bar", "BAR"}, fakeStdErr);
+ success = opts.Parse({"--foo", "--bar", "BAR"});
ASSERT_FALSE(success);
foo.clear();
bar.clear();
- success = opts.Parse({"--foo", "FOO", "--bar"}, fakeStdErr);
+ success = opts.Parse({"--foo", "FOO", "--bar"});
ASSERT_FALSE(success);
}
@@ -179,14 +172,13 @@
.MandatoryOption("--foo", "", &foo)
.OptionalFlag("--baz", "", &baz)
.OptionalOption("--bar", "", &bar);
- std::ostream fakeStdErr(nullptr);
- bool success = opts.Parse({"--unexpected"}, fakeStdErr);
+ auto success = opts.Parse({"--unexpected"});
ASSERT_FALSE(success);
- success = opts.Parse({"--bar", "BAR"}, fakeStdErr);
+ success = opts.Parse({"--bar", "BAR"});
ASSERT_FALSE(success);
- success = opts.Parse({"--baz", "--foo", "FOO"}, fakeStdErr);
+ success = opts.Parse({"--baz", "--foo", "FOO"});
ASSERT_TRUE(success);
ASSERT_TRUE(baz);
ASSERT_EQ(foo, "FOO");
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 8d65428..c20ae7b 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -501,7 +501,7 @@
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
- ASSERT_TRUE(header->IsUpToDate(error)) << error.str();
+ ASSERT_TRUE(header->IsUpToDate());
// magic: bytes (0x0, 0x03)
std::string bad_magic_string(stream.str());
@@ -514,7 +514,7 @@
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(error));
+ ASSERT_FALSE(bad_magic_header->IsUpToDate());
// version: bytes (0x4, 0x07)
std::string bad_version_string(stream.str());
@@ -527,7 +527,7 @@
IdmapHeader::FromBinaryStream(bad_version_stream);
ASSERT_THAT(bad_version_header, NotNull());
ASSERT_NE(header->GetVersion(), bad_version_header->GetVersion());
- ASSERT_FALSE(bad_version_header->IsUpToDate(error));
+ ASSERT_FALSE(bad_version_header->IsUpToDate());
// target crc: bytes (0x8, 0xb)
std::string bad_target_crc_string(stream.str());
@@ -540,7 +540,7 @@
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_target_crc_header->IsUpToDate(error));
+ ASSERT_FALSE(bad_target_crc_header->IsUpToDate());
// overlay crc: bytes (0xc, 0xf)
std::string bad_overlay_crc_string(stream.str());
@@ -553,7 +553,7 @@
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_overlay_crc_header->IsUpToDate(error));
+ ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate());
// target path: bytes (0x10, 0x10f)
std::string bad_target_path_string(stream.str());
@@ -563,7 +563,7 @@
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_target_path_header->IsUpToDate(error));
+ ASSERT_FALSE(bad_target_path_header->IsUpToDate());
// overlay path: bytes (0x110, 0x20f)
std::string bad_overlay_path_string(stream.str());
@@ -573,7 +573,7 @@
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_overlay_path_header->IsUpToDate(error));
+ ASSERT_FALSE(bad_overlay_path_header->IsUpToDate());
}
class TestVisitor : public Visitor {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f6cfe48..e8d3293 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -340,4 +340,21 @@
* like persisting database etc.
*/
public abstract void prepareForPossibleShutdown();
+
+ /**
+ * Returns {@code true} if {@code uid} is running a foreground service of a specific
+ * {@code foregroundServiceType}.
+ */
+ public abstract boolean hasRunningForegroundService(int uid, int foregroundServiceType);
+
+ /**
+ * Registers the specified {@code processObserver} to be notified of future changes to
+ * process state.
+ */
+ public abstract void registerProcessObserver(IProcessObserver processObserver);
+
+ /**
+ * Unregisters the specified {@code processObserver}.
+ */
+ public abstract void unregisterProcessObserver(IProcessObserver processObserver);
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a226062..7a0639e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3054,9 +3054,9 @@
}
@Override
- public String getContentCaptureServicePackageName() {
+ public String getSystemCaptionsServicePackageName() {
try {
- return mPM.getContentCaptureServicePackageName();
+ return mPM.getSystemCaptionsServicePackageName();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl
index b436aa2..7be3620 100644
--- a/core/java/android/app/IProcessObserver.aidl
+++ b/core/java/android/app/IProcessObserver.aidl
@@ -19,5 +19,6 @@
/** {@hide} */
oneway interface IProcessObserver {
void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities);
+ void onForegroundServicesChanged(int pid, int uid, int serviceTypes);
void onProcessDied(int pid, int uid);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index efd9990..93a9dac 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2438,7 +2438,7 @@
* Broadcast Action: A rollback has been committed.
*
* <p class="note">This is a protected intent that can only be sent
- * by the system.
+ * by the system. The receiver must hold MANAGE_ROLLBACK permission.
*
* @hide
*/
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 8e72fa5..ceea043 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -16,12 +16,14 @@
package android.content.om;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import java.util.List;
@@ -96,6 +98,28 @@
}
/**
+ * Returns information about the overlay with the given package name for
+ * the specified user.
+ *
+ * @param packageName The name of the package.
+ * @param userHandle The user to get the OverlayInfos for.
+ * @return An OverlayInfo object; if no overlays exist with the
+ * requested package name, null is returned.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public OverlayInfo getOverlayInfo(@NonNull final String packageName,
+ @NonNull final UserHandle userHandle) {
+ try {
+ return mService.getOverlayInfo(packageName, userHandle.myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns information about all overlays for the given target package for
* the specified user. The returned list is ordered according to the
* overlay priority with the highest priority at the end of the list.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 706cbbf..068a93a 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -671,6 +671,14 @@
*/
public static final int PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE = 1 << 27;
+ /**
+ * Indicates whether this package is in fact a runtime resource overlay.
+ *
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_IS_RESOURCE_OVERLAY = 1 << 28;
+
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -683,6 +691,7 @@
PRIVATE_FLAG_HAS_DOMAIN_URLS,
PRIVATE_FLAG_HIDDEN,
PRIVATE_FLAG_INSTANT,
+ PRIVATE_FLAG_IS_RESOURCE_OVERLAY,
PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
PRIVATE_FLAG_OEM,
PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
@@ -2023,6 +2032,14 @@
}
/**
+ * Returns true if the package has declared in its manifest that it is a
+ * runtime resource overlay.
+ */
+ public boolean isResourceOverlay() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY) != 0;
+ }
+
+ /**
* @hide
*/
@Override protected ApplicationInfo getApplicationInfo() {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index fd3529b..464e866 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -742,7 +742,7 @@
String getAppPredictionServicePackageName();
- String getContentCaptureServicePackageName();
+ String getSystemCaptionsServicePackageName();
String getIncidentReportApproverPackageName();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 33b9c72..3edd17a 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1448,19 +1448,6 @@
}
/**
- * Request that rollbacks be enabled for the given upgrade.
- *
- * @removed
- * @deprecated use {@link #setEnableRollback(boolean)} instead.
- * @hide
- */
- @Deprecated
- @SystemApi
- public void setEnableRollback() {
- installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
- }
-
- /**
* Request that rollbacks be enabled or disabled for the given upgrade.
*
* @param enable set to {@code true} to enable, {@code false} to disable
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 99324ba..2f99879 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2589,14 +2589,6 @@
public static final String FEATURE_PC = "android.hardware.type.pc";
/**
- * Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: This is a foldable device. Properties such as
- * the display size may change in response to being folded.
- */
- @SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_FOLDABLE = "android.hardware.type.foldable";
-
- /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device supports printing.
*/
@@ -6863,9 +6855,9 @@
*
* @hide
*/
- public String getContentCaptureServicePackageName() {
+ public String getSystemCaptionsServicePackageName() {
throw new UnsupportedOperationException(
- "getContentCaptureServicePackageName not implemented in subclass");
+ "getSystemCaptionsServicePackageName not implemented in subclass");
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 9d0ece0..743a302 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2108,6 +2108,9 @@
return null;
}
+ pkg.applicationInfo.privateFlags |=
+ ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY;
+
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_KEY_SETS)) {
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 13c49a0..3488cc3 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -307,9 +307,9 @@
@Override
public String toString() {
- return "SharedLibraryInfo[name:" + mName + ", type:" + typeToString(mType)
+ return "SharedLibraryInfo{name:" + mName + ", type:" + typeToString(mType)
+ ", version:" + mVersion + (!getDependentPackages().isEmpty()
- ? " has dependents" : "");
+ ? " has dependents" : "") + "}";
}
@Override
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 6729242..6b8416d 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -191,9 +191,9 @@
/**
* Return a global shared Resources object that provides access to only
- * system resources (no application resources), and is not configured for
- * the current screen (can not use dimension units, does not change based
- * on orientation, etc).
+ * system resources (no application resources), is not configured for the
+ * current screen (can not use dimension units, does not change based on
+ * orientation, etc), and is not affected by Runtime Resource Overlay.
*/
public static Resources getSystem() {
synchronized (sSync) {
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 293c1e4..d54a6fe 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -76,10 +76,12 @@
/**
* Returns a list of all currently available rollbacks.
*
- * @throws SecurityException if the caller does not have the
- * MANAGE_ROLLBACKS permission.
+ * @throws SecurityException if the caller does not have appropriate permissions.
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ android.Manifest.permission.TEST_MANAGE_ROLLBACKS
+ })
@NonNull
public List<RollbackInfo> getAvailableRollbacks() {
try {
@@ -104,10 +106,12 @@
* rolled back from.
*
* @return the recently committed rollbacks
- * @throws SecurityException if the caller does not have the
- * MANAGE_ROLLBACKS permission.
+ * @throws SecurityException if the caller does not have appropriate permissions.
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ android.Manifest.permission.TEST_MANAGE_ROLLBACKS
+ })
public @NonNull List<RollbackInfo> getRecentlyCommittedRollbacks() {
try {
return mBinder.getRecentlyExecutedRollbacks().getList();
@@ -190,10 +194,12 @@
* @param statusReceiver where to deliver the results. Intents sent to
* this receiver contain {@link #EXTRA_STATUS}
* and {@link #EXTRA_STATUS_MESSAGE}.
- * @throws SecurityException if the caller does not have the
- * MANAGE_ROLLBACKS permission.
+ * @throws SecurityException if the caller does not have appropriate permissions.
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ android.Manifest.permission.TEST_MANAGE_ROLLBACKS
+ })
public void commitRollback(int rollbackId, @NonNull List<VersionedPackage> causePackages,
@NonNull IntentSender statusReceiver) {
try {
@@ -210,12 +216,11 @@
* across device reboot, by simulating what happens on reboot without
* actually rebooting the device.
*
- * @throws SecurityException if the caller does not have the
- * MANAGE_ROLLBACKS permission.
+ * @throws SecurityException if the caller does not have appropriate permissions.
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+ @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
@TestApi
public void reloadPersistedData() {
try {
@@ -232,12 +237,11 @@
* recently committed rollbacks that contain the given package.
*
* @param packageName the name of the package to expire data for.
- * @throws SecurityException if the caller does not have the
- * MANAGE_ROLLBACKS permission.
+ * @throws SecurityException if the caller does not have appropriate permissions.
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+ @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
@TestApi
public void expireRollbackForPackage(@NonNull String packageName) {
try {
diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java
index d796003..5e2875d 100644
--- a/core/java/android/database/sqlite/SQLiteGlobal.java
+++ b/core/java/android/database/sqlite/SQLiteGlobal.java
@@ -176,6 +176,6 @@
/** @hide */
public static boolean checkDbWipe() {
- return true;
+ return false;
}
}
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index f208724..7873fc0 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -29,15 +29,33 @@
* {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} activity.
*/
public class CaptivePortal implements Parcelable {
- /** @hide */
+ /**
+ * Response code from the captive portal application, indicating that the portal was dismissed
+ * and the network should be re-validated.
+ * @see ICaptivePortal#appResponse(int)
+ * @see android.net.INetworkMonitor#notifyCaptivePortalAppFinished(int)
+ * @hide
+ */
@SystemApi
@TestApi
public static final int APP_RETURN_DISMISSED = 0;
- /** @hide */
+ /**
+ * Response code from the captive portal application, indicating that the user did not login and
+ * does not want to use the captive portal network.
+ * @see ICaptivePortal#appResponse(int)
+ * @see android.net.INetworkMonitor#notifyCaptivePortalAppFinished(int)
+ * @hide
+ */
@SystemApi
@TestApi
public static final int APP_RETURN_UNWANTED = 1;
- /** @hide */
+ /**
+ * Response code from the captive portal application, indicating that the user does not wish to
+ * login but wants to use the captive portal network as-is.
+ * @see ICaptivePortal#appResponse(int)
+ * @see android.net.INetworkMonitor#notifyCaptivePortalAppFinished(int)
+ * @hide
+ */
@SystemApi
@TestApi
public static final int APP_RETURN_WANTED_AS_IS = 2;
diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java
index e09fa8f..d6023d7 100644
--- a/core/java/android/net/apf/ApfCapabilities.java
+++ b/core/java/android/net/apf/ApfCapabilities.java
@@ -19,17 +19,20 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
import com.android.internal.R;
/**
* APF program support capabilities.
*
+ * This class is immutable.
* @hide
*/
@SystemApi
@TestApi
-public class ApfCapabilities {
+public final class ApfCapabilities implements Parcelable {
/**
* Version of APF instruction set supported for packet filtering. 0 indicates no support for
* packet filtering using APF programs.
@@ -53,6 +56,37 @@
this.apfPacketFormat = apfPacketFormat;
}
+ private ApfCapabilities(Parcel in) {
+ apfVersionSupported = in.readInt();
+ maximumApfProgramSize = in.readInt();
+ apfPacketFormat = in.readInt();
+ }
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(apfVersionSupported);
+ dest.writeInt(maximumApfProgramSize);
+ dest.writeInt(apfPacketFormat);
+ }
+
+ public static final Creator<ApfCapabilities> CREATOR = new Creator<ApfCapabilities>() {
+ @Override
+ public ApfCapabilities createFromParcel(Parcel in) {
+ return new ApfCapabilities(in);
+ }
+
+ @Override
+ public ApfCapabilities[] newArray(int size) {
+ return new ApfCapabilities[size];
+ }
+ };
+
@Override
public String toString() {
return String.format("%s{version: %d, maxSize: %d, format: %d}", getClass().getSimpleName(),
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 6432c24..b82e517 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -935,12 +935,16 @@
}
/**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
+ * Returns the value associated with the given key, or {@code null} if
+ * no mapping of the desired type exists for the given key or a {@code null}
* value is explicitly associated with the key.
*
- * @param key a String, or null
- * @return a Parcelable value, or null
+ * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+ * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+ * Otherwise, this method might throw an exception or return {@code null}.
+ *
+ * @param key a String, or {@code null}
+ * @return a Parcelable value, or {@code null}
*/
@Nullable
public <T extends Parcelable> T getParcelable(@Nullable String key) {
@@ -958,12 +962,16 @@
}
/**
- * Returns the value associated with the given key, or null if
+ * Returns the value associated with the given key, or {@code null} if
* no mapping of the desired type exists for the given key or a null
* value is explicitly associated with the key.
*
- * @param key a String, or null
- * @return a Parcelable[] value, or null
+ * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+ * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+ * Otherwise, this method might throw an exception or return {@code null}.
+ *
+ * @param key a String, or {@code null}
+ * @return a Parcelable[] value, or {@code null}
*/
@Nullable
public Parcelable[] getParcelableArray(@Nullable String key) {
@@ -981,12 +989,16 @@
}
/**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
+ * Returns the value associated with the given key, or {@code null} if
+ * no mapping of the desired type exists for the given key or a {@code null}
* value is explicitly associated with the key.
*
- * @param key a String, or null
- * @return an ArrayList<T> value, or null
+ * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+ * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+ * Otherwise, this method might throw an exception or return {@code null}.
+ *
+ * @param key a String, or {@code null}
+ * @return an ArrayList<T> value, or {@code null}
*/
@Nullable
public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 7bd0fe2..7fc07b0 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -225,6 +225,14 @@
public static final String NAMESPACE_SCHEDULER = "scheduler";
/**
+ * Namespace for storage-related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_STORAGE = "storage";
+
+ /**
* Namespace for System UI related features.
*
* @hide
@@ -294,23 +302,6 @@
String RAMPING_RINGER_VIBRATION_DURATION = "ramping_ringer_vibration_duration";
}
- /**
- * Namespace for storage-related features.
- *
- * @hide
- */
- @SystemApi
- public interface Storage {
- String NAMESPACE = "storage";
-
- /**
- * If {@code 1}, enables the isolated storage feature. If {@code -1},
- * disables the isolated storage feature. If {@code 0}, uses the default
- * value from the build system.
- */
- String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
- }
-
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertyChangedListener, Pair<String, Executor>> sSingleListeners =
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index e931826..8f772d4 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -34,7 +34,6 @@
import android.graphics.fonts.FontStyle;
import android.graphics.fonts.FontVariationAxis;
import android.net.Uri;
-import android.os.Build.VERSION_CODES;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.HandlerThread;
@@ -652,17 +651,12 @@
if (familyBuilder == null) {
familyBuilder = new FontFamily.Builder(font);
} else {
- try {
- familyBuilder.addFont(font);
- } catch (IllegalArgumentException e) {
- if (context.getApplicationInfo().targetSdkVersion <= VERSION_CODES.P) {
- // Surpress the IllegalArgumentException for keeping the backward
- // compatibility.
- continue;
- }
- throw e;
- }
+ familyBuilder.addFont(font);
}
+ } catch (IllegalArgumentException e) {
+ // To be a compatible behavior with API28 or before, catch IllegalArgumentExcetpion
+ // thrown by native code and returns null.
+ return null;
} catch (IOException e) {
continue;
}
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index 1549c45..d505f02 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -25,6 +25,7 @@
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
+import android.util.Log;
/**
* Base class for a search indexable provider. Such provider offers data to be indexed either
@@ -112,19 +113,26 @@
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
- switch (mMatcher.match(uri)) {
- case MATCH_RES_CODE:
- return queryXmlResources(null);
- case MATCH_RAW_CODE:
- return queryRawData(null);
- case MATCH_NON_INDEXABLE_KEYS_CODE:
- return queryNonIndexableKeys(null);
- case MATCH_SITE_MAP_PAIRS_CODE:
- return querySiteMapPairs();
- case MATCH_SLICE_URI_PAIRS_CODE:
- return querySliceUriPairs();
- default:
- throw new UnsupportedOperationException("Unknown Uri " + uri);
+ try {
+ switch (mMatcher.match(uri)) {
+ case MATCH_RES_CODE:
+ return queryXmlResources(null);
+ case MATCH_RAW_CODE:
+ return queryRawData(null);
+ case MATCH_NON_INDEXABLE_KEYS_CODE:
+ return queryNonIndexableKeys(null);
+ case MATCH_SITE_MAP_PAIRS_CODE:
+ return querySiteMapPairs();
+ case MATCH_SLICE_URI_PAIRS_CODE:
+ return querySliceUriPairs();
+ default:
+ throw new UnsupportedOperationException("Unknown Uri " + uri);
+ }
+ } catch (UnsupportedOperationException e) {
+ throw e;
+ } catch (Exception e) {
+ Log.e(TAG, "Provider querying exception:", e);
+ return null;
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dd38373..63235a1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5607,6 +5607,7 @@
*
* @hide
*/
+ @SystemApi
public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
private static final Validator ODI_CAPTIONS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
@@ -12598,8 +12599,9 @@
/**
* Battery level [1-100] at which low power mode automatically turns on.
- * Pre-Q If 0, it will not automatically turn on. Q and newer it will only automatically
- * turn on if the {@link #AUTOMATIC_POWER_SAVER_MODE} setting is also set to
+ * If 0, it will not automatically turn on. For Q and newer, it will only automatically
+ * turn on if the value is greater than 0 and the {@link #AUTOMATIC_POWER_SAVER_MODE}
+ * setting is also set to
* {@link android.os.PowerManager.AutoPowerSaverMode#POWER_SAVER_MODE_PERCENTAGE}.
*
* @see #AUTOMATIC_POWER_SAVER_MODE
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 34ced17..96b861b 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -427,7 +427,7 @@
mFocusedValue = focusedValue;
if (mCallback != null) {
try {
- if (mCallback.isCompleted()) {
+ if (!mCallback.isCompleted()) {
mCallback.cancel();
}
} catch (RemoteException e) {
diff --git a/core/java/android/service/contentcapture/ActivityEvent.java b/core/java/android/service/contentcapture/ActivityEvent.java
index 5407c1d..fc781c2 100644
--- a/core/java/android/service/contentcapture/ActivityEvent.java
+++ b/core/java/android/service/contentcapture/ActivityEvent.java
@@ -52,11 +52,17 @@
*/
public static final int TYPE_ACTIVITY_STOPPED = Event.ACTIVITY_STOPPED;
+ /**
+ * The activity was destroyed.
+ */
+ public static final int TYPE_ACTIVITY_DESTROYED = Event.ACTIVITY_DESTROYED;
+
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_ACTIVITY_RESUMED,
TYPE_ACTIVITY_PAUSED,
- TYPE_ACTIVITY_STOPPED
+ TYPE_ACTIVITY_STOPPED,
+ TYPE_ACTIVITY_DESTROYED
})
@Retention(RetentionPolicy.SOURCE)
public @interface ActivityEventType{}
@@ -81,7 +87,8 @@
/**
* Gets the event type.
*
- * @return either {@link #TYPE_ACTIVITY_RESUMED} or {@value #TYPE_ACTIVITY_PAUSED}.
+ * @return either {@link #TYPE_ACTIVITY_RESUMED}, {@value #TYPE_ACTIVITY_PAUSED},
+ * {@value #TYPE_ACTIVITY_STOPPED}, or {@value #TYPE_ACTIVITY_DESTROYED}.
*/
@ActivityEventType
public int getEventType() {
@@ -97,6 +104,8 @@
return "ACTIVITY_PAUSED";
case TYPE_ACTIVITY_STOPPED:
return "ACTIVITY_STOPPED";
+ case TYPE_ACTIVITY_DESTROYED:
+ return "ACTIVITY_DESTROYED";
default:
return "UKNOWN_TYPE: " + type;
}
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 6f4114d..df11397 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -78,6 +78,21 @@
public static final String SERVICE_INTERFACE =
"android.service.contentcapture.ContentCaptureService";
+ /**
+ * Name under which a ContentCaptureService component publishes information about itself.
+ *
+ * <p>This meta-data should reference an XML resource containing a
+ * <code><{@link
+ * android.R.styleable#ContentCaptureService content-capture-service}></code> tag.
+ *
+ * <p>This is a a sample XML file configuring a ContentCaptureService:
+ * <pre> <content-capture-service
+ * android:settingsActivity="foo.bar.SettingsActivity"
+ * . . .
+ * /></pre>
+ */
+ public static final String SERVICE_META_DATA = "android.content_capture";
+
private Handler mHandler;
private IContentCaptureServiceCallback mCallback;
diff --git a/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java b/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java
new file mode 100644
index 0000000..6ecd82f
--- /dev/null
+++ b/core/java/android/service/contentcapture/ContentCaptureServiceInfo.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 android.service.contentcapture;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * {@link ServiceInfo} and meta-data about an {@link ContentCaptureService}.
+ *
+ * @hide
+ */
+public final class ContentCaptureServiceInfo {
+
+ private static final String TAG = ContentCaptureServiceInfo.class.getSimpleName();
+ private static final String XML_TAG_SERVICE = "content-capture-service";
+
+ private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, boolean isTemp,
+ @UserIdInt int userId) throws PackageManager.NameNotFoundException {
+ int flags = PackageManager.GET_META_DATA;
+ if (!isTemp) {
+ flags |= PackageManager.MATCH_SYSTEM_ONLY;
+ }
+
+ ServiceInfo si = null;
+ try {
+ si = AppGlobals.getPackageManager().getServiceInfo(comp, flags, userId);
+ } catch (RemoteException e) {
+ }
+ if (si == null) {
+ throw new NameNotFoundException("Could not get serviceInfo for "
+ + (isTemp ? " (temp)" : "(default system)")
+ + " " + comp.flattenToShortString());
+ }
+ return si;
+ }
+
+ @NonNull
+ private final ServiceInfo mServiceInfo;
+
+ @Nullable
+ private final String mSettingsActivity;
+
+ public ContentCaptureServiceInfo(@NonNull Context context, @NonNull ComponentName comp,
+ boolean isTemporaryService, @UserIdInt int userId)
+ throws PackageManager.NameNotFoundException {
+ this(context, getServiceInfoOrThrow(comp, isTemporaryService, userId));
+ }
+
+ private ContentCaptureServiceInfo(@NonNull Context context, @NonNull ServiceInfo si) {
+ // Check for permissions.
+ if (!Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE.equals(si.permission)) {
+ Slog.w(TAG, "ContentCaptureService from '" + si.packageName
+ + "' does not require permission "
+ + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
+ throw new SecurityException("Service does not require permission "
+ + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
+ }
+
+ mServiceInfo = si;
+
+ // Get the metadata, if declared.
+ final XmlResourceParser parser = si.loadXmlMetaData(context.getPackageManager(),
+ ContentCaptureService.SERVICE_META_DATA);
+ if (parser == null) {
+ mSettingsActivity = null;
+ return;
+ }
+
+ String settingsActivity = null;
+
+ try {
+ final Resources resources = context.getPackageManager().getResourcesForApplication(
+ si.applicationInfo);
+
+ int type = 0;
+ while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+ type = parser.next();
+ }
+
+ if (XML_TAG_SERVICE.equals(parser.getName())) {
+ final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+ TypedArray afsAttributes = null;
+ try {
+ afsAttributes = resources.obtainAttributes(allAttributes,
+ com.android.internal.R.styleable.ContentCaptureService);
+ settingsActivity = afsAttributes.getString(
+ R.styleable.ContentCaptureService_settingsActivity);
+ } finally {
+ if (afsAttributes != null) {
+ afsAttributes.recycle();
+ }
+ }
+ } else {
+ Log.e(TAG, "Meta-data does not start with content-capture-service tag");
+ }
+ } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
+ Log.e(TAG, "Error parsing auto fill service meta-data", e);
+ }
+
+ mSettingsActivity = settingsActivity;
+ }
+
+ public ServiceInfo getServiceInfo() {
+ return mServiceInfo;
+ }
+
+ @Nullable
+ public String getSettingsActivity() {
+ return mSettingsActivity;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(getClass().getSimpleName());
+ builder.append("[").append(mServiceInfo);
+ builder.append(", settings:").append(mSettingsActivity);
+ return builder.toString();
+ }
+
+ /**
+ * Dumps it!
+ */
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix);
+ pw.print("Component: ");
+ pw.println(getServiceInfo().getComponentName());
+ pw.print(prefix);
+ pw.print("Settings: ");
+ pw.println(mSettingsActivity);
+ }
+}
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index cc74e1a..8512a0b 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -292,9 +292,9 @@
return uid;
}
- /** The package that posted the notification.
- *<p>
- * Might be different from {@link #getPackageName()} if the app owning the notification has
+ /**
+ * The package that posted the notification.
+ * <p> Might be different from {@link #getPackageName()} if the app owning the notification has
* a {@link NotificationManager#setNotificationDelegate(String) notification delegate}.
*/
public @NonNull String getOpPkg() {
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 83f14d1..100774c 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -536,7 +536,7 @@
* or playing back a file. The value should be one of the STREAM_ constants
* defined in {@link AudioManager}.
*
- * @see TextToSpeech#speak(String, int, HashMap)
+ * @see TextToSpeech#speak(CharSequence, int, Bundle, String)
* @see TextToSpeech#playEarcon(String, int, HashMap)
*/
public static final String KEY_PARAM_STREAM = "streamType";
@@ -546,7 +546,7 @@
* speaking text or playing back a file. The value should be set
* using {@link TextToSpeech#setAudioAttributes(AudioAttributes)}.
*
- * @see TextToSpeech#speak(String, int, HashMap)
+ * @see TextToSpeech#speak(CharSequence, int, Bundle, String)
* @see TextToSpeech#playEarcon(String, int, HashMap)
* @hide
*/
@@ -557,7 +557,7 @@
* {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
* spoken, a file has been played back or a silence duration has elapsed.
*
- * @see TextToSpeech#speak(String, int, HashMap)
+ * @see TextToSpeech#speak(CharSequence, int, Bundle, String)
* @see TextToSpeech#playEarcon(String, int, HashMap)
* @see TextToSpeech#synthesizeToFile(String, HashMap, String)
*/
@@ -568,7 +568,7 @@
* volume used when speaking text. Volume is specified as a float ranging from 0 to 1
* where 0 is silence, and 1 is the maximum volume (the default behavior).
*
- * @see TextToSpeech#speak(String, int, HashMap)
+ * @see TextToSpeech#speak(CharSequence, int, Bundle, String)
* @see TextToSpeech#playEarcon(String, int, HashMap)
*/
public static final String KEY_PARAM_VOLUME = "volume";
@@ -578,7 +578,7 @@
* Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan,
* 0 to center (the default behavior), and +1 to hard-right.
*
- * @see TextToSpeech#speak(String, int, HashMap)
+ * @see TextToSpeech#speak(CharSequence, int, Bundle, String)
* @see TextToSpeech#playEarcon(String, int, HashMap)
*/
public static final String KEY_PARAM_PAN = "pan";
@@ -589,7 +589,7 @@
* as per {@link TextToSpeech#getFeatures(Locale)}, the engine must
* use network based synthesis.
*
- * @see TextToSpeech#speak(String, int, java.util.HashMap)
+ * @see TextToSpeech#speak(CharSequence, int, Bundle, String)
* @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)
* @see TextToSpeech#getFeatures(java.util.Locale)
*
@@ -607,7 +607,7 @@
* as per {@link TextToSpeech#getFeatures(Locale)}, the engine must synthesize
* text on-device (without making network requests).
*
- * @see TextToSpeech#speak(String, int, java.util.HashMap)
+ * @see TextToSpeech#speak(CharSequence, int, Bundle, String)
* @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)
* @see TextToSpeech#getFeatures(java.util.Locale)
@@ -625,7 +625,7 @@
* output. It can be used to associate one of the {@link android.media.audiofx.AudioEffect}
* objects with the synthesis (or earcon) output.
*
- * @see TextToSpeech#speak(String, int, HashMap)
+ * @see TextToSpeech#speak(CharSequence, int, Bundle, String)
* @see TextToSpeech#playEarcon(String, int, HashMap)
*/
public static final String KEY_PARAM_SESSION_ID = "sessionId";
@@ -881,7 +881,7 @@
/**
* Adds a mapping between a string of text and a sound resource in a
* package. After a call to this method, subsequent calls to
- * {@link #speak(String, int, HashMap)} will play the specified sound resource
+ * {@link #speak(CharSequence, int, Bundle, String)} will play the specified sound resource
* if it is available, or synthesize the text it is missing.
*
* @param text
@@ -915,7 +915,7 @@
/**
* Adds a mapping between a CharSequence (may be spanned with TtsSpans) of text
* and a sound resource in a package. After a call to this method, subsequent calls to
- * {@link #speak(String, int, HashMap)} will play the specified sound resource
+ * {@link #speak(CharSequence, int, Bundle, String)} will play the specified sound resource
* if it is available, or synthesize the text it is missing.
*
* @param text
@@ -947,11 +947,10 @@
}
/**
- * Adds a mapping between a string of text and a sound file. Using this, it
- * is possible to add custom pronounciations for a string of text.
- * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
- * will play the specified sound resource if it is available, or synthesize the text it is
- * missing.
+ * Adds a mapping between a string of text and a sound file. Using this, it is possible to
+ * add custom pronounciations for a string of text. After a call to this method, subsequent
+ * calls to {@link #speak(CharSequence, int, Bundle, String)} will play the specified sound
+ * resource if it is available, or synthesize the text it is missing.
*
* @param text
* The string of text. Example: <code>"south_south_east"</code>
@@ -970,8 +969,8 @@
/**
* Adds a mapping between a CharSequence (may be spanned with TtsSpans and a sound file.
- * Using this, it is possible to add custom pronounciations for a string of text.
- * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
+ * Using this, it is possible to add custom pronounciations for a string of text. After a call
+ * to this method, subsequent calls to {@link #speak(CharSequence, int, Bundle, String)}
* will play the specified sound resource if it is available, or synthesize the text it is
* missing.
*
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 7fb0f95..610cf2c 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -70,7 +70,15 @@
* Default implementation of the {@link LineHeightSpan}, which changes the line height of the
* attached paragraph.
* <p>
- * LineHeightSpan will change the line height of the entire paragraph, even though it
+ * For example, a paragraph with its line height equal to 100px can be set like this:
+ * <pre>
+ * SpannableString string = new SpannableString("This is a multiline paragraph. This is a multiline paragraph.");
+ * string.setSpan(new LineHeightSpan.Standard(100), 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/lineheightspan.png" />
+ * <figcaption>Text with line height set to 100 pixels.</figcaption>
+ * <p>
+ * Notice that LineHeightSpan will change the line height of the entire paragraph, even though it
* covers only part of the paragraph.
* </p>
*/
diff --git a/core/java/android/view/ISystemGestureExclusionListener.aidl b/core/java/android/view/ISystemGestureExclusionListener.aidl
new file mode 100644
index 0000000..a032625
--- /dev/null
+++ b/core/java/android/view/ISystemGestureExclusionListener.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Region;
+
+/**
+ * Listener for changes to the system gesture exclusion region
+ *
+ * {@hide}
+ */
+oneway interface ISystemGestureExclusionListener {
+ /**
+ * Called when the system gesture exclusion for the given display changed.
+ * @param displayId the display whose system gesture exclusion changed
+ * @param systemGestureExclusion a {@code Region} where the app would like priority over the
+ * system gestures, in display coordinates.
+ */
+ void onSystemGestureExclusionChanged(int displayId, in Region systemGestureExclusion);
+}
\ No newline at end of file
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9ee6585..b91b93f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -40,6 +40,7 @@
import android.view.IPinnedStackListener;
import android.view.RemoteAnimationAdapter;
import android.view.IRotationWatcher;
+import android.view.ISystemGestureExclusionListener;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindowSession;
import android.view.IWindowSessionCallback;
@@ -281,6 +282,18 @@
int displayId);
/**
+ * Registers a system gesture exclusion listener for a given display.
+ */
+ void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener,
+ int displayId);
+
+ /**
+ * Unregisters a system gesture exclusion listener for a given display.
+ */
+ void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener,
+ int displayId);
+
+ /**
* Used only for assist -- request a screenshot of the current application.
*/
boolean requestAssistScreenshot(IAssistDataReceiver receiver);
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 1fcd432..87efb3f 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -32,6 +32,8 @@
import android.view.Surface;
import android.view.SurfaceControl;
+import java.util.List;
+
/**
* System private per-application interface to the window manager.
*
@@ -265,4 +267,10 @@
* that new state.
*/
void insetsModified(IWindow window, in InsetsState state);
+
+
+ /**
+ * Called when the system gesture exclusion has changed.
+ */
+ void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ab2cc66..bf0f4e2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11095,11 +11095,10 @@
* <p>Computes the coordinates of this view in its surface. The argument
* must be an array of two integers. After the method returns, the array
* contains the x and y location in that order.</p>
- * @hide
+ *
* @param location an array of two integers in which to hold the coordinates
*/
- @UnsupportedAppUsage
- public void getLocationInSurface(@Size(2) int[] location) {
+ public void getLocationInSurface(@NonNull @Size(2) int[] location) {
getLocationInWindow(location);
if (mAttachInfo != null && mAttachInfo.mViewRootImpl != null) {
location[0] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.left;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a28d662..6d04cd31 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3987,7 +3987,11 @@
void systemGestureExclusionChanged() {
final List<Rect> rectsForWindowManager = mGestureExclusionTracker.computeChangedRects();
if (rectsForWindowManager != null) {
- // TODO Send to WM
+ try {
+ mWindowSession.reportSystemGestureExclusionChanged(mWindow, rectsForWindowManager);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
mAttachInfo.mTreeObserver
.dispatchOnSystemGestureExclusionRectsChanged(rectsForWindowManager);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 019ebff..5a27e94 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -161,7 +161,7 @@
/**
* Gets the context id.
*/
- @NonNull
+ @Nullable
public LocusId getLocusId() {
return mId;
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 494eb03..a46580d 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -245,10 +245,11 @@
/**
* Used with {@link #setForceDark}
*
- * Enable force dark, dependent on the state of the WebView parent. If the WebView parent view
- * is being automatically rendered in dark mode, then WebView content will be rendered so as to
- * emulate a dark theme. WebViews that are not attached to the view hierarchy will not be
- * inverted.
+ * Enable force dark dependent on the state of the WebView parent view. If the WebView parent
+ * view is being automatically force darkened
+ * (see: {@link android.view.View#setForceDarkAllowed}), then WebView content will be rendered
+ * so as to emulate a dark theme. WebViews that are not attached to the view hierarchy will not
+ * be inverted.
*/
public static final int FORCE_DARK_AUTO = 1;
@@ -1466,6 +1467,8 @@
/**
* Set the force dark mode for this WebView.
+ *
+ * @param forceDark the force dark mode to set.
*/
public void setForceDark(@ForceDark int forceDark) {
// Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
@@ -1474,6 +1477,8 @@
/**
* Get the force dark mode for this WebView.
*
+ * The default force dark mode is {@link #FORCE_DARK_AUTO}
+ *
* @return the currently set force dark mode.
*/
public @ForceDark int getForceDark() {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b6ec5f9..c9ef038 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4984,9 +4984,6 @@
if (magnifierTopLeft == null) {
return;
}
- final Rect surfaceInsets =
- mTextView.getViewRootImpl().mWindowAttributes.surfaceInsets;
- magnifierTopLeft.offset(-surfaceInsets.left, -surfaceInsets.top);
final Rect magnifierRect = new Rect(magnifierTopLeft.x, magnifierTopLeft.y,
magnifierTopLeft.x + mMagnifierAnimator.mMagnifier.getWidth(),
magnifierTopLeft.y + mMagnifierAnimator.mMagnifier.getHeight());
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 50e8836..08799cf 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -471,13 +471,13 @@
}
/**
- * Returns the top left coordinates of the magnifier, relative to the surface of the
- * main application window. They will be determined by the coordinates of the last
- * {@link #show(float, float)} or {@link #show(float, float, float, float)} call, adjusted
- * to take into account any potential clamping behavior. The method can be used immediately
- * after a #show call to find out where the magnifier will be positioned. However, the
- * position of the magnifier will not be updated in the same frame due to the async
- * copying of the content copying and of the magnifier rendering.
+ * Returns the top left coordinates of the magnifier, relative to the main application
+ * window. They will be determined by the coordinates of the last {@link #show(float, float)}
+ * or {@link #show(float, float, float, float)} call, adjusted to take into account any
+ * potential clamping behavior. The method can be used immediately after a #show
+ * call to find out where the magnifier will be positioned. However, the position of the
+ * magnifier will not be updated visually in the same frame, due to the async nature of
+ * the content copying and of the magnifier rendering.
* The method will return {@code null} if #show has not yet been called, or if the last
* operation performed was a #dismiss.
*
@@ -488,15 +488,18 @@
if (mWindow == null) {
return null;
}
- return new Point(getCurrentClampedWindowCoordinates());
+ final Point position = getCurrentClampedWindowCoordinates();
+ position.offset(-mParentSurface.mInsets.left, -mParentSurface.mInsets.top);
+ return new Point(position);
}
/**
* Returns the top left coordinates of the magnifier source (i.e. the view region going to
- * be magnified and copied to the magnifier), relative to the surface the content is copied
- * from. The content will be copied:
+ * be magnified and copied to the magnifier), relative to the window or surface the content
+ * is copied from. The content will be copied:
* - if the magnified view is a {@link SurfaceView}, from the surface backing it
- * - otherwise, from the surface of the main application window
+ * - otherwise, from the surface backing the main application window, and the coordinates
+ * returned will be relative to the main application window
* The method will return {@code null} if #show has not yet been called, or if the last
* operation performed was a #dismiss.
*
@@ -507,7 +510,9 @@
if (mWindow == null) {
return null;
}
- return new Point(mPixelCopyRequestRect.left, mPixelCopyRequestRect.top);
+ final Point position = new Point(mPixelCopyRequestRect.left, mPixelCopyRequestRect.top);
+ position.offset(-mContentCopySurface.mInsets.left, -mContentCopySurface.mInsets.top);
+ return new Point(position);
}
/**
@@ -531,7 +536,7 @@
viewRootImpl.getHeight() + surfaceInsets.top + surfaceInsets.bottom;
validMainWindowSurface =
new SurfaceInfo(viewRootImpl.getSurfaceControl(), mainWindowSurface,
- surfaceWidth, surfaceHeight, true);
+ surfaceWidth, surfaceHeight, surfaceInsets, true);
}
}
// Get the surface backing the magnified view, if it is a SurfaceView.
@@ -544,7 +549,7 @@
if (sc != null && sc.isValid()) {
final Rect surfaceFrame = surfaceHolder.getSurfaceFrame();
validSurfaceViewSurface = new SurfaceInfo(sc, surfaceViewSurface,
- surfaceFrame.right, surfaceFrame.bottom, false);
+ surfaceFrame.right, surfaceFrame.bottom, new Rect(), false);
}
}
@@ -708,9 +713,13 @@
final Rect windowBounds;
if (mParentSurface.mIsMainWindowSurface) {
final Insets systemInsets = mView.getRootWindowInsets().getSystemWindowInsets();
- windowBounds = new Rect(systemInsets.left, systemInsets.top,
- mParentSurface.mWidth - systemInsets.right,
- mParentSurface.mHeight - systemInsets.bottom);
+ windowBounds = new Rect(
+ systemInsets.left + mParentSurface.mInsets.left,
+ systemInsets.top + mParentSurface.mInsets.top,
+ mParentSurface.mWidth - systemInsets.right - mParentSurface.mInsets.right,
+ mParentSurface.mHeight - systemInsets.bottom
+ - mParentSurface.mInsets.bottom
+ );
} else {
windowBounds = new Rect(0, 0, mParentSurface.mWidth, mParentSurface.mHeight);
}
@@ -725,21 +734,23 @@
* Contains a surface and metadata corresponding to it.
*/
private static class SurfaceInfo {
- public static final SurfaceInfo NULL = new SurfaceInfo(null, null, 0, 0, false);
+ public static final SurfaceInfo NULL = new SurfaceInfo(null, null, 0, 0, null, false);
private Surface mSurface;
private SurfaceControl mSurfaceControl;
private int mWidth;
private int mHeight;
+ private Rect mInsets;
private boolean mIsMainWindowSurface;
SurfaceInfo(final SurfaceControl surfaceControl, final Surface surface,
- final int width, final int height,
+ final int width, final int height, final Rect insets,
final boolean isMainWindowSurface) {
mSurfaceControl = surfaceControl;
mSurface = surface;
mWidth = width;
mHeight = height;
+ mInsets = insets;
mIsMainWindowSurface = isMainWindowSurface;
}
}
diff --git a/core/java/android/widget/TEST_MAPPING b/core/java/android/widget/TEST_MAPPING
new file mode 100644
index 0000000..ee378ff
--- /dev/null
+++ b/core/java/android/widget/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsWidgetTestCases",
+ "options": [
+ {
+ "instrumentation-arg": "size:=small"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 4773e16..3205b5a 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -20,12 +20,16 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
+import android.graphics.Path;
import android.graphics.RectF;
+import android.graphics.Region;
import android.hardware.input.InputManager;
import android.hardware.input.InputManager.InputDeviceListener;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
+import android.view.ISystemGestureExclusionListener;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -34,6 +38,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
+import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import java.util.ArrayList;
@@ -124,12 +129,16 @@
private int mActivePointerId;
private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
private final PointerCoords mTempCoords = new PointerCoords();
-
+
+ private final Region mSystemGestureExclusion = new Region();
+ private final Path mSystemGestureExclusionPath = new Path();
+ private final Paint mSystemGestureExclusionPaint;
+
private final VelocityTracker mVelocity;
private final VelocityTracker mAltVelocity;
private final FasterStringBuilder mText = new FasterStringBuilder();
-
+
private boolean mPrintCoords = true;
public PointerLocationView(Context c) {
@@ -168,7 +177,11 @@
mPathPaint.setARGB(255, 0, 96, 255);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(1);
-
+
+ mSystemGestureExclusionPaint = new Paint();
+ mSystemGestureExclusionPaint.setARGB(25, 255, 0, 0);
+ mSystemGestureExclusionPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+
PointerState ps = new PointerState();
mPointers.add(ps);
mActivePointerId = 0;
@@ -236,6 +249,12 @@
final int NP = mPointers.size();
+ if (!mSystemGestureExclusion.isEmpty()) {
+ mSystemGestureExclusionPath.reset();
+ mSystemGestureExclusion.getBoundaryPath(mSystemGestureExclusionPath);
+ canvas.drawPath(mSystemGestureExclusionPath, mSystemGestureExclusionPaint);
+ }
+
// Labels
if (mActivePointerId >= 0) {
final PointerState ps = mPointers.get(mActivePointerId);
@@ -719,6 +738,12 @@
super.onAttachedToWindow();
mIm.registerInputDeviceListener(this, getHandler());
+ try {
+ WindowManagerGlobal.getWindowManagerService().registerSystemGestureExclusionListener(
+ mSystemGestureExclusionListener, mContext.getDisplayId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
logInputDevices();
}
@@ -727,6 +752,12 @@
super.onDetachedFromWindow();
mIm.unregisterInputDeviceListener(this);
+ try {
+ WindowManagerGlobal.getWindowManagerService().unregisterSystemGestureExclusionListener(
+ mSystemGestureExclusionListener, mContext.getDisplayId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
@Override
@@ -876,4 +907,17 @@
return oldLength;
}
}
+
+ private ISystemGestureExclusionListener mSystemGestureExclusionListener =
+ new ISystemGestureExclusionListener.Stub() {
+ @Override
+ public void onSystemGestureExclusionChanged(int displayId, Region systemGestureExclusion) {
+ Region exclusion = Region.obtain(systemGestureExclusion);
+ getHandler().post(() -> {
+ mSystemGestureExclusion.set(exclusion);
+ exclusion.recycle();
+ invalidate();
+ });
+ }
+ };
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 7ad9223..f037407 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2305,4 +2305,8 @@
// Open: Settings > Special App Access > Do not disturb control for app
ZEN_ACCESS_DETAIL = 1692;
+
+ // OPEN: Settings > Face > Remove face
+ // OS: Q
+ DIALOG_FACE_REMOVE = 1693;
}
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index c08d7ca..4ef26dd5 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -66,6 +66,10 @@
optional bool can_show_light = 8;
optional string group_key = 9 [ (.android.privacy).dest = DEST_EXPLICIT ];
optional sint32 importance = 10;
+ // The package the notification was posted for.
+ optional string package = 11;
+ // The package that posted the notification. It might not be the same as package.
+ optional string delegate_package = 12;
}
message ListenersDisablingEffectsProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c7417bf..efc6ad0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3929,6 +3929,10 @@
<permission android:name="android.permission.MANAGE_ROLLBACKS"
android:protectionLevel="signature|verifier" />
+ <!-- @SystemApi @TestApi @hide Allows testing apk level rollbacks. -->
+ <permission android:name="android.permission.TEST_MANAGE_ROLLBACKS"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi @hide Allows an application to mark other applications as harmful -->
<permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS"
android:protectionLevel="signature|verifier" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 743496f..9d48fe3 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8108,7 +8108,7 @@
<!-- Use <code>autofill-service</code> as the root tag of the XML resource that describes a
{@link android.service.autofill.AutofillService}, which is referenced from its
- {@link android.service.autofill#SERVICE_META_DATA} meta-data entry.
+ {@link android.service.autofill.AutofillService#SERVICE_META_DATA} meta-data entry.
-->
<declare-styleable name="AutofillService">
<!-- Fully qualified class name of an activity that allows the user to modify
@@ -8134,6 +8134,23 @@
</declare-styleable>
<!-- =============================== -->
+ <!-- Content Capture attributes -->
+ <!-- =============================== -->
+ <eat-comment />
+
+ <!-- Use <code>content-capture-service</code> as the root tag of the XML resource that describes
+ a {@link android.service.contentcapture.ContentCaptureService}, which is referenced from
+ its {@link android.service.contentcapture.ContentCaptureService#SERVICE_META_DATA}
+ meta-data entry.
+ @hide @SystemApi
+ -->
+ <declare-styleable name="ContentCaptureService">
+ <!-- Fully qualified class name of an activity that allows the user to modify
+ the settings for this service. -->
+ <attr name="settingsActivity" />
+ </declare-styleable>
+
+ <!-- =============================== -->
<!-- Contacts meta-data attributes -->
<!-- =============================== -->
<eat-comment />
diff --git a/core/tests/overlaytests/device/assets/package-name.txt b/core/tests/overlaytests/device/assets/package-name.txt
new file mode 100644
index 0000000..8094438
--- /dev/null
+++ b/core/tests/overlaytests/device/assets/package-name.txt
@@ -0,0 +1 @@
+com.android.overlaytest
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index 91fcdbb..f86743a 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -17,9 +17,12 @@
package com.android.overlaytest;
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 android.app.UiAutomation;
+import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -30,6 +33,8 @@
import androidx.test.InstrumentationRegistry;
+import com.android.internal.util.ArrayUtils;
+
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -291,6 +296,33 @@
assertEquals(getExpected(no, so, mo), actual);
}
+ @Test
+ public void testAssetsNotPossibleToOverlay() throws Throwable {
+ final AssetManager am = mResources.getAssets();
+
+ // AssetManager#list will include assets from all loaded non-overlay
+ // APKs, including the framework; framework-res.apk contains at least
+ // assets/{images,webkit}. Rather than checking the list, verify that
+ // assets only present in overlays are never part of the list.
+ String[] files = am.list("");
+ assertTrue(ArrayUtils.contains(files, "package-name.txt"));
+ assertFalse(ArrayUtils.contains(files, "foo.txt"));
+ assertFalse(ArrayUtils.contains(files, "bar.txt"));
+
+ String contents = null;
+ try (InputStream is = am.open("package-name.txt")) {
+ final BufferedReader reader = new BufferedReader(
+ new InputStreamReader(is, StandardCharsets.UTF_8));
+ StringBuilder str = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ str.append(line);
+ }
+ contents = str.toString();
+ }
+ assertEquals("com.android.overlaytest", contents);
+ }
+
/*
* testMatrix* tests
*
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/assets/foo.txt b/core/tests/overlaytests/device/test-apps/AppOverlayOne/assets/foo.txt
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/assets/foo.txt
@@ -0,0 +1 @@
+foo
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/assets/package-name.txt b/core/tests/overlaytests/device/test-apps/AppOverlayOne/assets/package-name.txt
new file mode 100644
index 0000000..087cb96
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/assets/package-name.txt
@@ -0,0 +1 @@
+com.android.overlaytest.app_overlay_one
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/assets/bar.txt b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/assets/bar.txt
new file mode 100644
index 0000000..5716ca5
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/assets/bar.txt
@@ -0,0 +1 @@
+bar
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/assets/package-name.txt b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/assets/package-name.txt
new file mode 100644
index 0000000..1318565
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/assets/package-name.txt
@@ -0,0 +1 @@
+com.android.overlaytest.app_overlay_two
diff --git a/docs/html/reference/images/text/style/lineheightspan.png b/docs/html/reference/images/text/style/lineheightspan.png
new file mode 100644
index 0000000..18f5753
--- /dev/null
+++ b/docs/html/reference/images/text/style/lineheightspan.png
Binary files differ
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 0d52338..9c4b5e8 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1809,6 +1809,45 @@
}
/**
+ * <p>Computes the chromaticity coordinates of a specified correlated color
+ * temperature (CCT) on the Planckian locus. The specified CCT must be
+ * greater than 0. A meaningful CCT range is [1667, 25000].</p>
+ *
+ * <p>The transform is computed using the methods in Kang et
+ * al., <i>Design of Advanced Color - Temperature Control System for HDTV
+ * Applications</i>, Journal of Korean Physical Society 41, 865-871
+ * (2002).</p>
+ *
+ * @param cct The correlated color temperature, in Kelvin
+ * @return Corresponding XYZ values
+ * @throws IllegalArgumentException If cct is invalid
+ *
+ * @hide
+ */
+ @NonNull
+ @Size(3)
+ public static float[] cctToXyz(@IntRange(from = 1) int cct) {
+ if (cct < 1) {
+ throw new IllegalArgumentException("Temperature must be greater than 0");
+ }
+
+ final float icct = 1e3f / cct;
+ final float icct2 = icct * icct;
+ final float x = cct <= 4000.0f ?
+ 0.179910f + 0.8776956f * icct - 0.2343589f * icct2 - 0.2661239f * icct2 * icct :
+ 0.240390f + 0.2226347f * icct + 2.1070379f * icct2 - 3.0258469f * icct2 * icct;
+
+ final float x2 = x * x;
+ final float y = cct <= 2222.0f ?
+ -0.20219683f + 2.18555832f * x - 1.34811020f * x2 - 1.1063814f * x2 * x :
+ cct <= 4000.0f ?
+ -0.16748867f + 2.09137015f * x - 1.37418593f * x2 - 0.9549476f * x2 * x :
+ -0.37001483f + 3.75112997f * x - 5.8733867f * x2 + 3.0817580f * x2 * x;
+
+ return xyYToXyz(new float[] {x, y});
+ }
+
+ /**
* <p>Computes the chromaticity coordinates of a CIE series D illuminant
* from the specified correlated color temperature (CCT). The specified CCT
* must be greater than 0. A meaningful CCT range is [4000, 25000].</p>
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 9995f1e..c8b361b 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -33,7 +33,6 @@
import android.graphics.fonts.FontVariationAxis;
import android.graphics.fonts.SystemFonts;
import android.os.Build;
-import android.os.Build.VERSION_CODES;
import android.os.ParcelFileDescriptor;
import android.provider.FontRequest;
import android.provider.FontsContract;
@@ -48,7 +47,6 @@
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
-import dalvik.system.VMRuntime;
import libcore.util.NativeAllocationRegistry;
@@ -266,16 +264,7 @@
if (familyBuilder == null) {
familyBuilder = new FontFamily.Builder(fontBuilder.build());
} else {
- try {
- familyBuilder.addFont(fontBuilder.build());
- } catch (IllegalArgumentException e) {
- if (VMRuntime.getRuntime().getTargetSdkVersion() <= VERSION_CODES.P) {
- // Surpress the IllegalArgumentException for keeping the backward
- // compatibility.
- continue;
- }
- throw e;
- }
+ familyBuilder.addFont(fontBuilder.build());
}
}
if (familyBuilder == null) {
@@ -297,6 +286,10 @@
typeface = new Typeface.CustomFallbackBuilder(family)
.setStyle(bestFont.getStyle())
.build();
+ } catch (IllegalArgumentException e) {
+ // To be a compatible behavior with API28 or before, catch IllegalArgumentExcetpion
+ // thrown by native code and returns null.
+ return null;
} catch (IOException e) {
typeface = Typeface.DEFAULT;
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index d74f27c..9519704 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -310,6 +310,9 @@
// Start from the back.
for (auto iter = apk_assets_.rbegin(); iter != apk_assets_.rend(); ++iter) {
const ApkAssets* apk_assets = *iter;
+ if (apk_assets->IsOverlay()) {
+ continue;
+ }
auto func = [&](const StringPiece& name, FileType type) {
AssetDir::FileInfo info;
@@ -336,6 +339,13 @@
Asset::AccessMode mode,
ApkAssetsCookie* out_cookie) const {
for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
+ // Prevent RRO from modifying assets and other entries accessed by file
+ // path. Explicitly asking for a path in a given package (denoted by a
+ // cookie) is still OK.
+ if (apk_assets_[i]->IsOverlay()) {
+ continue;
+ }
+
std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
if (asset) {
if (out_cookie != nullptr) {
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index db2d038..35bbb58 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -80,6 +80,10 @@
return loaded_arsc_.get();
}
+ inline bool IsOverlay() const {
+ return idmap_asset_.get() != nullptr;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(ApkAssets);
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 9298be6..5f43b48 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -41,6 +41,7 @@
namespace android {
class Bitmap;
+class AutoBackendTextureRelease;
namespace uirenderer {
@@ -135,6 +136,7 @@
friend class DispatchFrameCallbacks;
friend class RenderProxy;
friend class DummyVsyncSource;
+ friend class android::AutoBackendTextureRelease;
friend class android::uirenderer::TestUtils;
friend class android::uirenderer::WebViewFunctor;
friend class android::uirenderer::skiapipeline::VkFunctorDrawHandler;
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 077a8f7..65d95ad 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -24,13 +24,17 @@
#include "renderthread/VulkanManager.h"
#include "utils/Color.h"
#include <GrAHardwareBufferUtils.h>
+#include <GrBackendSurface.h>
// Macro for including the SurfaceTexture name in log messages
#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+using namespace android::uirenderer::renderthread;
+
namespace android {
void ImageConsumer::onFreeBufferLocked(int slotIndex) {
+ // This callback may be invoked on any thread.
mImageSlots[slotIndex].clear();
}
@@ -46,55 +50,141 @@
mImageSlots[buf].eglFence() = EGL_NO_SYNC_KHR;
}
+/**
+ * AutoBackendTextureRelease manages EglImage/VkImage lifetime. It is a ref-counted object
+ * that keeps GPU resources alive until the last SKImage object using them is destroyed.
+ */
+class AutoBackendTextureRelease {
+public:
+ static void releaseProc(SkImage::ReleaseContext releaseContext);
+
+ AutoBackendTextureRelease(GrContext* context, GraphicBuffer* buffer);
+
+ const GrBackendTexture& getTexture() const { return mBackendTexture; }
+
+ void ref() { mUsageCount++; }
+
+ void unref(bool releaseImage);
+
+ inline sk_sp<SkImage> getImage() { return mImage; }
+
+ void makeImage(sp<GraphicBuffer>& graphicBuffer, android_dataspace dataspace,
+ GrContext* context);
+
+private:
+ // The only way to invoke dtor is with unref, when mUsageCount is 0.
+ ~AutoBackendTextureRelease() {}
+
+ GrBackendTexture mBackendTexture;
+ GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
+ GrAHardwareBufferUtils::DeleteImageCtx mDeleteCtx;
+
+ // Starting with refcount 1, because the first ref is held by SurfaceTexture. Additional refs
+ // are held by SkImages.
+ int mUsageCount = 1;
+
+ // mImage is the SkImage created from mBackendTexture.
+ sk_sp<SkImage> mImage;
+};
+
+AutoBackendTextureRelease::AutoBackendTextureRelease(GrContext* context, GraphicBuffer* buffer) {
+ bool createProtectedImage =
+ 0 != (buffer->getUsage() & GraphicBuffer::USAGE_PROTECTED);
+ GrBackendFormat backendFormat = GrAHardwareBufferUtils::GetBackendFormat(
+ context,
+ reinterpret_cast<AHardwareBuffer*>(buffer),
+ buffer->getPixelFormat(),
+ false);
+ mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture(
+ context,
+ reinterpret_cast<AHardwareBuffer*>(buffer),
+ buffer->getWidth(),
+ buffer->getHeight(),
+ &mDeleteProc,
+ &mDeleteCtx,
+ createProtectedImage,
+ backendFormat,
+ false);
+}
+
+void AutoBackendTextureRelease::unref(bool releaseImage) {
+ if (!RenderThread::isCurrent()) {
+ // EGLImage needs to be destroyed on RenderThread to prevent memory leak.
+ // ~SkImage dtor for both pipelines needs to be invoked on RenderThread, because it is not
+ // thread safe.
+ RenderThread::getInstance().queue().post([this, releaseImage]() { unref(releaseImage); });
+ return;
+ }
+
+ if (releaseImage) {
+ mImage.reset();
+ }
+
+ mUsageCount--;
+ if (mUsageCount <= 0) {
+ if (mBackendTexture.isValid()) {
+ mDeleteProc(mDeleteCtx);
+ mBackendTexture = {};
+ }
+ delete this;
+ }
+}
+
+void AutoBackendTextureRelease::releaseProc(SkImage::ReleaseContext releaseContext) {
+ AutoBackendTextureRelease* textureRelease =
+ reinterpret_cast<AutoBackendTextureRelease*>(releaseContext);
+ textureRelease->unref(false);
+}
+
+void AutoBackendTextureRelease::makeImage(sp<GraphicBuffer>& graphicBuffer,
+ android_dataspace dataspace, GrContext* context) {
+ SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(
+ graphicBuffer->getPixelFormat());
+ mImage = SkImage::MakeFromTexture(context,
+ mBackendTexture,
+ kTopLeft_GrSurfaceOrigin,
+ colorType,
+ kPremul_SkAlphaType,
+ uirenderer::DataSpaceToColorSpace(dataspace),
+ releaseProc,
+ this);
+ if (mImage.get()) {
+ // The following ref will be counteracted by releaseProc, when SkImage is discarded.
+ ref();
+ }
+}
+
void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer,
android_dataspace dataspace, bool forceCreate,
GrContext* context) {
- if (!mImage.get() || dataspace != mDataspace || forceCreate) {
+ if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace
+ || forceCreate) {
if (!graphicBuffer.get()) {
clear();
return;
}
- if (!mBackendTexture.isValid()) {
- clear();
- bool createProtectedImage =
- 0 != (graphicBuffer->getUsage() & GraphicBuffer::USAGE_PROTECTED);
- GrBackendFormat backendFormat = GrAHardwareBufferUtils::GetBackendFormat(
- context,
- reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()),
- graphicBuffer->getPixelFormat(),
- false);
- mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture(
- context,
- reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()),
- graphicBuffer->getWidth(),
- graphicBuffer->getHeight(),
- &mDeleteProc,
- &mDeleteCtx,
- createProtectedImage,
- backendFormat,
- false);
+ if (!mTextureRelease) {
+ mTextureRelease = new AutoBackendTextureRelease(context, graphicBuffer.get());
}
+
mDataspace = dataspace;
- SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(
- graphicBuffer->getPixelFormat());
- mImage = SkImage::MakeFromTexture(context,
- mBackendTexture,
- kTopLeft_GrSurfaceOrigin,
- colorType,
- kPremul_SkAlphaType,
- uirenderer::DataSpaceToColorSpace(dataspace));
+ mTextureRelease->makeImage(graphicBuffer, dataspace, context);
}
}
void ImageConsumer::ImageSlot::clear() {
- mImage.reset();
- if (mBackendTexture.isValid()) {
- mDeleteProc(mDeleteCtx);
- mBackendTexture = {};
+ if (mTextureRelease) {
+ // The following unref counteracts the initial mUsageCount of 1, set by default initializer.
+ mTextureRelease->unref(true);
+ mTextureRelease = nullptr;
}
}
+sk_sp<SkImage> ImageConsumer::ImageSlot::getImage() {
+ return mTextureRelease ? mTextureRelease->getImage() : nullptr;
+}
+
sk_sp<SkImage> ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st,
uirenderer::RenderState& renderState) {
BufferItem item;
diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h
index eee0a0a..2fdece9 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.h
+++ b/libs/hwui/surfacetexture/ImageConsumer.h
@@ -25,7 +25,6 @@
#include <cutils/compiler.h>
#include <gui/BufferItem.h>
#include <system/graphics.h>
-#include <GrBackendSurface.h>
namespace GrAHardwareBufferUtils {
typedef void* DeleteImageCtx;
@@ -38,6 +37,7 @@
class RenderState;
}
+class AutoBackendTextureRelease;
class SurfaceTexture;
/*
@@ -81,16 +81,14 @@
void createIfNeeded(sp<GraphicBuffer> graphicBuffer, android_dataspace dataspace,
bool forceCreate, GrContext* context);
+
void clear();
inline EGLSyncKHR& eglFence() { return mEglFence; }
- inline sk_sp<SkImage> getImage() { return mImage; }
+ sk_sp<SkImage> getImage();
private:
- // mImage is the SkImage created from mGraphicBuffer.
- sk_sp<SkImage> mImage;
-
// the dataspace associated with the current image
android_dataspace mDataspace;
@@ -100,11 +98,11 @@
*/
EGLSyncKHR mEglFence;
- GrBackendTexture mBackendTexture;
-
- GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
-
- GrAHardwareBufferUtils::DeleteImageCtx mDeleteCtx;
+ /**
+ * mTextureRelease may outlive ImageConsumer, if the last ref is held by an SkImage.
+ * ImageConsumer holds one ref to mTextureRelease, which is decremented by "clear".
+ */
+ AutoBackendTextureRelease* mTextureRelease = nullptr;
};
/**
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c639191..edf304c 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -167,9 +167,9 @@
/**
* Broadcast intent action when the set of enabled location providers changes. To check the
- * status of a provider, use {@link #isProviderEnabled(String)}. Depending on API level, may
+ * status of a provider, use {@link #isProviderEnabled(String)}. From Android Q and above, will
* include a string intent extra, {@link #EXTRA_PROVIDER_NAME}, with the name of the provider
- * whose state has changed. See {@link #EXTRA_PROVIDER_NAME} for the supported API level.
+ * whose state has changed.
*
* @see #EXTRA_PROVIDER_NAME
*/
diff --git a/media/apex/java/android/media/Session2Command.java b/media/apex/java/android/media/Session2Command.java
index 20d6391..6822ea5 100644
--- a/media/apex/java/android/media/Session2Command.java
+++ b/media/apex/java/android/media/Session2Command.java
@@ -32,6 +32,10 @@
* If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
* {@link #getCustomCommand()} shouldn't be {@code null}.
* <p>
+ * Refer to the
+ * <a href="{@docRoot}reference/androidx/media2/SessionCommand2.html">AndroidX SessionCommand</a>
+ * class for the list of valid commands.
+ * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
* <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 12e02e7..27499c6 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -1036,6 +1036,15 @@
*/
public static final String KEY_CA_PRIVATE_DATA = "ca-private-data";
+ /**
+ * A key describing the maximum number of B frames between I or P frames,
+ * to be used by a video encoder.
+ * The associated value is an integer. The default value is 0, which means
+ * that no B frames are allowed. Note that non-zero value does not guarantee
+ * B frames; it's up to the encoder to decide.
+ */
+ public static final String KEY_MAX_BFRAMES = "max-bframes";
+
/* package private */ MediaFormat(@NonNull Map<String, Object> map) {
mMap = map;
}
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index a288d010..bda5743 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -52,6 +52,7 @@
import android.webkit.CookieManager;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
+import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@@ -409,8 +410,7 @@
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
getResources().getDisplayMetrics());
private int mPagesLoaded;
- // the host of the page that this webview is currently loading. Can be null when undefined.
- private String mHostname;
+ private String mMainFrameUrl;
// If we haven't finished cleaning up the history, don't allow going back.
public boolean allowBack() {
@@ -436,7 +436,6 @@
}
final URL url = makeURL(urlString);
Log.d(TAG, "onPageStarted: " + sanitizeURL(url));
- mHostname = host(url);
// For internally generated pages, leave URL bar listing prior URL as this is the URL
// the page refers to.
if (!urlString.startsWith(INTERNAL_ASSETS)) {
@@ -480,17 +479,28 @@
return Integer.toString((int)dp) + "px";
}
+ // Check if webview is trying to load the main frame and record its url.
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+ if (request.isForMainFrame()) {
+ mMainFrameUrl = request.getUrl().toString();
+ }
+ return false;
+ }
+
// A web page consisting of a large broken lock icon to indicate SSL failure.
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
- final URL url = makeURL(error.getUrl());
- final String host = host(url);
+ final URL errorUrl = makeURL(error.getUrl());
+ final URL mainFrameUrl = makeURL(mMainFrameUrl);
Log.d(TAG, String.format("SSL error: %s, url: %s, certificate: %s",
- sslErrorName(error), sanitizeURL(url), error.getCertificate()));
- if (url == null || !Objects.equals(host, mHostname)) {
- // Ignore ssl errors for resources coming from a different hostname than the page
- // that we are currently loading, and only cancel the request.
+ sslErrorName(error), sanitizeURL(errorUrl), error.getCertificate()));
+ if (errorUrl == null
+ // Ignore SSL errors from resources by comparing the main frame url with SSL
+ // error url.
+ || !errorUrl.equals(mainFrameUrl)) {
+ Log.d(TAG, "onReceivedSslError: mMainFrameUrl = " + mMainFrameUrl);
handler.cancel();
return;
}
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 591cf70..a69b412 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -93,6 +93,7 @@
<activity android:name=".UninstallerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
+ android:theme="@style/Theme.AlertDialogActivity.NoActionBar"
android:excludeFromRecents="true"
android:noHistory="true">
<intent-filter android:priority="1">
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
index 6c8e4c5..b11d28b 100644
--- a/packages/PackageInstaller/res/values/themes.xml
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -24,4 +24,9 @@
<style name="Theme.AlertDialogActivity"
parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+ <style name="Theme.AlertDialogActivity.NoActionBar">
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index bf97d77..d3dde92 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -185,8 +185,12 @@
<!-- Connected devices settings. Message when Bluetooth is connected and active, showing remote device status and battery level. [CHAR LIMIT=NONE] -->
<string name="bluetooth_active_battery_level">Active, <xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery</string>
+ <!-- Connected devices settings. Message when Bluetooth is connected and active, showing remote device status and battery level for untethered headset. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_active_battery_level_untethered">Active, L: <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g> battery, R: <xliff:g id="battery_level_as_percentage" example="25%">%2$s</xliff:g> battery</string>
<!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level. [CHAR LIMIT=NONE] -->
<string name="bluetooth_battery_level"><xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery</string>
+ <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level for untethered headset. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_battery_level_untethered">L: <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g> battery, R: <xliff:g id="battery_level_as_percentage" example="25%">%2$s</xliff:g> battery</string>
<!-- Connected devices settings. Message when Bluetooth is connected and active but no battery information, showing remote device status. [CHAR LIMIT=NONE] -->
<string name="bluetooth_active_no_battery_level">Active</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 081f8a0..bb8c8a6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,7 +1,6 @@
package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothClass;
-import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -10,7 +9,6 @@
import androidx.annotation.DrawableRes;
import com.android.settingslib.R;
-import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import java.util.List;
@@ -51,38 +49,29 @@
public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
CachedBluetoothDevice cachedDevice) {
- return getBtClassDrawableWithDescription(context, cachedDevice, 1 /* iconScale */);
- }
-
- public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
- CachedBluetoothDevice cachedDevice, float iconScale) {
BluetoothClass btClass = cachedDevice.getBtClass();
- final int level = cachedDevice.getBatteryLevel();
if (btClass != null) {
switch (btClass.getMajorDeviceClass()) {
case BluetoothClass.Device.Major.COMPUTER:
return new Pair<>(getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_bt_laptop, level, iconScale),
+ com.android.internal.R.drawable.ic_bt_laptop),
context.getString(R.string.bluetooth_talkback_computer));
case BluetoothClass.Device.Major.PHONE:
return new Pair<>(
getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_phone, level,
- iconScale),
+ com.android.internal.R.drawable.ic_phone),
context.getString(R.string.bluetooth_talkback_phone));
case BluetoothClass.Device.Major.PERIPHERAL:
return new Pair<>(
- getBluetoothDrawable(context, HidProfile.getHidClassDrawable(btClass),
- level, iconScale),
+ getBluetoothDrawable(context, HidProfile.getHidClassDrawable(btClass)),
context.getString(R.string.bluetooth_talkback_input_peripheral));
case BluetoothClass.Device.Major.IMAGING:
return new Pair<>(
getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_settings_print, level,
- iconScale),
+ com.android.internal.R.drawable.ic_settings_print),
context.getString(R.string.bluetooth_talkback_imaging));
default:
@@ -94,38 +83,33 @@
for (LocalBluetoothProfile profile : profiles) {
int resId = profile.getDrawableResource(btClass);
if (resId != 0) {
- return new Pair<>(getBluetoothDrawable(context, resId, level, iconScale), null);
+ return new Pair<>(getBluetoothDrawable(context, resId), null);
}
}
if (btClass != null) {
if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
return new Pair<>(
getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_bt_headset_hfp, level,
- iconScale),
+ com.android.internal.R.drawable.ic_bt_headset_hfp),
context.getString(R.string.bluetooth_talkback_headset));
}
if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
return new Pair<>(
getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_bt_headphones_a2dp, level,
- iconScale),
+ com.android.internal.R.drawable.ic_bt_headphones_a2dp),
context.getString(R.string.bluetooth_talkback_headphone));
}
}
return new Pair<>(
getBluetoothDrawable(context,
- com.android.internal.R.drawable.ic_settings_bluetooth, level, iconScale),
+ com.android.internal.R.drawable.ic_settings_bluetooth),
context.getString(R.string.bluetooth_talkback_bluetooth));
}
- public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId,
- int batteryLevel, float iconScale) {
- if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- return BluetoothDeviceLayerDrawable.createLayerDrawable(context, resId, batteryLevel,
- iconScale);
- } else {
- return context.getDrawable(resId);
- }
+ /**
+ * Get bluetooth drawable by {@code resId}
+ */
+ public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId) {
+ return context.getDrawable(resId);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index ab95910..2405666 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -32,6 +32,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.R;
+import com.android.settingslib.Utils;
import java.util.ArrayList;
import java.util.Collection;
@@ -830,6 +831,8 @@
boolean a2dpConnected = true; // A2DP is connected
boolean hfpConnected = true; // HFP is connected
boolean hearingAidConnected = true; // Hearing Aid is connected
+ int leftBattery = -1;
+ int rightBattery = -1;
synchronized (mProfileLock) {
for (LocalBluetoothProfile profile : getProfiles()) {
@@ -877,8 +880,23 @@
int stringRes = R.string.bluetooth_pairing;
//when profile is connected, information would be available
if (profileConnected) {
+ // Update Meta data for connected device
+ if (Boolean.parseBoolean(
+ mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))) {
+ try {
+ leftBattery = Integer.parseInt(
+ mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY));
+ rightBattery = Integer.parseInt(mDevice.getMetadata(
+ BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY));
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Parse error for unthethered battery level.");
+ }
+ }
+
// Set default string with battery level in device connected situation.
- if (batteryLevelPercentageString != null) {
+ if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
+ stringRes = R.string.bluetooth_battery_level_untethered;
+ } else if (batteryLevelPercentageString != null) {
stringRes = R.string.bluetooth_battery_level;
}
@@ -887,22 +905,36 @@
// 2. Headset device active with in-calling state.
// 3. A2DP device active without in-calling state.
if (a2dpConnected || hfpConnected || hearingAidConnected) {
- final boolean isOnCall =
- com.android.settingslib.Utils.isAudioModeOngoingCall(mContext);
+ final boolean isOnCall = Utils.isAudioModeOngoingCall(mContext);
if ((mIsActiveDeviceHearingAid)
|| (mIsActiveDeviceHeadset && isOnCall)
|| (mIsActiveDeviceA2dp && !isOnCall)) {
- stringRes = (batteryLevelPercentageString != null)
- ? R.string.bluetooth_active_battery_level
- : R.string.bluetooth_active_no_battery_level;
+ if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
+ stringRes = R.string.bluetooth_active_battery_level_untethered;
+ } else if (batteryLevelPercentageString != null) {
+ stringRes = R.string.bluetooth_active_battery_level;
+ } else {
+ stringRes = R.string.bluetooth_active_no_battery_level;
+ }
}
}
}
- return (stringRes != R.string.bluetooth_pairing
- || getBondState() == BluetoothDevice.BOND_BONDING)
- ? mContext.getString(stringRes, batteryLevelPercentageString)
- : null;
+ if (stringRes != R.string.bluetooth_pairing
+ || getBondState() == BluetoothDevice.BOND_BONDING) {
+ if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
+ return mContext.getString(stringRes, Utils.formatPercentage(leftBattery),
+ Utils.formatPercentage(rightBattery));
+ } else {
+ return mContext.getString(stringRes, batteryLevelPercentageString);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ private boolean isTwsBatteryAvailable(int leftBattery, int rightBattery) {
+ return leftBattery >= 0 && rightBattery >= 0;
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
index bee1b3c..d28b00a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
@@ -276,6 +276,12 @@
return null;
}
ensureMetadataNotStale(context);
+ final ActivityInfo activityInfo = getActivityInfo(context);
+ if (activityInfo == null) {
+ Log.w(TAG, "Cannot find ActivityInfo for " + getDescription());
+ return null;
+ }
+
int iconResId = mMetaData.getInt(META_DATA_PREFERENCE_ICON);
// Set the icon
if (iconResId == 0) {
@@ -283,11 +289,11 @@
// ICON_URI should be loaded in app UI when need the icon object. Handling IPC at this
// level is too complex because we don't have a strong threading contract for this class
if (!mMetaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
- iconResId = getActivityInfo(context).icon;
+ iconResId = activityInfo.icon;
}
}
if (iconResId != 0) {
- return Icon.createWithResource(getActivityInfo(context).packageName, iconResId);
+ return Icon.createWithResource(activityInfo.packageName, iconResId);
} else {
return null;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 7a71551..b713e08 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -15,36 +15,55 @@
*/
package com.android.settingslib.bluetooth;
-import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
-import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothClass;
+import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.util.Pair;
-import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
-
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class BluetoothUtilsTest {
- @Test
- public void testGetBluetoothDrawable_noBatteryLevel_returnSimpleDrawable() {
- final Drawable drawable = BluetoothUtils.getBluetoothDrawable(
- RuntimeEnvironment.application, com.android.internal.R.drawable.ic_bt_laptop,
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN, 1 /* iconScale */);
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ private Context mContext;
- assertThat(drawable).isNotInstanceOf(BluetoothDeviceLayerDrawable.class);
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
}
@Test
- public void testGetBluetoothDrawable_hasBatteryLevel_returnLayerDrawable() {
- final Drawable drawable = BluetoothUtils.getBluetoothDrawable(
- RuntimeEnvironment.application, com.android.internal.R.drawable.ic_bt_laptop,
- 10 /* batteryLevel */, 1 /* iconScale */);
+ public void getBtClassDrawableWithDescription_typePhone_returnPhoneDrawable() {
+ when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass()).thenReturn(
+ BluetoothClass.Device.Major.PHONE);
+ final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
+ mContext, mCachedBluetoothDevice);
- assertThat(drawable).isInstanceOf(BluetoothDeviceLayerDrawable.class);
+ verify(mContext).getDrawable(com.android.internal.R.drawable.ic_phone);
}
-}
+
+ @Test
+ public void getBtClassDrawableWithDescription_typeComputer_returnComputerDrawable() {
+ when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass()).thenReturn(
+ BluetoothClass.Device.Major.COMPUTER);
+ final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
+ mContext, mCachedBluetoothDevice);
+
+ verify(mContext).getDrawable(com.android.internal.R.drawable.ic_bt_laptop);
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 4e5d38a..79b84b9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -41,14 +41,16 @@
@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceTest {
- private final static String DEVICE_NAME = "TestName";
- private final static String DEVICE_ALIAS = "TestAlias";
- private final static String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF";
- private final static String DEVICE_ALIAS_NEW = "TestAliasNew";
- private final static short RSSI_1 = 10;
- private final static short RSSI_2 = 11;
- private final static boolean JUSTDISCOVERED_1 = true;
- private final static boolean JUSTDISCOVERED_2 = false;
+ private static final String DEVICE_NAME = "TestName";
+ private static final String DEVICE_ALIAS = "TestAlias";
+ private static final String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF";
+ private static final String DEVICE_ALIAS_NEW = "TestAliasNew";
+ private static final String TWS_BATTERY_LEFT = "15";
+ private static final String TWS_BATTERY_RIGHT = "25";
+ private static final short RSSI_1 = 10;
+ private static final short RSSI_2 = 11;
+ private static final boolean JUSTDISCOVERED_1 = true;
+ private static final boolean JUSTDISCOVERED_2 = false;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
@@ -447,6 +449,41 @@
}
@Test
+ public void getConnectionSummary_trueWirelessActiveDeviceWithBattery_returnActiveWithBattery() {
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn(
+ "true");
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn(
+ TWS_BATTERY_LEFT);
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY)).thenReturn(
+ TWS_BATTERY_RIGHT);
+
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Active, L: 15% battery, R: 25% battery");
+ }
+
+ @Test
+ public void getConnectionSummary_trueWirelessDeviceWithBattery_returnActiveWithBattery() {
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn(
+ "true");
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn(
+ TWS_BATTERY_LEFT);
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY)).thenReturn(
+ TWS_BATTERY_RIGHT);
+
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "L: 15% battery, R: 25% battery");
+ }
+
+ @Test
public void getCarConnectionSummary_singleProfileConnectDisconnect() {
// Test without battery level
// Set PAN profile to be connected and test connection state summary
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d639e5e..f0399c5 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -86,6 +86,7 @@
<uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.MANAGE_ROLLBACKS" />
+ <uses-permission android:name="android.permission.TEST_MANAGE_ROLLBACKS" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index 6c5c7fa..3d63b7d 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -5,26 +5,25 @@
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="top"
- android:orientation="vertical">
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="10dp"/>
+ android:orientation="vertical"
+ android:padding="@dimen/global_actions_padding"
+ android:background="@drawable/rounded_bg_full">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@android:color/white">
+ android:orientation="vertical">
<CheckBox
android:id="@+id/checkbox_mic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/screenrecord_mic_label"/>
<CheckBox
android:id="@+id/checkbox_taps"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/screenrecord_taps_label"/>
<Button
android:id="@+id/record_button"
@@ -34,8 +33,4 @@
/>
</LinearLayout>
- <Space
- android:layout_width="match_parent"
- android:layout_height="10dp"/>
-
</LinearLayout>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 0fed96e..27d2bcd 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -18,8 +18,6 @@
<declare-styleable name="KeyButtonView">
<!-- key code to send when pressed; if absent or 0, no key is sent -->
<attr name="keyCode" format="integer" />
- <!-- does this button generate longpress / repeat events? -->
- <attr name="keyRepeat" format="boolean" />
<!-- Should this button play sound effects, default true -->
<attr name="playSound" format="boolean" />
<attr name="android:contentDescription" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index da43391..c6c2763 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1069,10 +1069,10 @@
<string name="battery_saver_notification_action_text">Turn off Battery Saver</string>
<!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] -->
- <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing everything on your screen including notifications, passwords, photos, messages and payment information.</string>
+ <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing the played audio and everything on your screen including notifications, passwords, photos, messages and payment information.</string>
<!-- Media projection permission dialog warning title. [CHAR LIMIT=NONE] -->
- <string name="media_projection_dialog_title">Allow <xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> to record or cast your screen?</string>
+ <string name="media_projection_dialog_title">Allow <xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> to record or cast your screen and played audio?</string>
<!-- Media projection permission dialog permanent grant check box. [CHAR LIMIT=NONE] -->
<string name="media_projection_remember_text">Don\'t show again</string>
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e0ed111..4a2731e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -61,6 +61,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import java.lang.annotation.Retention;
@@ -74,7 +75,8 @@
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
@Singleton
-public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListener {
+public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListener,
+ ConfigurationController.ConfigurationListener {
private static final String TAG = "BubbleController";
@@ -84,6 +86,7 @@
@IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION})
@interface DismissReason {}
+
static final int DISMISS_USER_GESTURE = 1;
static final int DISMISS_AGED = 2;
static final int DISMISS_TASK_FINISHED = 3;
@@ -151,6 +154,7 @@
public interface BubbleExpandListener {
/**
* Called when the expansion state of the bubble stack changes.
+ *
* @param isExpanding whether it's expanding or collapsing
* @param key the notification key associated with bubble being expanded
*/
@@ -179,13 +183,16 @@
@Inject
public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
- BubbleData data) {
- this(context, statusBarWindowController, data, null /* synchronizer */);
+ BubbleData data, ConfigurationController configurationController) {
+ this(context, statusBarWindowController, data, null /* synchronizer */,
+ configurationController);
}
public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
- BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer) {
+ BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
+ ConfigurationController configurationController) {
mContext = context;
+ configurationController.addCallback(this /* configurationListener */);
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
@@ -215,6 +222,20 @@
mSurfaceSynchronizer = synchronizer;
}
+ @Override
+ public void onUiModeChanged() {
+ if (mStackView != null) {
+ mStackView.onConfigChanged();
+ }
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ if (mStackView != null) {
+ mStackView.onConfigChanged();
+ }
+ }
+
/**
* Set a listener to be notified when some states of the bubbles change.
*/
@@ -295,7 +316,7 @@
/**
* Adds or updates a bubble associated with the provided notification entry.
*
- * @param notif the notification associated with this bubble.
+ * @param notif the notification associated with this bubble.
* @param updatePosition whether this update should promote the bubble to the top of the stack.
*/
public void updateBubble(NotificationEntry notif, boolean updatePosition) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 856b9d6..e8b346e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -354,6 +354,13 @@
}
}
+ /**
+ * Update bubble expanded view header when user toggles dark mode.
+ */
+ void updateHeaderColor() {
+ mHeaderView.setBackgroundColor(mContext.getColor(R.attr.colorAccent));
+ }
+
private void updateHeaderView() {
mSettingsIcon.setContentDescription(getResources().getString(
R.string.bubbles_settings_button_description, mAppName));
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 1cc6b87..461e79c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -238,6 +238,13 @@
mBubbleContainer.bringToFront();
}
+ /**
+ * Handle config changes.
+ */
+ public void onConfigChanged() {
+ mExpandedBubble.expandedView.updateHeaderColor();
+ }
+
@Override
public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
getBoundsOnScreen(outRect);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 024404d..1e05983 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -66,7 +66,6 @@
private int mTouchDownX;
private int mTouchDownY;
private boolean mIsVertical;
- private boolean mSupportsLongpress = true;
private AudioManager mAudioManager;
private boolean mGestureAborted;
private boolean mLongClicked;
@@ -83,7 +82,7 @@
// Just an old-fashioned ImageView
performLongClick();
mLongClicked = true;
- } else if (mSupportsLongpress) {
+ } else {
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
mLongClicked = true;
@@ -104,7 +103,6 @@
mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, KEYCODE_UNKNOWN);
- mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
mPlaySounds = a.getBoolean(R.styleable.KeyButtonView_playSound, true);
TypedValue value = new TypedValue();
@@ -166,7 +164,7 @@
super.onInitializeAccessibilityNodeInfo(info);
if (mCode != KEYCODE_UNKNOWN) {
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, null));
- if (mSupportsLongpress || isLongClickable()) {
+ if (isLongClickable()) {
info.addAction(
new AccessibilityNodeInfo.AccessibilityAction(ACTION_LONG_CLICK, null));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 4e9d892..63b6673 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
import org.junit.Test;
@@ -66,6 +67,9 @@
private IActivityManager mActivityManager;
@Mock
private DozeParameters mDozeParameters;
+ @Mock
+ private ConfigurationController mConfigurationController;
+
private FrameLayout mStatusBarView;
@Captor
private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
@@ -97,6 +101,7 @@
mStatusBarView = new FrameLayout(mContext);
mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager);
+
// Bubbles get added to status bar window view
mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
mActivityManager, mDozeParameters);
@@ -115,7 +120,7 @@
mBubbleData = new BubbleData();
mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController,
- mBubbleData);
+ mBubbleData, mConfigurationController);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
mBubbleController.setExpandListener(mBubbleExpandListener);
@@ -331,8 +336,9 @@
static class TestableBubbleController extends BubbleController {
// Let's assume surfaces can be synchronized immediately.
TestableBubbleController(Context context,
- StatusBarWindowController statusBarWindowController, BubbleData data) {
- super(context, statusBarWindowController, data, Runnable::run);
+ StatusBarWindowController statusBarWindowController, BubbleData data,
+ ConfigurationController configurationController) {
+ super(context, statusBarWindowController, data, Runnable::run, configurationController);
}
@Override
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index f106228..78f22e0 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7120,6 +7120,18 @@
// Open: Settings > Special App Access > Do not disturb control for app
ZEN_ACCESS_DETAIL = 1692;
+ // OPEN: Settings > Face > Remove face
+ // OS: Q
+ DIALOG_FACE_REMOVE = 1693;
+
+ // FIELD - Detailed reason in screen wake. One of WAKE_REASON_* in PowerManager.
+ // OS: Q
+ FIELD_SCREEN_WAKE_REASON = 1694;
+
+ // FIELD - Detailed reason in screen sleep. One of GO_TO_SLEEP_REASON_* in PowerManager.
+ // OS: Q
+ FIELD_SCREEN_SLEEP_REASON = 1695;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6597312..c917268 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -569,6 +569,42 @@
}
}
+ // Called by Shell command
+ boolean isDefaultAugmentedServiceEnabled(@UserIdInt int userId) {
+ enforceCallingPermissionForManagement();
+
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ return service.mAugmentedAutofillResolver.isDefaultServiceEnabled(userId);
+ }
+ }
+ return false;
+ }
+
+ // Called by Shell command
+ boolean setDefaultAugmentedServiceEnabled(@UserIdInt int userId, boolean enabled) {
+ Slog.i(mTag, "setDefaultAugmentedServiceEnabled() for userId " + userId + ": " + enabled);
+ enforceCallingPermissionForManagement();
+
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ final boolean changed = service.mAugmentedAutofillResolver
+ .setDefaultServiceEnabled(userId, enabled);
+ if (changed) {
+ service.updateRemoteAugmentedAutofillService();
+ return true;
+ } else {
+ if (debug) {
+ Slog.d(TAG, "setDefaultAugmentedServiceEnabled(): already " + enabled);
+ }
+ }
+ }
+ }
+ return false;
+ }
+
private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
com.android.server.autofill.Helper.sDebug = debug;
android.view.autofill.Helper.sDebug = debug;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 9612346..dd3efa0 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -156,6 +156,7 @@
/** When was {@link PruneTask} last executed? */
private long mLastPrune = 0;
+ // TODO(b/128911469): move to AutofillManagerService
/**
* Object used to set the name of the augmented autofill service.
*/
@@ -889,8 +890,6 @@
} else {
pw.println();
mInfo.dump(prefix2, pw);
- pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabelLocked());
- pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
}
pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
.getString(R.string.config_defaultAutofillService));
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index c562fb1..bbe37a5 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -109,6 +109,13 @@
+ "implementation.");
pw.println(" To reset, call with just the USER_ID argument.");
pw.println("");
+ pw.println(" set default-augmented-service-enabled USER_ID [true|false]");
+ pw.println(" Enable / disable the default augmented autofill service for the user.");
+ pw.println("");
+ pw.println(" get default-augmented-service-enabled USER_ID");
+ pw.println(" Checks whether the default augmented autofill service is enabled for "
+ + "the user.");
+ pw.println("");
pw.println(" list sessions [--user USER_ID]");
pw.println(" Lists all pending sessions.");
pw.println("");
@@ -136,6 +143,8 @@
return getFullScreenMode(pw);
case "bind-instant-service-allowed":
return getBindInstantService(pw);
+ case "default-augmented-service-enabled":
+ return getDefaultAugmentedServiceEnabled(pw);
default:
pw.println("Invalid set: " + what);
return -1;
@@ -158,6 +167,8 @@
return setBindInstantService(pw);
case "temporary-augmented-service":
return setTemporaryAugmentedService(pw);
+ case "default-augmented-service-enabled":
+ return setDefaultAugmentedServiceEnabled(pw);
default:
pw.println("Invalid set: " + what);
return -1;
@@ -314,6 +325,23 @@
return 0;
}
+ private int getDefaultAugmentedServiceEnabled(PrintWriter pw) {
+ final int userId = getNextIntArgRequired();
+ final boolean enabled = mService.isDefaultAugmentedServiceEnabled(userId);
+ pw.println(enabled);
+ return 0;
+ }
+
+ private int setDefaultAugmentedServiceEnabled(PrintWriter pw) {
+ final int userId = getNextIntArgRequired();
+ final boolean enabled = Boolean.parseBoolean(getNextArgRequired());
+ final boolean changed = mService.setDefaultAugmentedServiceEnabled(userId, enabled);
+ if (!changed) {
+ pw.println("already " + enabled);
+ }
+ return 0;
+ }
+
private int requestDestroy(PrintWriter pw) {
if (!isNextArgSessions(pw)) {
return -1;
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
index 86ad52d..fc9754a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
@@ -114,7 +114,7 @@
case "temporary-service":
return setTemporaryService(pw);
case "default-service-enabled":
- return setDefaultServiceEnabled();
+ return setDefaultServiceEnabled(pw);
default:
pw.println("Invalid set: " + what);
return -1;
@@ -159,10 +159,13 @@
return 0;
}
- private int setDefaultServiceEnabled() {
+ private int setDefaultServiceEnabled(PrintWriter pw) {
final int userId = getNextIntArgRequired();
- final boolean enabled = Boolean.parseBoolean(getNextArg());
- mService.setDefaultServiceEnabled(userId, enabled);
+ final boolean enabled = Boolean.parseBoolean(getNextArgRequired());
+ final boolean changed = mService.setDefaultServiceEnabled(userId, enabled);
+ if (!changed) {
+ pw.println("already " + enabled);
+ }
return 0;
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index c423f9c..d7d97b3 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -27,12 +27,10 @@
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
-import android.app.AppGlobals;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.content.ComponentName;
@@ -44,12 +42,12 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.contentcapture.ActivityEvent;
import android.service.contentcapture.ActivityEvent.ActivityEventType;
import android.service.contentcapture.ContentCaptureService;
+import android.service.contentcapture.ContentCaptureServiceInfo;
import android.service.contentcapture.IContentCaptureServiceCallback;
import android.service.contentcapture.SnapshotData;
import android.util.ArrayMap;
@@ -107,6 +105,9 @@
@GuardedBy("mLock")
private boolean mZombie;
+ @GuardedBy("mLock")
+ private ContentCaptureServiceInfo mInfo;
+
// TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
ContentCapturePerUserService(@NonNull ContentCaptureManagerService master,
@@ -144,33 +145,9 @@
@Override // from PerUserSystemService
protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
throws NameNotFoundException {
-
- int flags = PackageManager.GET_META_DATA;
- final boolean isTemp = isTemporaryServiceSetLocked();
- if (!isTemp) {
- flags |= PackageManager.MATCH_SYSTEM_ONLY;
- }
-
- ServiceInfo si;
- try {
- si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags, mUserId);
- } catch (RemoteException e) {
- Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e);
- return null;
- }
- if (si == null) {
- Slog.w(TAG, "Could not get serviceInfo for " + (isTemp ? " (temp)" : "(default system)")
- + " " + serviceComponent.flattenToShortString());
- return null;
- }
- if (!Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE.equals(si.permission)) {
- Slog.w(TAG, "ContentCaptureService from '" + si.packageName
- + "' does not require permission "
- + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
- throw new SecurityException("Service does not require permission "
- + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
- }
- return si;
+ mInfo = new ContentCaptureServiceInfo(getContext(), serviceComponent,
+ isTemporaryServiceSetLocked(), mUserId);
+ return mInfo.getServiceInfo();
}
@Override // from PerUserSystemService
@@ -490,9 +467,16 @@
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
+ final String prefix2 = prefix + " ";
+ pw.print(prefix); pw.print("Service Info: ");
+ if (mInfo == null) {
+ pw.println("N/A");
+ } else {
+ pw.println();
+ mInfo.dump(prefix2, pw);
+ }
pw.print(prefix); pw.print("Zombie: "); pw.println(mZombie);
- final String prefix2 = prefix + " ";
if (mRemoteService != null) {
pw.print(prefix); pw.println("remote service:");
mRemoteService.dump(prefix2, pw);
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index a400cc3..3d918fc 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -83,6 +83,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.StatsLog;
import android.util.TimeUtils;
@@ -195,6 +196,7 @@
private final Injector mInjector;
int mBroadcastRefCount = 0;
PowerManager.WakeLock mWakeLock;
+ SparseIntArray mAlarmsPerUid = new SparseIntArray();
ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>();
ArrayList<InFlight> mInFlight = new ArrayList<>();
private final ArrayList<AlarmManagerInternal.InFlightListener> mInFlightListeners =
@@ -393,6 +395,8 @@
@VisibleForTesting
static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
@VisibleForTesting
+ static final String KEY_MAX_ALARMS_PER_UID = "max_alarms_per_uid";
+ @VisibleForTesting
static final String KEY_APP_STANDBY_QUOTAS_ENABLED = "app_standby_quotas_enabled";
private static final String KEY_APP_STANDBY_WINDOW = "app_standby_window";
@VisibleForTesting
@@ -420,6 +424,7 @@
private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
+ private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
private static final boolean DEFAULT_APP_STANDBY_QUOTAS_ENABLED = true;
private static final long DEFAULT_APP_STANDBY_WINDOW = 60 * 60 * 1000; // 1 hr
/**
@@ -461,6 +466,8 @@
// Direct alarm listener callback timeout
public long LISTENER_TIMEOUT = DEFAULT_LISTENER_TIMEOUT;
+ public int MAX_ALARMS_PER_UID = DEFAULT_MAX_ALARMS_PER_UID;
+
public boolean APP_STANDBY_QUOTAS_ENABLED = DEFAULT_APP_STANDBY_QUOTAS_ENABLED;
public long APP_STANDBY_WINDOW = DEFAULT_APP_STANDBY_WINDOW;
@@ -549,6 +556,15 @@
APP_STANDBY_QUOTAS[i] = mParser.getInt(KEYS_APP_STANDBY_QUOTAS[i],
Math.min(APP_STANDBY_QUOTAS[i - 1], DEFAULT_APP_STANDBY_QUOTAS[i]));
}
+
+ MAX_ALARMS_PER_UID = mParser.getInt(KEY_MAX_ALARMS_PER_UID,
+ DEFAULT_MAX_ALARMS_PER_UID);
+ if (MAX_ALARMS_PER_UID < DEFAULT_MAX_ALARMS_PER_UID) {
+ Slog.w(TAG, "Cannot set " + KEY_MAX_ALARMS_PER_UID + " lower than "
+ + DEFAULT_MAX_ALARMS_PER_UID);
+ MAX_ALARMS_PER_UID = DEFAULT_MAX_ALARMS_PER_UID;
+ }
+
updateAllowWhileIdleWhitelistDurationLocked();
}
}
@@ -590,6 +606,9 @@
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION, pw);
pw.println();
+ pw.print(KEY_MAX_ALARMS_PER_UID); pw.print("=");
+ pw.println(MAX_ALARMS_PER_UID);
+
for (int i = 0; i < KEYS_APP_STANDBY_DELAY.length; i++) {
pw.print(KEYS_APP_STANDBY_DELAY[i]); pw.print("=");
TimeUtils.formatDuration(APP_STANDBY_MIN_DELAYS[i], pw);
@@ -671,12 +690,6 @@
final ArrayList<Alarm> alarms = new ArrayList<Alarm>();
- Batch() {
- start = 0;
- end = Long.MAX_VALUE;
- flags = 0;
- }
-
Batch(Alarm seed) {
start = seed.whenElapsed;
end = clampPositive(seed.maxWhenElapsed);
@@ -728,11 +741,16 @@
return newStart;
}
+ /**
+ * Remove an alarm from this batch.
+ * <p> <b> Should be used only while re-ordering the alarm within the service </b> as it
+ * does not update {@link #mAlarmsPerUid}
+ */
boolean remove(Alarm alarm) {
- return remove(a -> (a == alarm));
+ return remove(a -> (a == alarm), true);
}
- boolean remove(Predicate<Alarm> predicate) {
+ boolean remove(Predicate<Alarm> predicate, boolean reOrdering) {
boolean didRemove = false;
long newStart = 0; // recalculate endpoints as we go
long newEnd = Long.MAX_VALUE;
@@ -741,6 +759,9 @@
Alarm alarm = alarms.get(i);
if (predicate.test(alarm)) {
alarms.remove(i);
+ if (!reOrdering) {
+ decrementAlarmCount(alarm.uid);
+ }
didRemove = true;
if (alarm.alarmClock != null) {
mNextAlarmClockMayChange = true;
@@ -1734,6 +1755,15 @@
+ " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed
+ " interval=" + interval + " flags=0x" + Integer.toHexString(flags));
}
+ if (mAlarmsPerUid.get(callingUid, 0) >= mConstants.MAX_ALARMS_PER_UID) {
+ final String errorMsg =
+ "Maximum limit of concurrent alarms " + mConstants.MAX_ALARMS_PER_UID
+ + " reached for uid: " + UserHandle.formatUid(callingUid)
+ + ", callingPackage: " + callingPackage;
+ // STOPSHIP (b/128866264): Just to catch breakages. Remove before final release.
+ Slog.wtf(TAG, errorMsg);
+ throw new UnsupportedOperationException(errorMsg);
+ }
setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
interval, operation, directReceiver, listenerTag, flags, true, workSource,
alarmClock, callingUid, callingPackage);
@@ -1756,6 +1786,7 @@
} catch (RemoteException e) {
}
removeLocked(operation, directReceiver);
+ incrementAlarmCount(a.uid);
setImplLocked(a, false, doValidate);
}
@@ -2197,7 +2228,6 @@
? sdf.format(new Date(nowRTC - (nowELAPSED - time)))
: "-");
} while (i != mNextTickHistory);
- pw.println();
}
SystemServiceManager ssm = LocalServices.getService(SystemServiceManager.class);
@@ -2305,6 +2335,18 @@
if (!blocked) {
pw.println(" none");
}
+ pw.println();
+ pw.print(" Pending alarms per uid: [");
+ for (int i = 0; i < mAlarmsPerUid.size(); i++) {
+ if (i > 0) {
+ pw.print(", ");
+ }
+ UserHandle.formatUid(pw, mAlarmsPerUid.keyAt(i));
+ pw.print(":");
+ pw.print(mAlarmsPerUid.valueAt(i));
+ }
+ pw.println("]");
+ pw.println();
mAppWakeupHistory.dump(pw, " ", nowELAPSED);
@@ -3046,7 +3088,7 @@
final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(operation, directReceiver);
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(whichAlarms);
+ didRemove |= b.remove(whichAlarms, false);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -3098,7 +3140,7 @@
final Predicate<Alarm> whichAlarms = (Alarm a) -> a.uid == uid;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(whichAlarms);
+ didRemove |= b.remove(whichAlarms, false);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -3145,7 +3187,7 @@
final boolean oldHasTick = haveBatchesTimeTickAlarm(mAlarmBatches);
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(whichAlarms);
+ didRemove |= b.remove(whichAlarms, false);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -3183,6 +3225,7 @@
}
}
+ // Only called for ephemeral apps
void removeForStoppedLocked(final int uid) {
if (uid == Process.SYSTEM_UID) {
Slog.wtf(TAG, "removeForStoppedLocked: Shouldn't for UID=" + uid);
@@ -3200,7 +3243,7 @@
};
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(whichAlarms);
+ didRemove |= b.remove(whichAlarms, false);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -3237,7 +3280,7 @@
(Alarm a) -> UserHandle.getUserId(a.creatorUid) == userHandle;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(whichAlarms);
+ didRemove |= b.remove(whichAlarms, false);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -3407,8 +3450,7 @@
return mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
}
- boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
- final long nowRTC) {
+ boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED) {
boolean hasWakeup = false;
// batches are temporally sorted, so we need only pull from the
// start of the list until we either empty it or hit a batch
@@ -3984,7 +4026,7 @@
}
mLastTrigger = nowELAPSED;
- boolean hasWakeup = triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
+ boolean hasWakeup = triggerAlarmsLocked(triggerList, nowELAPSED);
if (!hasWakeup && checkAllowNonWakeupDelayLocked(nowELAPSED)) {
// if there are no wakeup alarms and the screen is off, we can
// delay what we have so far until the future.
@@ -4108,9 +4150,8 @@
case ALARM_EVENT: {
ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
synchronized (mLock) {
- final long nowRTC = mInjector.getCurrentTimeMillis();
final long nowELAPSED = mInjector.getElapsedRealtime();
- triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
+ triggerAlarmsLocked(triggerList, nowELAPSED);
updateNextAlarmClockLocked();
}
@@ -4719,7 +4760,7 @@
mAppWakeupHistory.recordAlarmForPackage(alarm.sourcePackage,
UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
}
-
+ decrementAlarmCount(alarm.uid);
final BroadcastStats bs = inflight.mBroadcastStats;
bs.count++;
if (bs.nesting == 0) {
@@ -4747,6 +4788,27 @@
}
}
+ private void incrementAlarmCount(int uid) {
+ final int uidIndex = mAlarmsPerUid.indexOfKey(uid);
+ if (uidIndex >= 0) {
+ mAlarmsPerUid.setValueAt(uidIndex, mAlarmsPerUid.valueAt(uidIndex) + 1);
+ } else {
+ mAlarmsPerUid.put(uid, 1);
+ }
+ }
+
+ private void decrementAlarmCount(int uid) {
+ final int uidIndex = mAlarmsPerUid.indexOfKey(uid);
+ if (uidIndex >= 0) {
+ final int newCount = mAlarmsPerUid.valueAt(uidIndex) - 1;
+ if (newCount > 0) {
+ mAlarmsPerUid.setValueAt(uidIndex, newCount);
+ } else {
+ mAlarmsPerUid.removeAt(uidIndex);
+ }
+ }
+ }
+
private class ShellCmd extends ShellCommand {
IAlarmManager getBinderService() {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 3ab3c34..833faa6 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -115,7 +115,8 @@
*
* Test: atest com.android.server.DeviceIdleControllerTest
*
- * Current idling state machine (as of Android 9 Pie). This can be visualized using Graphviz:
+ * Current idling state machine (as of Android Q). This can be visualized using Graphviz:
+ <pre>
digraph {
subgraph deep {
@@ -259,6 +260,7 @@
]
}
}
+ </pre>
*/
public class DeviceIdleController extends SystemService
implements AnyMotionDetector.DeviceIdleCallback {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index edd5b68..ceeb539 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -311,9 +311,13 @@
AppOpsManager.WATCH_FOREGROUND_CHANGES,
new AppOpsManager.OnOpChangedInternalListener() {
public void onOpChanged(int op, String packageName) {
- synchronized (mLock) {
- onAppOpChangedLocked();
- }
+ // onOpChanged invoked on ui thread, move to our thread to reduce risk of
+ // blocking ui thread
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ onAppOpChangedLocked();
+ }
+ });
}
});
mPackageManager.addOnPermissionsChangeListener(
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 19b0bf7..8cb1ec7 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -208,6 +208,13 @@
private static final boolean ENABLE_LEGACY_GREYLIST = SystemProperties
.getBoolean(StorageManager.PROP_LEGACY_GREYLIST, true);
+ /**
+ * If {@code 1}, enables the isolated storage feature. If {@code -1},
+ * disables the isolated storage feature. If {@code 0}, uses the default
+ * value from the build system.
+ */
+ private static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
+
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
@@ -797,7 +804,7 @@
}
});
// For now, simply clone property when it changes
- DeviceConfig.addOnPropertyChangedListener(DeviceConfig.Storage.NAMESPACE,
+ DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_STORAGE,
mContext.getMainExecutor(), (namespace, name, value) -> {
refreshIsolatedStorageSettings();
});
@@ -837,8 +844,7 @@
// Always copy value from newer DeviceConfig location
Settings.Global.putString(mResolver,
Settings.Global.ISOLATED_STORAGE_REMOTE,
- DeviceConfig.getProperty(DeviceConfig.Storage.NAMESPACE,
- DeviceConfig.Storage.ISOLATED_STORAGE_ENABLED));
+ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE, ISOLATED_STORAGE_ENABLED));
final int local = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ISOLATED_STORAGE_LOCAL, 0);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index d6fdbe3..26896f5 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -625,6 +625,11 @@
r.callingPackage = callingPackage;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
+ if (r.subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID && r.subId != subId) {
+ throw new IllegalArgumentException(
+ "PhoneStateListener cannot concurrently listen on multiple " +
+ "subscriptions. Previously registered on subId: " + r.subId);
+ }
// Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
// force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8225476..97cc756 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1273,11 +1273,13 @@
static final class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
+ static final int CHANGE_FOREGROUND_SERVICES = 1<<1;
int changes;
int uid;
int pid;
int processState;
boolean foregroundActivities;
+ int foregroundServiceTypes;
}
static final class UidObserverRegistration {
@@ -2935,7 +2937,8 @@
}
}
if (mContentCaptureService != null && (event == Event.ACTIVITY_PAUSED
- || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED)) {
+ || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED
+ || event == Event.ACTIVITY_DESTROYED)) {
mContentCaptureService.notifyActivityEvent(userId, activity, event);
}
}
@@ -3079,6 +3082,13 @@
observer.onForegroundActivitiesChanged(item.pid, item.uid,
item.foregroundActivities);
}
+ if ((item.changes & ProcessChangeItem.CHANGE_FOREGROUND_SERVICES) != 0) {
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+ "FOREGROUND SERVICES CHANGED pid=" + item.pid + " uid="
+ + item.uid + ": " + item.foregroundServiceTypes);
+ observer.onForegroundServicesChanged(item.pid, item.uid,
+ item.foregroundServiceTypes);
+ }
}
} catch (RemoteException e) {
}
@@ -3093,6 +3103,47 @@
}
}
+ @GuardedBy("this")
+ ProcessChangeItem enqueueProcessChangeItemLocked(int uid, int pid) {
+ int i = mPendingProcessChanges.size()-1;
+ ActivityManagerService.ProcessChangeItem item = null;
+ while (i >= 0) {
+ item = mPendingProcessChanges.get(i);
+ if (item.pid == pid) {
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+ "Re-using existing item: " + item);
+ break;
+ }
+ i--;
+ }
+
+ if (i < 0) {
+ // No existing item in pending changes; need a new one.
+ final int NA = mAvailProcessChanges.size();
+ if (NA > 0) {
+ item = mAvailProcessChanges.remove(NA-1);
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+ "Retrieving available item: " + item);
+ } else {
+ item = new ActivityManagerService.ProcessChangeItem();
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+ "Allocating new item: " + item);
+ }
+ item.changes = 0;
+ item.pid = pid;
+ item.uid = uid;
+ if (mPendingProcessChanges.size() == 0) {
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+ "*** Enqueueing dispatch processes changed!");
+ mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG)
+ .sendToTarget();
+ }
+ mPendingProcessChanges.add(item);
+ }
+
+ return item;
+ }
+
private void dispatchProcessDied(int pid, int uid) {
int i = mProcessObservers.beginBroadcast();
while (i > 0) {
@@ -16275,11 +16326,12 @@
@GuardedBy("this")
final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
int fgServiceTypes, boolean oomAdj) {
+ proc.setHasForegroundServices(isForeground, fgServiceTypes);
+
final boolean hasFgServiceLocationType =
(fgServiceTypes & ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION) != 0;
if (isForeground != proc.hasForegroundServices()
|| proc.hasLocationForegroundServices() != hasFgServiceLocationType) {
- proc.setHasForegroundServices(isForeground, fgServiceTypes);
ArrayList<ProcessRecord> curProcs = mForegroundPackages.get(proc.info.packageName,
proc.info.uid);
if (isForeground) {
@@ -16308,6 +16360,13 @@
updateOomAdjLocked();
}
}
+
+ if (proc.getForegroundServiceTypes() != fgServiceTypes) {
+ proc.setReportedForegroundServiceTypes(fgServiceTypes);
+ ProcessChangeItem item = enqueueProcessChangeItemLocked(proc.info.uid, proc.pid);
+ item.changes = ProcessChangeItem.CHANGE_FOREGROUND_SERVICES;
+ item.foregroundServiceTypes = fgServiceTypes;
+ }
}
// TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
@@ -18101,6 +18160,34 @@
public void prepareForPossibleShutdown() {
ActivityManagerService.this.prepareForPossibleShutdown();
}
+
+ @Override
+ public boolean hasRunningForegroundService(int uid, int foregroundServicetype) {
+ synchronized (ActivityManagerService.this) {
+ for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
+ final ProcessRecord pr = mProcessList.mLruProcesses.get(i);
+ if (pr.uid != uid) {
+ continue;
+ }
+
+ if ((pr.getForegroundServiceTypes() & foregroundServicetype) != 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void registerProcessObserver(IProcessObserver processObserver) {
+ ActivityManagerService.this.registerProcessObserver(processObserver);
+ }
+
+ @Override
+ public void unregisterProcessObserver(IProcessObserver processObserver) {
+ ActivityManagerService.this.unregisterProcessObserver(processObserver);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index 438538f..9216343 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -230,11 +230,8 @@
*/
@GuardedBy("mPhenotypeFlagLock")
private void updateUseCompaction() {
- String useCompactionFlag =
- DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- KEY_USE_COMPACTION);
- mUseCompaction = TextUtils.isEmpty(useCompactionFlag)
- ? DEFAULT_USE_COMPACTION : Boolean.parseBoolean(useCompactionFlag);
+ mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
if (mUseCompaction && !mCompactionThread.isAlive()) {
mCompactionThread.start();
mCompactionHandler = new MemCompactionHandler();
@@ -243,28 +240,11 @@
@GuardedBy("mPhenotypeFlagLock")
private void updateCompactionActions() {
- String compactAction1Flag =
- DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- KEY_COMPACT_ACTION_1);
- String compactAction2Flag =
- DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- KEY_COMPACT_ACTION_2);
+ int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);
- int compactAction1 = DEFAULT_COMPACT_ACTION_1;
- try {
- compactAction1 = TextUtils.isEmpty(compactAction1Flag)
- ? DEFAULT_COMPACT_ACTION_1 : Integer.parseInt(compactAction1Flag);
- } catch (NumberFormatException e) {
- // Do nothing, leave default.
- }
-
- int compactAction2 = DEFAULT_COMPACT_ACTION_2;
- try {
- compactAction2 = TextUtils.isEmpty(compactAction2Flag)
- ? DEFAULT_COMPACT_ACTION_2 : Integer.parseInt(compactAction2Flag);
- } catch (NumberFormatException e) {
- // Do nothing, leave default.
- }
+ int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
mCompactActionSome = compactActionIntToString(compactAction1);
mCompactActionFull = compactActionIntToString(compactAction2);
@@ -312,14 +292,8 @@
@GuardedBy("mPhenotypeFlagLock")
private void updateStatsdSampleRate() {
- String sampleRateFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- KEY_COMPACT_STATSD_SAMPLE_RATE);
- try {
- mStatsdSampleRate = TextUtils.isEmpty(sampleRateFlag)
- ? DEFAULT_STATSD_SAMPLE_RATE : Float.parseFloat(sampleRateFlag);
- } catch (NumberFormatException e) {
- mStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
- }
+ mStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_COMPACT_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE);
mStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mStatsdSampleRate));
}
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 09df7e20..d8d6528 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -24,6 +24,7 @@
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
+import android.app.IActivityTaskManager;
import android.app.IAssistDataReceiver;
import android.content.Context;
import android.graphics.Bitmap;
@@ -33,6 +34,7 @@
import android.view.IWindowManager;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import java.io.PrintWriter;
@@ -50,6 +52,8 @@
public static final String KEY_RECEIVER_EXTRA_INDEX = "index";
private IWindowManager mWindowManager;
+ @VisibleForTesting
+ public IActivityTaskManager mActivityTaskManager;
private Context mContext;
private AppOpsManager mAppOpsManager;
@@ -128,6 +132,7 @@
mCallbacks = callbacks;
mCallbacksLock = callbacksLock;
mWindowManager = windowManager;
+ mActivityTaskManager = ActivityTaskManager.getService();
mContext = context;
mAppOpsManager = appOpsManager;
mRequestStructureAppOps = requestStructureAppOps;
@@ -195,8 +200,7 @@
// Ensure that the current activity supports assist data
boolean isAssistDataAllowed = false;
try {
- isAssistDataAllowed =
- ActivityTaskManager.getService().isAssistDataAllowedOnCurrentActivity();
+ isAssistDataAllowed = mActivityTaskManager.isAssistDataAllowedOnCurrentActivity();
} catch (RemoteException e) {
// Should never happen
}
@@ -222,9 +226,9 @@
receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities);
boolean result = requestAutofillData
- ? ActivityTaskManager.getService().requestAutofillData(this,
+ ? mActivityTaskManager.requestAutofillData(this,
receiverExtras, topActivity, 0 /* flags */)
- : ActivityTaskManager.getService().requestAssistContextExtras(
+ : mActivityTaskManager.requestAssistContextExtras(
ASSIST_CONTEXT_FULL, this, receiverExtras, topActivity,
/* focused= */ i == 0, /* newSessionId= */ i == 0);
if (result) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 9056f76..aa03de1 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -38,7 +38,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
import static com.android.server.am.ActivityManagerService.DISPATCH_OOM_ADJ_OBSERVER_MSG;
-import static com.android.server.am.ActivityManagerService.DISPATCH_PROCESSES_CHANGED_UI_MSG;
import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG;
import static com.android.server.am.ActivityManagerService.TAG_BACKUP;
import static com.android.server.am.ActivityManagerService.TAG_LRU;
@@ -1921,41 +1920,9 @@
if (changes != 0) {
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Changes in " + app + ": " + changes);
- int i = mService.mPendingProcessChanges.size()-1;
- ActivityManagerService.ProcessChangeItem item = null;
- while (i >= 0) {
- item = mService.mPendingProcessChanges.get(i);
- if (item.pid == app.pid) {
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "Re-using existing item: " + item);
- break;
- }
- i--;
- }
- if (i < 0) {
- // No existing item in pending changes; need a new one.
- final int NA = mService.mAvailProcessChanges.size();
- if (NA > 0) {
- item = mService.mAvailProcessChanges.remove(NA-1);
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "Retrieving available item: " + item);
- } else {
- item = new ActivityManagerService.ProcessChangeItem();
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "Allocating new item: " + item);
- }
- item.changes = 0;
- item.pid = app.pid;
- item.uid = app.info.uid;
- if (mService.mPendingProcessChanges.size() == 0) {
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "*** Enqueueing dispatch processes changed!");
- mService.mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG)
- .sendToTarget();
- }
- mService.mPendingProcessChanges.add(item);
- }
- item.changes |= changes;
+ ActivityManagerService.ProcessChangeItem item =
+ mService.enqueueProcessChangeItemLocked(app.pid, app.info.uid);
+ item.changes = changes;
item.foregroundActivities = app.repForegroundActivities;
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Item " + Integer.toHexString(System.identityHashCode(item))
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 51b1ae1..17b244c 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -174,6 +174,7 @@
boolean hasStartedServices; // Are there any started services running in this process?
private boolean mHasForegroundServices; // Running any services that are foreground?
private int mFgServiceTypes; // Type of foreground service, if there is a foreground service.
+ private int mRepFgServiceTypes; // Last reported foreground service types.
private boolean mHasForegroundActivities; // Running any activities that are foreground?
boolean repForegroundActivities; // Last reported foreground activities.
boolean systemNoUi; // This is a system process, but not currently showing UI.
@@ -1079,6 +1080,18 @@
&& (mFgServiceTypes & ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION) != 0;
}
+ int getForegroundServiceTypes() {
+ return mHasForegroundServices ? mFgServiceTypes : 0;
+ }
+
+ int getReportedForegroundServiceTypes() {
+ return mRepFgServiceTypes;
+ }
+
+ void setReportedForegroundServiceTypes(int foregroundServiceTypes) {
+ mRepFgServiceTypes = foregroundServiceTypes;
+ }
+
void setHasForegroundActivities(boolean hasForegroundActivities) {
mHasForegroundActivities = hasForegroundActivities;
mWindowProcessController.setHasForegroundActivities(hasForegroundActivities);
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 4e0380d..42a7a5c 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -113,6 +113,11 @@
cancelAndUnbindLocked(peekUserStateLocked(userId));
}
+ /** Returns {@code true} if attention service is configured on this device. */
+ public static boolean isServiceConfigured(Context context) {
+ return !TextUtils.isEmpty(getServiceConfig(context));
+ }
+
/** Resolves and sets up the attention service if it had not been done yet. */
private boolean isServiceAvailable() {
if (mComponentName == null) {
@@ -285,6 +290,10 @@
return mUserStates.get(userId);
}
+ private static String getServiceConfig(Context context) {
+ return context.getString(R.string.config_defaultAttentionService);
+ }
+
/**
* Provides attention service component name at runtime, making sure it's provided by the
* system.
@@ -293,9 +302,7 @@
final String flag = DeviceConfig.getProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
COMPONENT_NAME);
- final String componentNameString = flag != null ? flag : context.getString(
- R.string.config_defaultAttentionService);
-
+ final String componentNameString = flag != null ? flag : getServiceConfig(context);
if (TextUtils.isEmpty(componentNameString)) {
return null;
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index f313e1d..516844d 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -628,6 +628,9 @@
null /* description */);
// Then give it the bundle to do magic behavior..
intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
+ // Create a new task with this activity located at the root.
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
getContext().startActivityAsUser(intent, UserHandle.CURRENT);
});
return;
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 45567e5..ed420b7 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -1417,7 +1417,7 @@
mCurrentColorTemperature = cct;
// Adapt the display's nominal white point to match the requested CCT value
- mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
+ mCurrentColorTemperatureXYZ = ColorSpace.cctToXyz(cct);
mChromaticAdaptationMatrix =
ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 3c27bf2..430203d 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -332,21 +332,31 @@
* with the test results.
*
* @throws SecurityException if caller is not allowed to manage this service's settings.
+ *
+ * @return whether the enabled state changed.
*/
- public final void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
+ public final boolean setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
Slog.i(mTag, "setDefaultServiceEnabled() for userId " + userId + ": " + enabled);
enforceCallingPermissionForManagement();
synchronized (mLock) {
+ final boolean changed = mServiceNameResolver.setDefaultServiceEnabled(userId, enabled);
+ if (!changed) {
+ if (verbose) {
+ Slog.v(mTag, "setDefaultServiceEnabled(" + userId + "): already " + enabled);
+ }
+ return false;
+ }
+
final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) {
oldService.removeSelfFromCacheLocked();
}
- mServiceNameResolver.setDefaultServiceEnabled(userId, enabled);
// Must update the service on cache so its initialization code is triggered
updateCachedServiceLocked(userId);
}
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index cd9ebcd..ac07e9d 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -327,6 +327,10 @@
@GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+ if (mServiceInfo != null) {
+ pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabelLocked());
+ pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
+ }
if (mMaster.mServiceNameResolver != null) {
pw.print(prefix); pw.print("Name resolver: ");
mMaster.mServiceNameResolver.dumpShort(pw, mUserId); pw.println();
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 1b23794..d204813 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -174,23 +174,35 @@
}
@Override
- public void setDefaultServiceEnabled(int userId, boolean enabled) {
+ public boolean setDefaultServiceEnabled(int userId, boolean enabled) {
synchronized (mLock) {
+ final boolean currentlyEnabled = isDefaultServiceEnabledLocked(userId);
+ if (currentlyEnabled == enabled) {
+ Slog.i(TAG, "setDefaultServiceEnabled(" + userId + "): already " + enabled);
+ return false;
+ }
if (enabled) {
+ Slog.i(TAG, "disabling default service for user " + userId);
mDefaultServicesDisabled.removeAt(userId);
} else {
+ Slog.i(TAG, "enabling default service for user " + userId);
mDefaultServicesDisabled.put(userId, true);
}
}
+ return true;
}
@Override
public boolean isDefaultServiceEnabled(int userId) {
synchronized (mLock) {
- return !mDefaultServicesDisabled.get(userId);
+ return isDefaultServiceEnabledLocked(userId);
}
}
+ private boolean isDefaultServiceEnabledLocked(int userId) {
+ return !mDefaultServicesDisabled.get(userId);
+ }
+
@Override
public String toString() {
return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]";
diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java
index 5b60413..8c348ebb 100644
--- a/services/core/java/com/android/server/infra/ServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java
@@ -115,11 +115,13 @@
*
* @param userId user handle
* @param enabled whether the default service should be used when the temporary service is not
- * set
+ * set. If the service enabled state is already that value, the command is ignored and this
+ * method return {@code false}.
*
+ * @return whether the enabled state changed.
* @throws UnsupportedOperationException if not implemented.
*/
- default void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
+ default boolean setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
throw new UnsupportedOperationException("changing default service not supported");
}
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index aca02bf..c820841 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -30,6 +30,9 @@
import android.net.NetworkPolicyManager;
import android.net.NetworkRequest;
import android.net.TrafficStats;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.UserHandle;
import android.text.format.DateUtils;
import android.util.ArraySet;
@@ -85,8 +88,14 @@
@GuardedBy("mLock")
private final ArraySet<Network> mAvailableNetworks = new ArraySet<>();
+ private static final int MSG_DATA_SAVER_TOGGLED = 0;
+ private static final int MSG_UID_RULES_CHANGES = 1;
+
+ private final Handler mHandler;
+
public ConnectivityController(JobSchedulerService service) {
super(service);
+ mHandler = new CcHandler(mContext.getMainLooper());
mConnManager = mContext.getSystemService(ConnectivityManager.class);
mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
@@ -545,11 +554,39 @@
private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() {
@Override
+ public void onRestrictBackgroundChanged(boolean restrictBackground) {
+ if (DEBUG) {
+ Slog.v(TAG, "onRestrictBackgroundChanged: " + restrictBackground);
+ }
+ mHandler.obtainMessage(MSG_DATA_SAVER_TOGGLED).sendToTarget();
+ }
+
+ @Override
public void onUidRulesChanged(int uid, int uidRules) {
if (DEBUG) {
Slog.v(TAG, "onUidRulesChanged: " + uid);
}
- updateTrackedJobs(uid, null);
+ mHandler.obtainMessage(MSG_UID_RULES_CHANGES, uid, 0).sendToTarget();
+ }
+ };
+
+ private class CcHandler extends Handler {
+ CcHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mLock) {
+ switch (msg.what) {
+ case MSG_DATA_SAVER_TOGGLED:
+ updateTrackedJobs(-1, null);
+ break;
+ case MSG_UID_RULES_CHANGES:
+ updateTrackedJobs(msg.arg1, null);
+ break;
+ }
+ }
}
};
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 270fbc6..3872523 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -17,9 +17,14 @@
package com.android.server.media.projection;
import android.Manifest;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
+import android.app.IProcessObserver;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.projection.IMediaProjection;
@@ -29,6 +34,8 @@
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Binder;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -38,6 +45,7 @@
import android.util.Slog;
import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
@@ -63,6 +71,8 @@
private final Context mContext;
private final AppOpsManager mAppOps;
+ private final ActivityManagerInternal mActivityManagerInternal;
+ private final PackageManager mPackageManager;
private final MediaRouter mMediaRouter;
private final MediaRouterCallback mMediaRouterCallback;
@@ -77,6 +87,8 @@
mDeathEaters = new ArrayMap<IBinder, IBinder.DeathRecipient>();
mCallbackDelegate = new CallbackDelegate();
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mPackageManager = mContext.getPackageManager();
mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mMediaRouterCallback = new MediaRouterCallback();
Watchdog.getInstance().addMonitor(this);
@@ -88,6 +100,21 @@
false /*allowIsolated*/);
mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mMediaRouterCallback,
MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
+ mActivityManagerInternal.registerProcessObserver(new IProcessObserver.Stub() {
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
+ }
+
+ @Override
+ public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {
+ MediaProjectionManagerService.this.handleForegroundServicesChanged(pid, uid,
+ serviceTypes);
+ }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {
+ }
+ });
}
@Override
@@ -105,6 +132,29 @@
synchronized (mLock) { /* check for deadlock */ }
}
+ /**
+ * Called when the set of active foreground service types for a given {@code uid / pid} changes.
+ * We will stop the active projection grant if its owner targets {@code Q} or higher and has no
+ * started foreground services of type {@code FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION}.
+ */
+ private void handleForegroundServicesChanged(int pid, int uid, int serviceTypes) {
+ synchronized (mLock) {
+ if (mProjectionGrant == null || mProjectionGrant.uid != uid) {
+ return;
+ }
+
+ if (mProjectionGrant.targetSdkVersion < VERSION_CODES.Q) {
+ return;
+ }
+
+ if ((serviceTypes & ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION) != 0) {
+ return;
+ }
+
+ mProjectionGrant.stop();
+ }
+ }
+
private void startProjectionLocked(final MediaProjection projection) {
if (mProjectionGrant != null) {
mProjectionGrant.stop();
@@ -229,10 +279,19 @@
if (packageName == null || packageName.isEmpty()) {
throw new IllegalArgumentException("package name must not be empty");
}
+
long callingToken = Binder.clearCallingIdentity();
+
MediaProjection projection;
try {
- projection = new MediaProjection(type, uid, packageName);
+ ApplicationInfo ai;
+ try {
+ ai = mPackageManager.getApplicationInfo(packageName, 0);
+ } catch (NameNotFoundException e) {
+ throw new IllegalArgumentException("No package matching :" + packageName);
+ }
+
+ projection = new MediaProjection(type, uid, packageName, ai.targetSdkVersion);
if (isPermanentGrant) {
mAppOps.setMode(AppOpsManager.OP_PROJECT_MEDIA,
projection.uid, projection.packageName, AppOpsManager.MODE_ALLOWED);
@@ -334,17 +393,19 @@
public final int uid;
public final String packageName;
public final UserHandle userHandle;
+ public final int targetSdkVersion;
private IMediaProjectionCallback mCallback;
private IBinder mToken;
private IBinder.DeathRecipient mDeathEater;
private int mType;
- public MediaProjection(int type, int uid, String packageName) {
+ MediaProjection(int type, int uid, String packageName, int targetSdkVersion) {
mType = type;
this.uid = uid;
this.packageName = packageName;
userHandle = new UserHandle(UserHandle.getUserId(uid));
+ this.targetSdkVersion = targetSdkVersion;
}
@Override // Binder call
@@ -400,6 +461,14 @@
+ " attempted to start already started MediaProjection");
return;
}
+
+ if (targetSdkVersion >= Build.VERSION_CODES.Q
+ && !mActivityManagerInternal.hasRunningForegroundService(
+ uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) {
+ throw new SecurityException("Media projections require a foreground service"
+ + " of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION");
+ }
+
mCallback = callback;
registerCallback(mCallback);
try {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index d9ab132..de93120 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -438,6 +438,8 @@
if (getAudioAttributes() != null) {
getAudioAttributes().writeToProto(proto, NotificationRecordProto.AUDIO_ATTRIBUTES);
}
+ proto.write(NotificationRecordProto.PACKAGE, sbn.getPackageName());
+ proto.write(NotificationRecordProto.DELEGATE_PACKAGE, sbn.getOpPkg());
proto.end(token);
}
@@ -458,6 +460,7 @@
pw.println(prefix + this);
prefix = prefix + " ";
pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
+ pw.println(prefix + "opPkg=" + sbn.getOpPkg());
pw.println(prefix + "icon=" + iconStr);
pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
pw.println(prefix + "pri=" + notification.priority);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 181b7a2..ad17549 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -123,6 +123,8 @@
/** Automatically destroy sessions older than this */
private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
+ /** Automatically destroy staged sessions that have not changed state in this time */
+ private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 7 * DateUtils.DAY_IN_MILLIS;
/** Upper bound on number of active sessions for a UID */
private static final long MAX_ACTIVE_SESSIONS = 1024;
/** Upper bound on number of historical sessions for a UID */
@@ -357,11 +359,19 @@
}
final long age = System.currentTimeMillis() - session.createdMillis;
-
+ final long timeSinceUpdate =
+ System.currentTimeMillis() - session.updatedMillis;
final boolean valid;
- if (age >= MAX_AGE_MILLIS) {
- Slog.w(TAG, "Abandoning old session first created at "
- + session.createdMillis);
+ if (session.isStaged()) {
+ if (timeSinceUpdate >= MAX_TIME_SINCE_UPDATE_MILLIS
+ && session.isStagedAndInTerminalState()) {
+ valid = false;
+ } else {
+ valid = true;
+ }
+ } else if (age >= MAX_AGE_MILLIS) {
+ Slog.w(TAG, "Abandoning old session created at "
+ + session.createdMillis);
valid = false;
} else {
valid = true;
@@ -1196,6 +1206,7 @@
}
public void onStagedSessionChanged(PackageInstallerSession session) {
+ session.markUpdated();
writeSessionsAsync();
if (mOkToSendBroadcasts) {
mPm.sendSessionUpdatedBroadcast(session.generateInfo(false),
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 6451b56..66b530f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -144,6 +144,7 @@
private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
private static final String ATTR_INSTALLER_UID = "installerUid";
private static final String ATTR_CREATED_MILLIS = "createdMillis";
+ private static final String ATTR_UPDATED_MILLIS = "updatedMillis";
private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
private static final String ATTR_PREPARED = "prepared";
@@ -199,6 +200,10 @@
private final Object mLock = new Object();
+ /** Timestamp of the last time this session changed state */
+ @GuardedBy("mLock")
+ long updatedMillis;
+
/** Uid of the creator of this session. */
private final int mOriginalInstallerUid;
@@ -422,6 +427,7 @@
mInstallerUid = installerUid;
this.params = params;
this.createdMillis = createdMillis;
+ this.updatedMillis = createdMillis;
this.stageDir = stageDir;
this.stageCid = stageCid;
if (childSessionIds != null) {
@@ -521,6 +527,13 @@
}
}
+ /** Returns true if a staged session has reached a final state and can be forgotten about */
+ public boolean isStagedAndInTerminalState() {
+ synchronized (mLock) {
+ return params.isStaged && (mStagedSessionApplied || mStagedSessionFailed);
+ }
+ }
+
@GuardedBy("mLock")
private void assertPreparedAndNotSealedLocked(String cookie) {
assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
@@ -1034,6 +1047,13 @@
}
}
+ /** Update the timestamp of when the staged session last changed state */
+ public void markUpdated() {
+ synchronized (mLock) {
+ this.updatedMillis = System.currentTimeMillis();
+ }
+ }
+
@Override
public void transfer(String packageName) {
Preconditions.checkNotNull(packageName);
@@ -2114,7 +2134,7 @@
private void destroyInternal() {
synchronized (mLock) {
mSealed = true;
- if (!params.isStaged) {
+ if (!params.isStaged || isStagedAndInTerminalState()) {
mDestroyed = true;
}
// Force shut down all bridges
@@ -2224,6 +2244,7 @@
mInstallerPackageName);
writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid);
writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis);
+ writeLongAttribute(out, ATTR_UPDATED_MILLIS, updatedMillis);
if (stageDir != null) {
writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
stageDir.getAbsolutePath());
@@ -2326,6 +2347,7 @@
final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid(
installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
+ long updatedMillis = readLongAttribute(in, ATTR_UPDATED_MILLIS);
final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
diff --git a/services/core/java/com/android/server/pm/PackageKeySetData.java b/services/core/java/com/android/server/pm/PackageKeySetData.java
index a9126c0..031b5ce 100644
--- a/services/core/java/com/android/server/pm/PackageKeySetData.java
+++ b/services/core/java/com/android/server/pm/PackageKeySetData.java
@@ -107,10 +107,7 @@
}
protected void removeAllDefinedKeySets() {
- final int aliasSize = mKeySetAliases.size();
- for (int i = 0; i < aliasSize; i++) {
- mKeySetAliases.removeAt(i);
- }
+ mKeySetAliases.erase();
}
protected boolean isUsingDefinedKeySets() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a679601..ec2ff28 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -20173,21 +20173,20 @@
}
@Override
- public String getContentCaptureServicePackageName() {
- String contentCaptureServiceName =
- mContext.getString(R.string.config_defaultContentCaptureService);
+ public String getSystemCaptionsServicePackageName() {
+ String flattenedSystemCaptionsServiceComponentName =
+ mContext.getString(R.string.config_defaultSystemCaptionsService);
- if (TextUtils.isEmpty(contentCaptureServiceName)) {
+ if (TextUtils.isEmpty(flattenedSystemCaptionsServiceComponentName)) {
return null;
}
- int separatorIndex = contentCaptureServiceName.indexOf("/");
-
- if (separatorIndex < 0) {
+ ComponentName systemCaptionsServiceComponentName =
+ ComponentName.unflattenFromString(flattenedSystemCaptionsServiceComponentName);
+ if (systemCaptionsServiceComponentName == null) {
return null;
}
-
- return contentCaptureServiceName.substring(0, separatorIndex);
+ return systemCaptionsServiceComponentName.getPackageName();
}
public String getIncidentReportApproverPackageName() {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index eced165..9782648 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -99,6 +99,7 @@
private static final String ATTR_ICON_RES_ID = "icon-res";
private static final String ATTR_ICON_RES_NAME = "icon-resname";
private static final String ATTR_BITMAP_PATH = "bitmap-path";
+ private static final String ATTR_LOCUS_ID = "locus-id";
private static final String ATTR_PERSON_NAME = "name";
private static final String ATTR_PERSON_URI = "uri";
@@ -1473,6 +1474,10 @@
ShortcutService.writeAttr(out, ATTR_DISABLED_REASON, si.getDisabledReason());
ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
si.getLastChangedTimestamp());
+ final LocusId locusId = si.getLocusId();
+ if (locusId != null) {
+ ShortcutService.writeAttr(out, ATTR_LOCUS_ID, si.getLocusId().getId());
+ }
if (forBackup) {
// Don't write icon information. Also drop the dynamic flag.
@@ -1612,6 +1617,7 @@
int iconResId;
String iconResName;
String bitmapPath;
+ final String locusIdString;
int backupVersionCode;
ArraySet<String> categories = null;
ArrayList<Person> persons = new ArrayList<>();
@@ -1638,6 +1644,7 @@
iconResId = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES_ID);
iconResName = ShortcutService.parseStringAttribute(parser, ATTR_ICON_RES_NAME);
bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
+ locusIdString = ShortcutService.parseStringAttribute(parser, ATTR_LOCUS_ID);
final int outerDepth = parser.getDepth();
int type;
@@ -1703,7 +1710,7 @@
flags |= ShortcutInfo.FLAG_SHADOW;
}
- LocusId locusId = null; // LocusId is not set on XML.
+ final LocusId locusId = locusIdString == null ? null : new LocusId(locusIdString);
return new ShortcutInfo(
userId, id, packageName, activityComponent, /* icon= */ null,
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e9077a9..0a17e130 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -748,11 +748,11 @@
grantSystemFixedPermissionsToSystemPackage("com.android.sharedstoragebackup", userId,
STORAGE_PERMISSIONS);
- // Content Capture Service
- String contentCaptureServicePackageName =
- mContext.getPackageManager().getContentCaptureServicePackageName();
- if (!TextUtils.isEmpty(contentCaptureServicePackageName)) {
- grantPermissionsToSystemPackage(contentCaptureServicePackageName, userId,
+ // System Captions Service
+ String systemCaptionsServicePackageName =
+ mContext.getPackageManager().getSystemCaptionsServicePackageName();
+ if (!TextUtils.isEmpty(systemCaptionsServicePackageName)) {
+ grantPermissionsToSystemPackage(systemCaptionsServicePackageName, userId,
MICROPHONE_PERMISSIONS);
}
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index 406cbc1..701e5af 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -269,7 +269,7 @@
*/
@VisibleForTesting
boolean isAttentionServiceSupported() {
- return mAttentionManager.isAttentionServiceSupported();
+ return mAttentionManager != null && mAttentionManager.isAttentionServiceSupported();
}
public void dump(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 1a82858..b81d969 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -486,6 +486,8 @@
log.setType(MetricsEvent.TYPE_OPEN);
log.setSubtype(why);
log.setLatency(interactiveChangeLatency);
+ log.addTaggedData(
+ MetricsEvent.FIELD_SCREEN_WAKE_REASON, mInteractiveChangeReason);
MetricsLogger.action(log);
EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);
mPolicy.finishedWakingUp(why);
@@ -513,6 +515,8 @@
log.setType(MetricsEvent.TYPE_CLOSE);
log.setSubtype(why);
log.setLatency(interactiveChangeLatency);
+ log.addTaggedData(
+ MetricsEvent.FIELD_SCREEN_SLEEP_REASON, mInteractiveChangeReason);
MetricsLogger.action(log);
EventLogTags.writePowerScreenState(0, why, 0, 0, interactiveChangeLatency);
mPolicy.finishedGoingToSleep(why);
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index c3582ea..d632749 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -16,6 +16,7 @@
package com.android.server.rollback;
+import android.Manifest;
import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
@@ -24,6 +25,7 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
@@ -42,6 +44,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.IntArray;
import android.util.Log;
@@ -220,9 +223,7 @@
@Override
public ParceledListSlice getAvailableRollbacks() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_ROLLBACKS,
- "getAvailableRollbacks");
+ enforceManageRollbacks("getAvailableRollbacks");
synchronized (mLock) {
ensureRollbackDataLoadedLocked();
@@ -239,9 +240,7 @@
@Override
public ParceledListSlice<RollbackInfo> getRecentlyExecutedRollbacks() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_ROLLBACKS,
- "getRecentlyExecutedRollbacks");
+ enforceManageRollbacks("getRecentlyCommittedRollbacks");
synchronized (mLock) {
ensureRollbackDataLoadedLocked();
@@ -259,9 +258,7 @@
@Override
public void commitRollback(int rollbackId, ParceledListSlice causePackages,
String callerPackageName, IntentSender statusReceiver) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_ROLLBACKS,
- "executeRollback");
+ enforceManageRollbacks("executeRollback");
final int callingUid = Binder.getCallingUid();
AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
@@ -459,11 +456,8 @@
Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
- // TODO: This call emits the warning "Calling a method in the
- // system process without a qualified user". Fix that.
- // TODO: Limit this to receivers holding the
- // MANAGE_ROLLBACKS permission?
- mContext.sendBroadcast(broadcast);
+ mContext.sendBroadcastAsUser(broadcast, UserHandle.SYSTEM,
+ Manifest.permission.MANAGE_ROLLBACKS);
});
}
);
@@ -484,7 +478,7 @@
@Override
public void reloadPersistedData() {
mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_ROLLBACKS,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
"reloadPersistedData");
synchronized (mLock) {
@@ -499,7 +493,7 @@
@Override
public void expireRollbackForPackage(String packageName) {
mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_ROLLBACKS,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
"expireRollbackForPackage");
synchronized (mLock) {
ensureRollbackDataLoadedLocked();
@@ -894,12 +888,19 @@
Log.i(TAG, "Enabling rollback for install of " + packageName
+ ", session:" + session.sessionId);
+ String installerPackageName = session.getInstallerPackageName();
+ if (!enableRollbackAllowed(installerPackageName, packageName)) {
+ Log.e(TAG, "Installer " + installerPackageName
+ + " is not allowed to enable rollback on " + packageName);
+ return false;
+ }
+
VersionedPackage newVersion = new VersionedPackage(packageName, newPackage.versionCode);
final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
// Get information about the currently installed package.
PackageManager pm = mContext.getPackageManager();
- PackageInfo pkgInfo = null;
+ final PackageInfo pkgInfo;
try {
pkgInfo = pm.getPackageInfo(packageName, isApex ? PackageManager.MATCH_APEX : 0);
} catch (PackageManager.NameNotFoundException e) {
@@ -1086,6 +1087,44 @@
}
/**
+ * Returns true if the installer is allowed to enable rollback for the
+ * given named package, false otherwise.
+ */
+ private boolean enableRollbackAllowed(String installerPackageName, String packageName) {
+ if (installerPackageName == null) {
+ return false;
+ }
+
+ PackageManager pm = mContext.getPackageManager();
+ boolean manageRollbacksGranted = pm.checkPermission(
+ Manifest.permission.MANAGE_ROLLBACKS,
+ installerPackageName) == PackageManager.PERMISSION_GRANTED;
+
+ boolean testManageRollbacksGranted = pm.checkPermission(
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
+ installerPackageName) == PackageManager.PERMISSION_GRANTED;
+
+ // For now only allow rollbacks for modules or for testing.
+ return (isModule(packageName) && manageRollbacksGranted)
+ || testManageRollbacksGranted;
+ }
+
+ /**
+ * Returns true if the package name is the name of a module.
+ */
+ private boolean isModule(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ final ModuleInfo moduleInfo;
+ try {
+ moduleInfo = pm.getModuleInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+
+ return moduleInfo != null;
+ }
+
+ /**
* Gets the version of the package currently installed.
* Returns null if the package is not currently installed.
*/
@@ -1311,4 +1350,15 @@
}
}
}
+
+ private void enforceManageRollbacks(@NonNull String message) {
+ if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
+ Manifest.permission.MANAGE_ROLLBACKS))
+ && (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
+ Manifest.permission.TEST_MANAGE_ROLLBACKS))) {
+ throw new SecurityException(message + " requires "
+ + Manifest.permission.MANAGE_ROLLBACKS + " or "
+ + Manifest.permission.TEST_MANAGE_ROLLBACKS);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index a33b454d..9d6efb4 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -1173,7 +1173,17 @@
}
private void releaseSelfIfNeeded() {
- if (mStacks.isEmpty() && mRemoved) {
+ if (!mRemoved || mDisplayContent == null) {
+ return;
+ }
+
+ final ActivityStack stack = mStacks.size() == 1 ? mStacks.get(0) : null;
+ if (stack != null && stack.isActivityTypeHome() && stack.getAllTasks().isEmpty()) {
+ // Release this display if an empty home stack is the only thing left.
+ // Since it is the last stack, this display will be released along with the stack
+ // removal.
+ stack.remove();
+ } else if (mStacks.isEmpty()) {
mDisplayContent.removeIfPossible();
mDisplayContent = null;
mRootActivityContainer.removeChild(this);
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index ad98970..4c9b80b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -3053,7 +3053,17 @@
ActivityOptions.abort(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityInNextFocusableStack: " + reason + ", go home");
- return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
+ if (isActivityTypeHome()) {
+ // resumeTopActivityUncheckedLocked has been prevented to run recursively. Post a
+ // runnable to resume home since we are currently in the process of resuming top
+ // activity in home stack.
+ // See {@link #mInResumeTopActivity}.
+ mService.mH.post(
+ () -> mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId));
+ return true;
+ } else {
+ return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
+ }
}
/** Returns the position the input task should be placed in this stack. */
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index f6326957..75e34fb 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
@@ -638,6 +639,39 @@
return transit;
}
+ /**
+ * Identifies whether the current transition occurs within a single task or not. This is used
+ * to determine whether animations should be clipped to the task bounds instead of stack bounds.
+ */
+ @VisibleForTesting
+ boolean isTransitWithinTask(int transit, Task task) {
+ if (task == null
+ || !mDisplayContent.mChangingApps.isEmpty()) {
+ // if there is no task, then we can't constrain to the task.
+ // if anything is changing, it can animate outside its task.
+ return false;
+ }
+ if (!(transit == TRANSIT_ACTIVITY_OPEN
+ || transit == TRANSIT_ACTIVITY_CLOSE
+ || transit == TRANSIT_ACTIVITY_RELAUNCH)) {
+ // only activity-level transitions will be within-task.
+ return false;
+ }
+ // check that all components are in the task.
+ for (AppWindowToken activity : mDisplayContent.mOpeningApps) {
+ Task activityTask = activity.getTask();
+ if (activityTask != task) {
+ return false;
+ }
+ }
+ for (AppWindowToken activity : mDisplayContent.mClosingApps) {
+ if (activity.getTask() != task) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) {
for (int i = apps.size() - 1; i >= 0; i--) {
if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ea3a7d5..3f29181 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2705,16 +2705,21 @@
// If the animation needs to be cropped then an animation bounds layer is created as a child
// of the pinned stack or animation layer. The leash is then reparented to this new layer.
if (mNeedsAnimationBoundsLayer) {
- final TaskStack stack = getStack();
- if (stack == null) {
- return;
+ mTmpRect.setEmpty();
+ final Task task = getTask();
+ if (getDisplayContent().mAppTransitionController.isTransitWithinTask(
+ getTransit(), task)) {
+ task.getBounds(mTmpRect);
+ } else {
+ final TaskStack stack = getStack();
+ if (stack == null) {
+ return;
+ }
+ // Set clip rect to stack bounds.
+ stack.getBounds(mTmpRect);
}
mAnimationBoundsLayer = createAnimationBoundsLayer(t);
- // Set clip rect to stack bounds.
- mTmpRect.setEmpty();
- stack.getBounds(mTmpRect);
-
// Crop to stack bounds.
t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b00cafd..bec72f5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -130,6 +130,7 @@
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
import android.animation.AnimationHandler;
import android.annotation.CallSuper;
@@ -153,6 +154,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -165,6 +167,7 @@
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
+import android.view.ISystemGestureExclusionListener;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputWindowHandle;
@@ -312,6 +315,10 @@
private DisplayRotation mDisplayRotation;
DisplayFrames mDisplayFrames;
+ private final RemoteCallbackList<ISystemGestureExclusionListener>
+ mSystemGestureExclusionListeners = new RemoteCallbackList<>();
+ private final Region mSystemGestureExclusion = new Region();
+
/**
* For default display it contains real metrics, empty for others.
* @see WindowManagerService#createWatermarkInTransaction()
@@ -2818,6 +2825,14 @@
mWallpaperController.dump(pw, " ");
pw.println();
+ pw.print("mSystemGestureExclusion=");
+ if (mSystemGestureExclusionListeners.getRegisteredCallbackCount() > 0) {
+ pw.println(mSystemGestureExclusion);
+ } else {
+ pw.println("<no lstnrs>");
+ }
+
+ pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
@@ -4951,6 +4966,100 @@
}
/**
+ * Updates the display's system gesture exclusion.
+ *
+ * @return true, if the exclusion changed.
+ */
+ boolean updateSystemGestureExclusion() {
+ if (mSystemGestureExclusionListeners.getRegisteredCallbackCount() == 0) {
+ // No one's interested anyways.
+ return false;
+ }
+
+ final Region systemGestureExclusion = calculateSystemGestureExclusion();
+ try {
+ if (mSystemGestureExclusion.equals(systemGestureExclusion)) {
+ return false;
+ }
+ mSystemGestureExclusion.set(systemGestureExclusion);
+ for (int i = mSystemGestureExclusionListeners.beginBroadcast() - 1; i >= 0; --i) {
+ try {
+ mSystemGestureExclusionListeners.getBroadcastItem(i)
+ .onSystemGestureExclusionChanged(mDisplayId, systemGestureExclusion);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify SystemGestureExclusionListener", e);
+ }
+ }
+ mSystemGestureExclusionListeners.finishBroadcast();
+ return true;
+ } finally {
+ systemGestureExclusion.recycle();
+ }
+ }
+
+ @VisibleForTesting
+ Region calculateSystemGestureExclusion() {
+ final Region global = Region.obtain();
+ final Region touchableRegion = Region.obtain();
+ final Region local = Region.obtain();
+
+ // Traverse all windows bottom up to assemble the gesture exclusion rects.
+ // For each window, we only take the rects that fall within its touchable region.
+ forAllWindows(w -> {
+ if (w.cantReceiveTouchInput() || !w.isVisible()
+ || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0) {
+ return;
+ }
+ final boolean modal =
+ (w.mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
+
+ // Only keep the exclusion zones from the windows behind where the current window
+ // isn't touchable.
+ w.getTouchableRegion(touchableRegion);
+ global.op(touchableRegion, Op.DIFFERENCE);
+
+ rectListToRegion(w.getSystemGestureExclusion(), local);
+
+ // Transform to display coordinates
+ local.scale(w.mGlobalScale);
+ final Rect frame = w.getWindowFrames().mFrame;
+ local.translate(frame.left, frame.top);
+
+ // A window can only exclude system gestures where it is actually touchable
+ local.op(touchableRegion, Op.INTERSECT);
+
+ global.op(local, Op.UNION);
+ }, false /* topToBottom */);
+ local.recycle();
+ touchableRegion.recycle();
+ return global;
+ }
+
+ void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener) {
+ mSystemGestureExclusionListeners.register(listener);
+ final boolean changed;
+ if (mSystemGestureExclusionListeners.getRegisteredCallbackCount() == 1) {
+ changed = updateSystemGestureExclusion();
+ } else {
+ changed = false;
+ }
+
+ if (!changed) {
+ // If updateSystemGestureExclusion changed the exclusion, it will already have
+ // notified the listener. Otherwise, we'll do it here.
+ try {
+ listener.onSystemGestureExclusionChanged(mDisplayId, mSystemGestureExclusion);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify SystemGestureExclusionListener during register", e);
+ }
+ }
+ }
+
+ void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener) {
+ mSystemGestureExclusionListeners.unregister(listener);
+ }
+
+ /**
* Create a portal window handle for input. This window transports any touch to the display
* indicated by {@link InputWindowHandle#portalToDisplayId} if the touch hits this window.
*
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 8f4e842..ed5f665 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -682,6 +682,7 @@
// Finally update all input windows now that the window changes have stabilized.
forAllDisplays(dc -> {
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
+ dc.updateSystemGestureExclusion();
});
mWmService.setHoldScreenLocked(mHoldScreen);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index dc8c7b7..9b634f9 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -57,6 +57,7 @@
import java.io.PrintWriter;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -314,6 +315,16 @@
}
}
+ @Override
+ public void reportSystemGestureExclusionChanged(IWindow window, List<Rect> exclusionRects) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mService.reportSystemGestureExclusionChanged(this, window, exclusionRects);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private void actionOnWallpaper(IBinder window,
BiConsumer<WallpaperController, WindowState> action) {
final WindowState windowState = mService.windowForClientLocked(this, window, true);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4a9d798f..7751560 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -202,6 +202,7 @@
import android.view.IPinnedStackListener;
import android.view.IRecentsAnimationRunner;
import android.view.IRotationWatcher;
+import android.view.ISystemGestureExclusionListener;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindow;
import android.view.IWindowId;
@@ -274,6 +275,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
+import java.util.List;
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
@@ -3817,6 +3819,42 @@
}
@Override
+ public void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener,
+ int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ throw new IllegalArgumentException("Trying to register visibility event "
+ + "for invalid display: " + displayId);
+ }
+ displayContent.registerSystemGestureExclusionListener(listener);
+ }
+ }
+
+ @Override
+ public void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener,
+ int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ throw new IllegalArgumentException("Trying to register visibility event "
+ + "for invalid display: " + displayId);
+ }
+ displayContent.unregisterSystemGestureExclusionListener(listener);
+ }
+ }
+
+ void reportSystemGestureExclusionChanged(Session session, IWindow window,
+ List<Rect> exclusionRects) {
+ synchronized (mGlobalLock) {
+ final WindowState win = windowForClientLocked(session, window, true);
+ if (win.setSystemGestureExclusion(exclusionRects)) {
+ win.getDisplayContent().updateSystemGestureExclusion();
+ }
+ }
+ }
+
+ @Override
public void registerDisplayFoldListener(IDisplayFoldListener listener) {
mPolicy.registerDisplayFoldListener(listener);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ee445d8..600178f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -205,6 +205,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.List;
import java.util.function.Predicate;
/** A window in the window manager. */
@@ -363,6 +364,13 @@
*/
private final Rect mInsetFrame = new Rect();
+ /**
+ * List of rects where system gestures should be ignored.
+ *
+ * Coordinates are relative to the window's position.
+ */
+ private final List<Rect> mExclusionRects = new ArrayList<>();
+
// If a window showing a wallpaper: the requested offset for the
// wallpaper; if a wallpaper window: the currently applied offset.
float mWallpaperX = -1;
@@ -612,6 +620,24 @@
}
}
+ List<Rect> getSystemGestureExclusion() {
+ return mExclusionRects;
+ }
+
+ /**
+ * Sets the system gesture exclusion rects.
+ *
+ * @return {@code true} if anything changed
+ */
+ boolean setSystemGestureExclusion(List<Rect> exclusionRects) {
+ if (mExclusionRects.equals(exclusionRects)) {
+ return false;
+ }
+ mExclusionRects.clear();
+ mExclusionRects.addAll(exclusionRects);
+ return true;
+ }
+
interface PowerManagerWrapper {
void wakeUp(long time, @WakeReason int reason, String details);
diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java
new file mode 100644
index 0000000..1458440
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java
@@ -0,0 +1,45 @@
+/*
+ * 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.server.wm.utils;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+
+import java.util.List;
+
+/**
+ * Utility methods to handle Regions.
+ */
+public class RegionUtils {
+
+ private RegionUtils() {}
+
+
+ /**
+ * Converts a list of rects into a {@code Region}.
+ *
+ * @param rects the list of rects to convert
+ * @param outRegion the Region to set to the list of rects
+ */
+ public static void rectListToRegion(List<Rect> rects, Region outRegion) {
+ outRegion.setEmpty();
+ final int n = rects.size();
+ for (int i = 0; i < n; i++) {
+ outRegion.union(rects.get(i));
+ }
+ }
+}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index a4955f0..a6e9fdd 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -119,7 +119,6 @@
using android::hardware::gnss::V1_0::IAGnssRilCallback;
using android::hardware::gnss::V1_0::IGnssBatching;
using android::hardware::gnss::V1_0::IGnssBatchingCallback;
-using android::hardware::gnss::V1_0::IGnssDebug;
using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
using android::hardware::gnss::V1_0::IGnssGeofencing;
using android::hardware::gnss::V1_0::IGnssNavigationMessage;
@@ -143,9 +142,12 @@
using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss;
using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss;
using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss;
+using IGnssCallback_V1_0 = android::hardware::gnss::V1_0::IGnssCallback;
using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
+using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug;
+using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug;
using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
@@ -191,7 +193,8 @@
sp<IAGnss_V1_0> agnssIface = nullptr;
sp<IAGnss_V2_0> agnssIface_V2_0 = nullptr;
sp<IGnssBatching> gnssBatchingIface = nullptr;
-sp<IGnssDebug> gnssDebugIface = nullptr;
+sp<IGnssDebug_V1_0> gnssDebugIface = nullptr;
+sp<IGnssDebug_V2_0> gnssDebugIface_V2_0 = nullptr;
sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr;
sp<IGnssConfiguration_V2_0> gnssConfigurationIface_V2_0 = nullptr;
@@ -548,7 +551,9 @@
struct GnssCallback : public IGnssCallback {
Return<void> gnssLocationCb(const GnssLocation_V1_0& location) override;
Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue status) override;
- Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override;
+ Return<void> gnssSvStatusCb(const IGnssCallback_V1_0::GnssSvStatus& svStatus) override {
+ return gnssSvStatusCbImpl(svStatus);
+ }
Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override;
Return<void> gnssAcquireWakelockCb() override;
@@ -566,16 +571,47 @@
override;
Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
Return<void> gnssLocationCb_2_0(const GnssLocation_V2_0& location) override;
-
- // Templated implementation for gnnsLocationCb and gnnsLocationCb_2_0.
- template <class T>
- Return<void> gnssLocationCbImpl(const T& location);
+ Return<void> gnssSvStatusCb_2_0(const hidl_vec<IGnssCallback::GnssSvInfo>& svInfoList) override {
+ return gnssSvStatusCbImpl(svInfoList);
+ }
Return<void> gnssSetCapabilitesCbImpl(uint32_t capabilities, bool hasSubHalCapabilityFlags);
// TODO: Reconsider allocation cost vs threadsafety on these statics
static const char* sNmeaString;
static size_t sNmeaStringLength;
+private:
+ template<class T>
+ Return<void> gnssLocationCbImpl(const T& location);
+
+ template<class T>
+ Return<void> gnssSvStatusCbImpl(const T& svStatus);
+
+ uint32_t getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
+ return svStatus.numSvs;
+ }
+
+ uint32_t getGnssSvInfoListSize(const hidl_vec<IGnssCallback::GnssSvInfo>& svInfoList) {
+ return svInfoList.size();
+ }
+
+ const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
+ const IGnssCallback_V1_0::GnssSvStatus& svStatus, size_t i) {
+ return svStatus.gnssSvList.data()[i];
+ }
+
+ const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
+ const hidl_vec<IGnssCallback::GnssSvInfo>& svInfoList, size_t i) {
+ return svInfoList[i].v1_0;
+ }
+
+ uint32_t getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus, size_t i) {
+ return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation);
+ }
+
+ uint32_t getConstellationType(const hidl_vec<IGnssCallback::GnssSvInfo>& svInfoList, size_t i) {
+ return static_cast<uint32_t>(svInfoList[i].constellation);
+ }
};
Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
@@ -623,10 +659,11 @@
return Void();
}
-Return<void> GnssCallback::gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) {
+template<class T>
+Return<void> GnssCallback::gnssSvStatusCbImpl(const T& svStatus) {
JNIEnv* env = getJniEnv();
- uint32_t listSize = svStatus.numSvs;
+ uint32_t listSize = getGnssSvInfoListSize(svStatus);
if (listSize > static_cast<uint32_t>(
android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)) {
ALOGD("Too many satellites %u. Clamps to %u.", listSize,
@@ -655,9 +692,9 @@
CONSTELLATION_TYPE_SHIFT_WIDTH = 4
};
- const IGnssCallback::GnssSvInfo& info = svStatus.gnssSvList.data()[i];
+ const IGnssCallback_V1_0::GnssSvInfo& info = getGnssSvInfoOfIndex(svStatus, i);
svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
- (static_cast<uint32_t>(info.constellation) << CONSTELLATION_TYPE_SHIFT_WIDTH) |
+ (getConstellationType(svStatus, i) << CONSTELLATION_TYPE_SHIFT_WIDTH) |
static_cast<uint32_t>(info.svFlag);
cn0s[i] = info.cN0Dbhz;
elev[i] = info.elevationDegrees;
@@ -1079,6 +1116,9 @@
// Overwrite with v2_0.state since v2_0->v1_1->v1_0.state is deprecated.
SET(State, static_cast<int32_t>(measurement_V2_0->state));
+
+ // Overwrite with v2_0.constellation since v2_0->v1_1->v1_0.constellation is deprecated.
+ SET(ConstellationType, static_cast<int32_t>(measurement_V2_0->constellation));
}
jobject GnssMeasurementCallback::translateGnssClock(
@@ -1656,11 +1696,21 @@
}
}
- auto gnssDebug = gnssHal->getExtensionGnssDebug();
- if (!gnssDebug.isOk()) {
- ALOGD("Unable to get a handle to GnssDebug");
+ if (gnssHal_V2_0 != nullptr) {
+ auto gnssDebug = gnssHal_V2_0->getExtensionGnssDebug_2_0();
+ if (!gnssDebug.isOk()) {
+ ALOGD("Unable to get a handle to GnssDebug_V2_0");
+ } else {
+ gnssDebugIface_V2_0 = gnssDebug;
+ gnssDebugIface = gnssDebugIface_V2_0;
+ }
} else {
- gnssDebugIface = gnssDebug;
+ auto gnssDebug = gnssHal->getExtensionGnssDebug();
+ if (!gnssDebug.isOk()) {
+ ALOGD("Unable to get a handle to GnssDebug");
+ } else {
+ gnssDebugIface = gnssDebug;
+ }
}
auto gnssNi = gnssHal->getExtensionGnssNi();
@@ -2189,6 +2239,76 @@
gnssNiIface->respond(notifId, static_cast<IGnssNiCallback::GnssUserResponseType>(response));
}
+const IGnssDebug_V1_0::SatelliteData& getSatelliteData(const hidl_vec<IGnssDebug_V1_0::SatelliteData>& satelliteDataArray, size_t i) {
+ return satelliteDataArray[i];
+}
+
+const IGnssDebug_V1_0::SatelliteData& getSatelliteData(const hidl_vec<IGnssDebug_V2_0::SatelliteData>& satelliteDataArray, size_t i) {
+ return satelliteDataArray[i].v1_0;
+}
+
+template<class T>
+uint32_t getConstellationType(const hidl_vec<T>& satelliteDataArray, size_t i) {
+ return static_cast<uint32_t>(satelliteDataArray[i].constellation);
+}
+
+template<class T>
+static jstring parseDebugData(JNIEnv* env, std::stringstream& internalState, const T& data) {
+ internalState << "Gnss Location Data:: ";
+ if (!data.position.valid) {
+ internalState << "not valid";
+ } else {
+ internalState << "LatitudeDegrees: " << data.position.latitudeDegrees
+ << ", LongitudeDegrees: " << data.position.longitudeDegrees
+ << ", altitudeMeters: " << data.position.altitudeMeters
+ << ", speedMetersPerSecond: " << data.position.speedMetersPerSec
+ << ", bearingDegrees: " << data.position.bearingDegrees
+ << ", horizontalAccuracyMeters: "
+ << data.position.horizontalAccuracyMeters
+ << ", verticalAccuracyMeters: " << data.position.verticalAccuracyMeters
+ << ", speedAccuracyMetersPerSecond: "
+ << data.position.speedAccuracyMetersPerSecond
+ << ", bearingAccuracyDegrees: " << data.position.bearingAccuracyDegrees
+ << ", ageSeconds: " << data.position.ageSeconds;
+ }
+ internalState << std::endl;
+
+ internalState << "Gnss Time Data:: timeEstimate: " << data.time.timeEstimate
+ << ", timeUncertaintyNs: " << data.time.timeUncertaintyNs
+ << ", frequencyUncertaintyNsPerSec: "
+ << data.time.frequencyUncertaintyNsPerSec << std::endl;
+
+ if (data.satelliteDataArray.size() != 0) {
+ internalState << "Satellite Data for " << data.satelliteDataArray.size()
+ << " satellites:: " << std::endl;
+ }
+
+ internalState << "constell: 1=GPS, 2=SBAS, 3=GLO, 4=QZSS, 5=BDS, 6=GAL, 7=IRNSS; "
+ << "ephType: 0=Eph, 1=Alm, 2=Unk; "
+ << "ephSource: 0=Demod, 1=Supl, 2=Server, 3=Unk; "
+ << "ephHealth: 0=Good, 1=Bad, 2=Unk" << std::endl;
+ for (size_t i = 0; i < data.satelliteDataArray.size(); i++) {
+ IGnssDebug_V1_0::SatelliteData satelliteData =
+ getSatelliteData(data.satelliteDataArray, i);
+ internalState << "constell: "
+ << getConstellationType(data.satelliteDataArray, i)
+ << ", svid: " << std::setw(3) << satelliteData.svid
+ << ", serverPredAvail: "
+ << satelliteData.serverPredictionIsAvailable
+ << ", serverPredAgeSec: " << std::setw(7)
+ << satelliteData.serverPredictionAgeSeconds
+ << ", ephType: "
+ << static_cast<uint32_t>(satelliteData.ephemerisType)
+ << ", ephSource: "
+ << static_cast<uint32_t>(satelliteData.ephemerisSource)
+ << ", ephHealth: "
+ << static_cast<uint32_t>(satelliteData.ephemerisHealth)
+ << ", ephAgeSec: " << std::setw(7)
+ << satelliteData.ephemerisAgeSeconds << std::endl;
+ }
+ return (jstring) env->NewStringUTF(internalState.str().c_str());
+}
+
static jstring android_location_GnssLocationProvider_get_internal_state(JNIEnv* env,
jobject /* obj */) {
jstring result = nullptr;
@@ -2200,65 +2320,19 @@
if (gnssDebugIface == nullptr) {
internalState << "Gnss Debug Interface not available" << std::endl;
- } else {
- IGnssDebug::DebugData data;
- gnssDebugIface->getDebugData([&data](const IGnssDebug::DebugData& debugData) {
+ } else if (gnssDebugIface_V2_0 != nullptr) {
+ IGnssDebug_V2_0::DebugData data;
+ gnssDebugIface_V2_0->getDebugData_2_0([&data](const IGnssDebug_V2_0::DebugData& debugData) {
data = debugData;
});
-
- internalState << "Gnss Location Data:: ";
- if (!data.position.valid) {
- internalState << "not valid";
- } else {
- internalState << "LatitudeDegrees: " << data.position.latitudeDegrees
- << ", LongitudeDegrees: " << data.position.longitudeDegrees
- << ", altitudeMeters: " << data.position.altitudeMeters
- << ", speedMetersPerSecond: " << data.position.speedMetersPerSec
- << ", bearingDegrees: " << data.position.bearingDegrees
- << ", horizontalAccuracyMeters: "
- << data.position.horizontalAccuracyMeters
- << ", verticalAccuracyMeters: " << data.position.verticalAccuracyMeters
- << ", speedAccuracyMetersPerSecond: "
- << data.position.speedAccuracyMetersPerSecond
- << ", bearingAccuracyDegrees: " << data.position.bearingAccuracyDegrees
- << ", ageSeconds: " << data.position.ageSeconds;
- }
- internalState << std::endl;
-
- internalState << "Gnss Time Data:: timeEstimate: " << data.time.timeEstimate
- << ", timeUncertaintyNs: " << data.time.timeUncertaintyNs
- << ", frequencyUncertaintyNsPerSec: "
- << data.time.frequencyUncertaintyNsPerSec << std::endl;
-
- if (data.satelliteDataArray.size() != 0) {
- internalState << "Satellite Data for " << data.satelliteDataArray.size()
- << " satellites:: " << std::endl;
- }
-
- internalState << "constell: 1=GPS, 2=SBAS, 3=GLO, 4=QZSS, 5=BDS, 6=GAL; "
- << "ephType: 0=Eph, 1=Alm, 2=Unk; "
- << "ephSource: 0=Demod, 1=Supl, 2=Server, 3=Unk; "
- << "ephHealth: 0=Good, 1=Bad, 2=Unk" << std::endl;
- for (size_t i = 0; i < data.satelliteDataArray.size(); i++) {
- internalState << "constell: "
- << static_cast<uint32_t>(data.satelliteDataArray[i].constellation)
- << ", svid: " << std::setw(3) << data.satelliteDataArray[i].svid
- << ", serverPredAvail: "
- << data.satelliteDataArray[i].serverPredictionIsAvailable
- << ", serverPredAgeSec: " << std::setw(7)
- << data.satelliteDataArray[i].serverPredictionAgeSeconds
- << ", ephType: "
- << static_cast<uint32_t>(data.satelliteDataArray[i].ephemerisType)
- << ", ephSource: "
- << static_cast<uint32_t>(data.satelliteDataArray[i].ephemerisSource)
- << ", ephHealth: "
- << static_cast<uint32_t>(data.satelliteDataArray[i].ephemerisHealth)
- << ", ephAgeSec: " << std::setw(7)
- << data.satelliteDataArray[i].ephemerisAgeSeconds << std::endl;
- }
+ result = parseDebugData(env, internalState, data);
+ } else {
+ IGnssDebug_V1_0::DebugData data;
+ gnssDebugIface->getDebugData([&data](const IGnssDebug_V1_0::DebugData& debugData) {
+ data = debugData;
+ });
+ result = parseDebugData(env, internalState, data);
}
-
- result = env->NewStringUTF(internalState.str().c_str());
return result;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 39af565..419f52c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1230,6 +1230,7 @@
}
startContentCaptureService(context);
+ startAttentionService(context);
// App prediction manager service
traceBeginAndSlog("StartAppPredictionService");
@@ -1284,10 +1285,6 @@
traceEnd();
}
- traceBeginAndSlog("StartAttentionManagerService");
- mSystemServiceManager.startService(AttentionManagerService.class);
- traceEnd();
-
traceBeginAndSlog("StartNetworkScoreService");
mSystemServiceManager.startService(NetworkScoreService.Lifecycle.class);
traceEnd();
@@ -2260,6 +2257,17 @@
traceEnd();
}
+ private void startAttentionService(@NonNull Context context) {
+ if (!AttentionManagerService.isServiceConfigured(context)) {
+ Slog.d(TAG, "AttentionService is not configured on this device");
+ return;
+ }
+
+ traceBeginAndSlog("StartAttentionManagerService");
+ mSystemServiceManager.startService(AttentionManagerService.class);
+ traceEnd();
+ }
+
static final void startSystemUi(Context context, WindowManagerService windowManager) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 6386b3b3..7c91b64 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -15,7 +15,10 @@
*/
package com.android.server;
+import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+import static android.app.AlarmManager.RTC;
+import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -254,14 +257,22 @@
}
private void setTestAlarm(int type, long triggerTime, PendingIntent operation) {
+ setTestAlarm(type, triggerTime, operation, TEST_CALLING_UID);
+ }
+
+ private void setTestAlarm(int type, long triggerTime, PendingIntent operation, int callingUid) {
mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, 0,
operation, null, "test", AlarmManager.FLAG_STANDALONE, null, null,
- TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ callingUid, TEST_CALLING_PACKAGE);
}
private PendingIntent getNewMockPendingIntent() {
+ return getNewMockPendingIntent(TEST_CALLING_UID);
+ }
+
+ private PendingIntent getNewMockPendingIntent(int mockUid) {
final PendingIntent mockPi = mock(PendingIntent.class, Answers.RETURNS_DEEP_STUBS);
- when(mockPi.getCreatorUid()).thenReturn(TEST_CALLING_UID);
+ when(mockPi.getCreatorUid()).thenReturn(mockUid);
when(mockPi.getCreatorPackage()).thenReturn(TEST_CALLING_PACKAGE);
return mockPi;
}
@@ -724,6 +735,91 @@
verify(mMockContext).sendBroadcastAsUser(mService.mTimeTickIntent, UserHandle.ALL);
}
+ @Test
+ public void alarmCountKeyedOnCallingUid() {
+ final int mockCreatorUid = 431412;
+ final PendingIntent pi = getNewMockPendingIntent(mockCreatorUid);
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + 5, pi);
+ assertEquals(1, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ assertEquals(-1, mService.mAlarmsPerUid.get(mockCreatorUid, -1));
+ }
+
+ @Test
+ public void alarmCountOnSet() {
+ final int numAlarms = 103;
+ final int[] types = {RTC_WAKEUP, RTC, ELAPSED_REALTIME_WAKEUP, ELAPSED_REALTIME};
+ for (int i = 1; i <= numAlarms; i++) {
+ setTestAlarm(types[i % 4], mNowElapsedTest + i, getNewMockPendingIntent());
+ assertEquals(i, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ }
+ }
+
+ @Test
+ public void alarmCountOnExpiration() throws InterruptedException {
+ final int numAlarms = 8; // This test is slow
+ for (int i = 0; i < numAlarms; i++) {
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10, getNewMockPendingIntent());
+ }
+ int expired = 0;
+ while (expired < numAlarms) {
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ expired++;
+ assertEquals(numAlarms - expired, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
+ }
+ }
+
+ @Test
+ public void alarmCountOnUidRemoved() {
+ final int numAlarms = 10;
+ for (int i = 0; i < numAlarms; i++) {
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10, getNewMockPendingIntent());
+ }
+ assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ mService.removeLocked(TEST_CALLING_UID);
+ assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
+ }
+
+ @Test
+ public void alarmCountOnPackageRemoved() {
+ final int numAlarms = 10;
+ for (int i = 0; i < numAlarms; i++) {
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10, getNewMockPendingIntent());
+ }
+ assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ mService.removeLocked(TEST_CALLING_PACKAGE);
+ assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
+ }
+
+ @Test
+ public void alarmCountOnUserRemoved() {
+ final int mockUserId = 15;
+ final int numAlarms = 10;
+ for (int i = 0; i < numAlarms; i++) {
+ int mockUid = UserHandle.getUid(mockUserId, 1234 + i);
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10,
+ getNewMockPendingIntent(mockUid), mockUid);
+ }
+ assertEquals(numAlarms, mService.mAlarmsPerUid.size());
+ mService.removeUserLocked(mockUserId);
+ assertEquals(0, mService.mAlarmsPerUid.size());
+ }
+
+ @Test
+ public void alarmCountOnAlarmRemoved() {
+ final int numAlarms = 10;
+ final PendingIntent[] pis = new PendingIntent[numAlarms];
+ for (int i = 0; i < numAlarms; i++) {
+ pis[i] = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]);
+ }
+ assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ for (int i = 0; i < numAlarms; i++) {
+ mService.removeLocked(pis[i], null);
+ assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
+ }
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 8e78a56..c45122e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -52,6 +52,7 @@
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkPolicyManager;
import android.os.Build;
+import android.os.Looper;
import android.os.SystemClock;
import android.util.DataUnit;
@@ -102,6 +103,8 @@
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+
// Freeze the clocks at this moment in time
JobSchedulerService.sSystemClock =
Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
index 26b1224..caf6c9c 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
@@ -1138,7 +1138,7 @@
}
@Override
- public String getContentCaptureServicePackageName() throws RemoteException {
+ public String getSystemCaptionsServicePackageName() throws RemoteException {
return null;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index cd095a5..fa1bcac 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -35,6 +35,7 @@
import android.app.Person;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.LocusId;
import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
@@ -895,6 +896,7 @@
.setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
.setRank(123)
.setExtras(pb)
+ .setLocusId(new LocusId("1.2.3.4.5"))
.build();
sorig.setTimestamp(mInjectedCurrentTimeMillis);
@@ -906,6 +908,7 @@
.setPersons(list(makePerson("person1", "personKey1", "personUri1"),
makePerson("person2", "personKey2", "personUri2")).toArray(new Person[2]))
.setRank(456)
+ .setLocusId(new LocusId("6.7.8.9"))
.build();
sorig2.setTimestamp(mInjectedCurrentTimeMillis);
@@ -946,6 +949,7 @@
assertEquals("personUri", si.getPersons()[0].getUri());
assertEquals(0, si.getRank());
assertEquals(1, si.getExtras().getInt("k"));
+ assertEquals("1.2.3.4.5", si.getLocusId().getId());
assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE
| ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
@@ -959,6 +963,7 @@
assertEquals(1, si.getRank());
assertEquals(2, si.getPersons().length);
assertEquals("personUri2", si.getPersons()[1].getUri());
+ assertEquals("6.7.8.9", si.getLocusId().getId());
dumpUserFile(USER_10);
}
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index a2f1f01..a1a58b4 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -105,6 +105,13 @@
}
@Test
+ public void testOnUserActivity_doesntCrashIfNoAttentionService() {
+ mAttentionManagerInternal = null;
+ registerAttention();
+ // Does not crash.
+ }
+
+ @Test
public void onUserActivity_ignoresWhiteListedActivityTypes() {
for (int i = 0; i < NUM_USER_ACTIVITY_TYPES; i++) {
int result = mAttentionDetector.onUserActivity(SystemClock.uptimeMillis(), i);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 81133d1..9bd9930 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -17,12 +17,16 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager;
@@ -95,4 +99,24 @@
TRANSIT_TASK_CHANGE_WINDOWING_MODE));
}
}
+
+ @Test
+ public void testTransitWithinTask() {
+ synchronized (mWm.mGlobalLock) {
+ final AppWindowToken opening = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ opening.setFillsParent(false);
+ final AppWindowToken closing = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ closing.setFillsParent(false);
+ Task task = opening.getTask();
+ mDisplayContent.mOpeningApps.add(opening);
+ mDisplayContent.mClosingApps.add(closing);
+ assertFalse(mAppTransitionController.isTransitWithinTask(TRANSIT_ACTIVITY_OPEN, task));
+ closing.getTask().removeChild(closing);
+ task.addChild(closing, 0);
+ assertTrue(mAppTransitionController.isTransitWithinTask(TRANSIT_ACTIVITY_OPEN, task));
+ assertFalse(mAppTransitionController.isTransitWithinTask(TRANSIT_TASK_OPEN, task));
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index 1e02a12..329af95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -38,9 +38,7 @@
import static org.mockito.ArgumentMatchers.eq;
import android.app.AppOpsManager;
-import android.app.IActivityManager;
import android.app.IActivityTaskManager;
-import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
@@ -50,7 +48,6 @@
import android.util.Log;
import android.view.IWindowManager;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import com.android.server.am.AssistDataRequester;
@@ -88,12 +85,10 @@
private static final int TEST_UID = 0;
private static final String TEST_PACKAGE = "";
- private Context mContext;
private AssistDataRequester mDataRequester;
private Callbacks mCallbacks;
private Object mCallbacksLock;
private Handler mHandler;
- private IActivityManager mAm;
private IActivityTaskManager mAtm;
private IWindowManager mWm;
private AppOpsManager mAppOpsManager;
@@ -109,7 +104,6 @@
@Before
public void setUp() throws Exception {
- mAm = mock(IActivityManager.class);
mAtm = mock(IActivityTaskManager.class);
mWm = mock(IWindowManager.class);
mAppOpsManager = mock(AppOpsManager.class);
@@ -118,7 +112,7 @@
mCallbacks = new Callbacks();
mDataRequester = new AssistDataRequester(mContext, mWm, mAppOpsManager, mCallbacks,
mCallbacksLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
-
+ mDataRequester.mActivityTaskManager = mAtm;
// Gate the continuation of the assist data callbacks until we are ready within the tests
mGate = new CountDownLatch(1);
doAnswer(invocation -> {
@@ -155,7 +149,6 @@
.checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString());
}
- @FlakyTest(bugId = 124088319)
@Test
public void testRequestData() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
@@ -176,7 +169,6 @@
assertReceivedDataCount(0, 0, 0, 0);
}
- @FlakyTest(bugId = 124088319)
@Test
public void testCurrentAppDisallow_expectNullCallbacks() throws Exception {
setupMocks(!CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
@@ -187,7 +179,6 @@
assertReceivedDataCount(0, 1, 0, 1);
}
- @FlakyTest(bugId = 124088319)
@Test
public void testProcessPendingData() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
@@ -245,7 +236,6 @@
assertReceivedDataCount(0, 1, 0, 1);
}
- @FlakyTest(bugId = 124088319)
@Test
public void testDisallowAssistContextExtras_expectNullDataCallbacks() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
@@ -259,7 +249,6 @@
assertReceivedDataCount(0, 1, 0, 1);
}
- @FlakyTest(bugId = 124088319)
@Test
public void testNoFetchScreenshots_expectNoScreenshotCallbacks() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
@@ -270,7 +259,6 @@
assertReceivedDataCount(5, 5, 0, 0);
}
- @FlakyTest(bugId = 124088319)
@Test
public void testDisallowAssistScreenshot_expectNullScreenshotCallback() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 98333b2..e60e54c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -25,6 +25,8 @@
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -58,12 +60,15 @@
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.graphics.Region;
import android.metrics.LogMaker;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
+import android.util.MutableBoolean;
import android.view.DisplayCutout;
import android.view.Gravity;
+import android.view.ISystemGestureExclusionListener;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.ViewRootImpl;
@@ -683,6 +688,60 @@
}
@Test
+ public void testUpdateSystemGestureExclusion() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
+
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
+
+ win.setHasSurface(true);
+ dc.updateSystemGestureExclusion();
+
+ final MutableBoolean invoked = new MutableBoolean(false);
+ final ISystemGestureExclusionListener.Stub verifier =
+ new ISystemGestureExclusionListener.Stub() {
+ @Override
+ public void onSystemGestureExclusionChanged(int displayId, Region actual) {
+ Region expected = Region.obtain();
+ expected.set(10, 20, 30, 40);
+ assertEquals(expected, actual);
+ invoked.value = true;
+ }
+ };
+ try {
+ dc.registerSystemGestureExclusionListener(verifier);
+ } finally {
+ dc.unregisterSystemGestureExclusionListener(verifier);
+ }
+ assertTrue("SystemGestureExclusionListener was not invoked", invoked.value);
+ }
+
+ @Test
+ public void testCalculateSystemGestureExclusion() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
+
+ final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "win2");
+ win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50)));
+
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
+
+ win.setHasSurface(true);
+ win2.setHasSurface(true);
+
+ final Region expected = Region.obtain();
+ expected.set(20, 30, 40, 50);
+ assertEquals(expected, dc.calculateSystemGestureExclusion());
+ }
+
+ @Test
public void testOrientationChangeLogging() {
MetricsLogger mockLogger = mock(MetricsLogger.class);
Configuration oldConfig = new Configuration();
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 17e2e90..30dcaa0 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -53,6 +53,9 @@
* <li>Operator name, short name and numeric id
* <li>Network selection mode
* </ul>
+ *
+ * For historical reasons this class is not declared as final; however,
+ * it should be treated as though it were final.
*/
public class ServiceState implements Parcelable {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index c4c5983..2edef83 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2595,7 +2595,9 @@
* {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, it means
* it's unset and {@link SubscriptionManager#getDefaultDataSubscriptionId()}
* is used to determine which modem is preferred.
- * @param needValidation whether validation is needed before switch happens.
+ * @param needValidation whether Telephony will wait until the network is validated by
+ * connectivity service before switching data to it. More details see
+ * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED}.
* @param executor The executor of where the callback will execute.
* @param callback Callback will be triggered once it succeeds or failed.
* See {@link TelephonyManager.SetOpportunisticSubscriptionResult}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c91f16b..282b1ff 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4779,18 +4779,22 @@
* Registers a listener object to receive notification of changes
* in specified telephony states.
* <p>
- * To register a listener, pass a {@link PhoneStateListener}
- * and specify at least one telephony state of interest in
- * the events argument.
+ * To register a listener, pass a {@link PhoneStateListener} and specify at least one telephony
+ * state of interest in the events argument.
*
- * At registration, and when a specified telephony state
- * changes, the telephony manager invokes the appropriate
- * callback method on the listener object and passes the
- * current (updated) values.
+ * At registration, and when a specified telephony state changes, the telephony manager invokes
+ * the appropriate callback method on the listener object and passes the current (updated)
+ * values.
* <p>
- * To unregister a listener, pass the listener object and set the
- * events argument to
+ * To un-register a listener, pass the listener object and set the events argument to
* {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+ *
+ * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+ * applies to the given subId. Otherwise, applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
+ * pass a separate listener object to each TelephonyManager object created with
+ * {@link #createForSubscriptionId}.
+ *
* Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
* call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
* {@link SecurityException} will be thrown otherwise.
@@ -4805,17 +4809,18 @@
if (mContext == null) return;
try {
boolean notifyNow = (getITelephony() != null);
- // If the listener has not explicitly set the subId (for example, created with the
- // default constructor), replace the subId so it will listen to the account the
- // telephony manager is created with.
- if (listener.mSubId == null) {
- listener.mSubId = mSubId;
- }
-
ITelephonyRegistry registry = getTelephonyRegistry();
if (registry != null) {
- registry.listenForSubscriber(listener.mSubId, getOpPackageName(),
+ // listen to the subId the telephony manager is created with. Ignore subId in
+ // PhoneStateListener.
+ registry.listenForSubscriber(mSubId, getOpPackageName(),
listener.callback, events, notifyNow);
+ // TODO: remove this once we remove PhoneStateListener constructor with subId.
+ if (events == PhoneStateListener.LISTEN_NONE) {
+ listener.mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ } else {
+ listener.mSubId = mSubId;
+ }
} else {
Rlog.w(TAG, "telephony registry not ready.");
}
@@ -10368,7 +10373,7 @@
@IntDef(prefix = {"SET_OPPORTUNISTIC_SUB"}, value = {
SET_OPPORTUNISTIC_SUB_SUCCESS,
SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED,
- SET_OPPORTUNISTIC_SUB_INVALID_PARAMETER})
+ SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION})
public @interface SetOpportunisticSubscriptionResult {}
/**
@@ -10382,9 +10387,9 @@
public static final int SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED = 1;
/**
- * The parameter passed in is invalid.
+ * The subscription is not valid. It must be an active opportunistic subscription.
*/
- public static final int SET_OPPORTUNISTIC_SUB_INVALID_PARAMETER = 2;
+ public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
index 02eddf6..8c686f7 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -31,7 +31,7 @@
* @hide
*/
@SystemApi
-public class ImsException extends Exception {
+public final class ImsException extends Exception {
/**
* The operation has failed due to an unknown or unspecified error.
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 9d78bf4..4886a3f 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -317,7 +317,8 @@
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_PRIV_CHECK_RELAXED, 0) == 1;
ApplicationInfo callingPackageInfo = null;
try {
- callingPackageInfo = context.getPackageManager().getApplicationInfo(callingPackage, 0);
+ callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser(
+ callingPackage, 0, UserHandle.getUserId(uid));
if (callingPackageInfo.isSystemApp()) {
isPreinstalled = true;
if (callingPackageInfo.isPrivilegedApp()) {
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index b31235b..41cb74a 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -4,7 +4,7 @@
-->
<configuration description="Runs WindowManager Flicker Tests">
<option name="test-tag" value="FlickerTests" />
- <target_preparer class="com.google.android.tradefed.targetprep.GoogleDeviceSetup">
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
<!-- keeps the screen on during tests -->
<option name="screen-always-on" value="on" />
<!-- prevents the phone from restarting -->
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
index 8a925b9..ebe5418 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
@@ -63,6 +63,13 @@
}
/**
+ * Waits forever for the next rollback broadcast.
+ */
+ Intent take() throws InterruptedException {
+ return mRollbackBroadcasts.take();
+ }
+
+ /**
* Unregisters this broadcast receiver.
*/
void unregister() {
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 48bc9e8..52919df 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -27,7 +27,6 @@
import static org.junit.Assert.fail;
import android.Manifest;
-import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -46,8 +45,6 @@
import org.junit.runners.JUnit4;
import java.util.Collections;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
@@ -87,6 +84,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
Manifest.permission.MANAGE_ROLLBACKS);
// Register a broadcast receiver for notification when the
@@ -175,7 +173,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
RollbackManager rm = RollbackTestUtils.getRollbackManager();
@@ -233,7 +231,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
RollbackManager rm = RollbackTestUtils.getRollbackManager();
@@ -290,7 +288,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
RollbackManager rm = RollbackTestUtils.getRollbackManager();
@@ -343,7 +341,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
Manifest.permission.WRITE_DEVICE_CONFIG);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
@@ -403,7 +401,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
Manifest.permission.WRITE_DEVICE_CONFIG,
Manifest.permission.SET_TIME);
@@ -475,7 +473,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
RollbackManager rm = RollbackTestUtils.getRollbackManager();
RollbackTestUtils.uninstall(TEST_APP_A);
@@ -512,7 +510,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
RollbackTestUtils.uninstall(TEST_APP_A);
RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
@@ -540,7 +538,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
RollbackTestUtils.uninstall(TEST_APP_A);
RollbackTestUtils.installSplit(false,
@@ -598,7 +596,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
RollbackManager rm = RollbackTestUtils.getRollbackManager();
// Prep installation of the test apps.
@@ -693,6 +691,75 @@
}
/**
+ * Test that you cannot enable rollback for a package without the
+ * MANAGE_ROLLBACKS permission.
+ */
+ @Test
+ public void testEnableRollbackPermission() throws Exception {
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES);
+
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", /* enableRollback */ false);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackTestUtils.install("RollbackTestAppAv2.apk", /* enableRollback */ true);
+
+ // We expect v2 of the app was installed, but rollback has not
+ // been enabled.
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // TODO: See if there is a way to remove this race condition
+ // between when the app is installed and when the rollback
+ // would be made available.
+ Thread.sleep(1000);
+
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
+
+ /**
+ * Test that you cannot enable rollback for a non-module package when
+ * holding the MANAGE_ROLLBACKS permission.
+ */
+ @Test
+ public void testNonModuleEnableRollback() throws Exception {
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
+
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", /* enableRollback */ false);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackTestUtils.install("RollbackTestAppAv2.apk", /* enableRollback */ true);
+
+ // We expect v2 of the app was installed, but rollback has not
+ // been enabled because the test app is not a module.
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ // TODO: See if there is a way to remove this race condition
+ // between when the app is installed and when the rollback
+ // would be made available.
+ Thread.sleep(1000);
+
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
+
+ /**
* Test rollback of multi-package installs is implemented.
*/
@Test
@@ -701,7 +768,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
RollbackManager rm = RollbackTestUtils.getRollbackManager();
// Prep installation of the test apps.
@@ -760,7 +827,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
Manifest.permission.KILL_BACKGROUND_PROCESSES,
Manifest.permission.RESTART_PACKAGES);
RollbackManager rm = RollbackTestUtils.getRollbackManager();
@@ -790,34 +857,14 @@
rm.getAvailableRollbacks(), TEST_APP_B);
assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
- BlockingQueue<Integer> crashQueue = new SynchronousQueue<>();
+ // Register rollback committed receiver
+ RollbackBroadcastReceiver rollbackReceiver = new RollbackBroadcastReceiver();
- IntentFilter crashCountFilter = new IntentFilter();
- crashCountFilter.addAction("com.android.tests.rollback.CRASH");
- crashCountFilter.addCategory(Intent.CATEGORY_DEFAULT);
+ // Crash TEST_APP_A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
+ crashCountReceiver = RollbackTestUtils.sendCrashBroadcast(context, TEST_APP_A, 5);
- crashCountReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- try {
- // Sleep long enough for packagewatchdog to be notified of crash
- Thread.sleep(1000);
- // Kill app and close AppErrorDialog
- ActivityManager am = context.getSystemService(ActivityManager.class);
- am.killBackgroundProcesses(TEST_APP_A);
- // Allow another package launch
- crashQueue.put(intent.getIntExtra("count", 0));
- } catch (InterruptedException e) {
- fail("Failed to communicate with test app");
- }
- }
- };
- context.registerReceiver(crashCountReceiver, crashCountFilter);
-
- // Start apps PackageWatchdog#TRIGGER_FAILURE_COUNT times so TEST_APP_A crashes
- do {
- RollbackTestUtils.launchPackage(TEST_APP_A);
- } while(crashQueue.take() < 5);
+ // Verify we received a broadcast for the rollback.
+ rollbackReceiver.take();
// TEST_APP_A is automatically rolled back by the RollbackPackageHealthObserver
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index 9aed074..81629aa 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
+import android.app.ActivityManager;
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -47,6 +48,7 @@
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SynchronousQueue;
/**
* Utilities to facilitate testing rollbacks.
@@ -187,7 +189,7 @@
}
/** Launches {@code packageName} with {@link Intent#ACTION_MAIN}. */
- static void launchPackage(String packageName)
+ private static void launchPackage(String packageName)
throws InterruptedException, IOException {
Context context = InstrumentationRegistry.getContext();
Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -488,4 +490,39 @@
}
return null;
}
+
+ /**
+ * Send broadcast to crash {@code packageName} {@code count} times. If {@code count} is at least
+ * {@link PackageWatchdog#TRIGGER_FAILURE_COUNT}, watchdog crash detection will be triggered.
+ */
+ static BroadcastReceiver sendCrashBroadcast(Context context, String packageName, int count)
+ throws InterruptedException, IOException {
+ BlockingQueue<Integer> crashQueue = new SynchronousQueue<>();
+ IntentFilter crashCountFilter = new IntentFilter();
+ crashCountFilter.addAction("com.android.tests.rollback.CRASH");
+ crashCountFilter.addCategory(Intent.CATEGORY_DEFAULT);
+
+ BroadcastReceiver crashCountReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ // Sleep long enough for packagewatchdog to be notified of crash
+ Thread.sleep(1000);
+ // Kill app and close AppErrorDialog
+ ActivityManager am = context.getSystemService(ActivityManager.class);
+ am.killBackgroundProcesses(packageName);
+ // Allow another package launch
+ crashQueue.put(intent.getIntExtra("count", 0));
+ } catch (InterruptedException e) {
+ fail("Failed to communicate with test app");
+ }
+ }
+ };
+ context.registerReceiver(crashCountReceiver, crashCountFilter);
+
+ do {
+ launchPackage(packageName);
+ } while(crashQueue.take() < count);
+ return crashCountReceiver;
+ }
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 047451b..7e711c2 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -63,7 +63,7 @@
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
}
/**
diff --git a/tests/net/java/android/net/apf/ApfCapabilitiesTest.java b/tests/net/java/android/net/apf/ApfCapabilitiesTest.java
new file mode 100644
index 0000000..75752c3
--- /dev/null
+++ b/tests/net/java/android/net/apf/ApfCapabilitiesTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.apf;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.net.shared.ParcelableTestUtil;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.TestUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ApfCapabilitiesTest {
+ @Test
+ public void testParcelUnparcel() {
+ final ApfCapabilities caps = new ApfCapabilities(123, 456, 789);
+ ParcelableTestUtil.assertFieldCountEquals(3, ApfCapabilities.class);
+
+ TestUtils.assertParcelingIsLossless(caps, ApfCapabilities.CREATOR);
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(new ApfCapabilities(1, 2, 3), new ApfCapabilities(1, 2, 3));
+ assertNotEquals(new ApfCapabilities(2, 2, 3), new ApfCapabilities(1, 2, 3));
+ assertNotEquals(new ApfCapabilities(1, 3, 3), new ApfCapabilities(1, 2, 3));
+ assertNotEquals(new ApfCapabilities(1, 2, 4), new ApfCapabilities(1, 2, 3));
+ }
+}
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index e937517..a24e0d2f 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -58,7 +58,7 @@
// valid. This is because un-mangled references are mangled, then looked up at resolution
// time. Also, when linking, we convert references with no package name to use the compilation
// package name.
- error |= !DoMerge(src, table, package.get(), false /*mangle*/, overlay, allow_new);
+ error |= !DoMerge(src, package.get(), false /*mangle*/, overlay, allow_new);
}
}
return !error;
@@ -78,7 +78,7 @@
bool mangle = package_name != context_->GetCompilationPackage();
merged_packages_.insert(package->name);
- error |= !DoMerge(src, table, package.get(), mangle, false /*overlay*/, true /*allow_new*/);
+ error |= !DoMerge(src, package.get(), mangle, false /*overlay*/, true /*allow_new*/);
}
return !error;
}
@@ -213,9 +213,8 @@
return collision_result;
}
-bool TableMerger::DoMerge(const Source& src, ResourceTable* src_table,
- ResourceTablePackage* src_package, bool mangle_package, bool overlay,
- bool allow_new_resources) {
+bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package, bool mangle_package,
+ bool overlay, bool allow_new_resources) {
bool error = false;
for (auto& src_type : src_package->types) {
@@ -337,8 +336,7 @@
->FindOrCreateValue(file_desc.config, {})
->value = std::move(file_ref);
- return DoMerge(file->GetSource(), &table, pkg, false /*mangle*/, overlay /*overlay*/,
- true /*allow_new*/);
+ return DoMerge(file->GetSource(), pkg, false /*mangle*/, overlay /*overlay*/, true /*allow_new*/);
}
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index 24c5e13..51305cf 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -85,8 +85,8 @@
bool MergeImpl(const Source& src, ResourceTable* src_table, bool overlay, bool allow_new);
- bool DoMerge(const Source& src, ResourceTable* src_table, ResourceTablePackage* src_package,
- bool mangle_package, bool overlay, bool allow_new_resources);
+ bool DoMerge(const Source& src, ResourceTablePackage* src_package, bool mangle_package,
+ bool overlay, bool allow_new_resources);
std::unique_ptr<FileReference> CloneAndMangleFile(const std::string& package,
const FileReference& value);