Merge "Add helper methods for View attribute debugging"
diff --git a/Android.mk b/Android.mk
index 65d4d24..9c65948 100644
--- a/Android.mk
+++ b/Android.mk
@@ -79,34 +79,6 @@
# ==== hiddenapi lists =======================================
ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
-.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
-$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
- PRIVATE_FLAGS_INPUTS := $(PRIVATE_FLAGS_INPUTS) $(SOONG_HIDDENAPI_FLAGS)
-$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
- frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
- frameworks/base/config/hiddenapi-greylist.txt \
- frameworks/base/config/hiddenapi-greylist-max-p.txt \
- frameworks/base/config/hiddenapi-greylist-max-o.txt \
- frameworks/base/config/hiddenapi-force-blacklist.txt \
- $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) \
- $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
- $(SOONG_HIDDENAPI_FLAGS)
- frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
- --csv $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) $(PRIVATE_FLAGS_INPUTS) \
- --greylist frameworks/base/config/hiddenapi-greylist.txt \
- --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
- --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \
- --greylist-max-o-ignore-conflicts \
- frameworks/base/config/hiddenapi-greylist-max-o.txt \
- --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \
- --output $@.tmp
- $(call commit-change-for-toc,$@)
-
-$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \
- frameworks/base/tools/hiddenapi/merge_csv.py \
- $(PRIVATE_METADATA_INPUTS)
- frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@
-
$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
endif # UNSAFE_DISABLE_HIDDENAPI_FLAGS
diff --git a/api/current.txt b/api/current.txt
index f6f68e1..98d30a3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -224,6 +224,11 @@
public static final class R.attr {
ctor public R.attr();
+ field public static final int __removed1 = 16844185; // 0x1010599
+ field public static final int __removed2 = 16844186; // 0x101059a
+ field public static final int __removed3 = 16844187; // 0x101059b
+ field public static final int __removed4 = 16844188; // 0x101059c
+ field public static final int __removed5 = 16844189; // 0x101059d
field public static final int absListViewStyle = 16842858; // 0x101006a
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -484,10 +489,6 @@
field public static final int dashGap = 16843175; // 0x10101a7
field public static final int dashWidth = 16843174; // 0x10101a6
field public static final int data = 16842798; // 0x101002e
- field public static final int dataRetentionTime = 16844189; // 0x101059d
- field public static final int dataSentOffDevice = 16844186; // 0x101059a
- field public static final int dataSharedWithThirdParty = 16844187; // 0x101059b
- field public static final int dataUsedForMonetization = 16844188; // 0x101059c
field public static final int datePickerDialogTheme = 16843948; // 0x10104ac
field public static final int datePickerMode = 16843955; // 0x10104b3
field public static final int datePickerStyle = 16843612; // 0x101035c
@@ -1508,7 +1509,6 @@
field @Deprecated public static final int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
- field public static final int usageInfoRequired = 16844185; // 0x1010599
field public static final int use32bitAbi = 16844053; // 0x1010515
field public static final int useAppZygote = 16844184; // 0x1010598
field public static final int useDefaultMargins = 16843641; // 0x1010379
@@ -6738,6 +6738,7 @@
method public void setCrossProfileCalendarPackages(@NonNull android.content.ComponentName, @Nullable java.util.Set<java.lang.String>);
method public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean);
method public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean);
+ method public void setDefaultSmsApplication(@NonNull android.content.ComponentName, @NonNull String);
method public void setDelegatedScopes(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.lang.String>);
method public void setDeviceOwnerLockScreenInfo(@NonNull android.content.ComponentName, CharSequence);
method public void setEndUserSessionMessage(@NonNull android.content.ComponentName, @Nullable CharSequence);
@@ -10264,7 +10265,6 @@
field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
field public static final String ACTION_PASTE = "android.intent.action.PASTE";
- field public static final String ACTION_PERMISSION_USAGE_DETAILS = "android.intent.action.PERMISSION_USAGE_DETAILS";
field public static final String ACTION_PICK = "android.intent.action.PICK";
field public static final String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY";
field public static final String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
@@ -10390,7 +10390,6 @@
field public static final String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
field public static final String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
field public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
- field public static final String EXTRA_PERMISSION_USAGE_PERMISSIONS = "android.intent.extra.PERMISSION_USAGE_PERMISSIONS";
field public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
field public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
field public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -11355,8 +11354,8 @@
field public android.content.pm.ProviderInfo[] providers;
field public android.content.pm.ActivityInfo[] receivers;
field public android.content.pm.FeatureInfo[] reqFeatures;
- field @Deprecated public String[] requestedPermissions;
- field @Deprecated public int[] requestedPermissionsFlags;
+ field public String[] requestedPermissions;
+ field public int[] requestedPermissionsFlags;
field public android.content.pm.ServiceInfo[] services;
field public String sharedUserId;
field public int sharedUserLabel;
@@ -11364,7 +11363,6 @@
field public android.content.pm.SigningInfo signingInfo;
field public String[] splitNames;
field public int[] splitRevisionCodes;
- field public android.content.pm.UsesPermissionInfo[] usesPermissions;
field @Deprecated public int versionCode;
field public String versionName;
}
@@ -11864,7 +11862,6 @@
field public String group;
field public CharSequence nonLocalizedDescription;
field @Deprecated public int protectionLevel;
- field public boolean usageInfoRequired;
}
public final class ProviderInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable {
@@ -12056,28 +12053,6 @@
field public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
}
- public final class UsesPermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
- method public int describeContents();
- method public int getDataRetention();
- method public int getDataRetentionWeeks();
- method public int getDataSentOffDevice();
- method public int getDataSharedWithThirdParty();
- method public int getDataUsedForMonetization();
- method public int getFlags();
- method public String getPermission();
- field public static final android.os.Parcelable.Creator<android.content.pm.UsesPermissionInfo> CREATOR;
- field public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 2; // 0x2
- field public static final int RETENTION_NOT_RETAINED = 1; // 0x1
- field public static final int RETENTION_SPECIFIED = 4; // 0x4
- field public static final int RETENTION_UNDEFINED = 0; // 0x0
- field public static final int RETENTION_UNLIMITED = 3; // 0x3
- field public static final int RETENTION_USER_SELECTED = 2; // 0x2
- field public static final int USAGE_NO = 3; // 0x3
- field public static final int USAGE_UNDEFINED = 0; // 0x0
- field public static final int USAGE_USER_TRIGGERED = 2; // 0x2
- field public static final int USAGE_YES = 1; // 0x1
- }
-
public final class VersionedPackage implements android.os.Parcelable {
ctor public VersionedPackage(@NonNull String, int);
ctor public VersionedPackage(@NonNull String, long);
@@ -19447,9 +19422,9 @@
method public static android.icu.text.BreakIterator getSentenceInstance(java.util.Locale);
method public static android.icu.text.BreakIterator getSentenceInstance(android.icu.util.ULocale);
method public abstract java.text.CharacterIterator getText();
- method public static android.icu.text.BreakIterator getTitleInstance();
- method public static android.icu.text.BreakIterator getTitleInstance(java.util.Locale);
- method public static android.icu.text.BreakIterator getTitleInstance(android.icu.util.ULocale);
+ method @Deprecated public static android.icu.text.BreakIterator getTitleInstance();
+ method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(java.util.Locale);
+ method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(android.icu.util.ULocale);
method public static android.icu.text.BreakIterator getWordInstance();
method public static android.icu.text.BreakIterator getWordInstance(java.util.Locale);
method public static android.icu.text.BreakIterator getWordInstance(android.icu.util.ULocale);
@@ -19466,7 +19441,7 @@
field public static final int KIND_CHARACTER = 0; // 0x0
field public static final int KIND_LINE = 2; // 0x2
field public static final int KIND_SENTENCE = 3; // 0x3
- field public static final int KIND_TITLE = 4; // 0x4
+ field @Deprecated public static final int KIND_TITLE = 4; // 0x4
field public static final int KIND_WORD = 1; // 0x1
field public static final int WORD_IDEO = 400; // 0x190
field public static final int WORD_IDEO_LIMIT = 500; // 0x1f4
@@ -56674,6 +56649,7 @@
method public boolean isFillViewport();
method public boolean isSmoothScrollingEnabled();
method public boolean pageScroll(int);
+ method public void scrollToDescendant(android.view.View);
method public void setFillViewport(boolean);
method public void setSmoothScrollingEnabled(boolean);
method public final void smoothScrollBy(int, int);
diff --git a/api/removed.txt b/api/removed.txt
index 9f4b041..262ffec 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -344,7 +344,7 @@
public final class PowerManager {
method public void goToSleep(long);
method @Deprecated public void userActivity(long, boolean);
- method public void wakeUp(long);
+ method @Deprecated public void wakeUp(long);
}
public class RecoverySystem {
diff --git a/api/system-current.txt b/api/system-current.txt
index 8d7ec26..b7fc339 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1072,16 +1072,17 @@
public final class RoleManager {
method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
+ field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
}
@@ -3919,6 +3920,7 @@
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
+ method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
@@ -5001,10 +5003,11 @@
method public boolean getEnableAdjustBrightness();
method public boolean getEnableDataSaver();
method public boolean getEnableFirewall();
+ method public boolean getEnableNightMode();
method public boolean getEnableQuickDoze();
method public boolean getForceAllAppsStandby();
method public boolean getForceBackgroundCheck();
- method public int getGpsMode();
+ method public int getLocationMode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.os.BatterySaverPolicyConfig> CREATOR;
}
@@ -5026,10 +5029,11 @@
method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableAdjustBrightness(boolean);
method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableDataSaver(boolean);
method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableFirewall(boolean);
+ method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableNightMode(boolean);
method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableQuickDoze(boolean);
method @NonNull public android.os.BatterySaverPolicyConfig.Builder setForceAllAppsStandby(boolean);
method @NonNull public android.os.BatterySaverPolicyConfig.Builder setForceBackgroundCheck(boolean);
- method @NonNull public android.os.BatterySaverPolicyConfig.Builder setGpsMode(int);
+ method @NonNull public android.os.BatterySaverPolicyConfig.Builder setLocationMode(int);
}
public class Binder implements android.os.IBinder {
@@ -6037,11 +6041,11 @@
public abstract class RoleControllerService extends android.app.Service {
ctor public RoleControllerService();
- method public abstract void onAddRoleHolder(@NonNull String, @NonNull String, @NonNull android.app.role.RoleManagerCallback);
+ method public abstract void onAddRoleHolder(@NonNull String, @NonNull String, int, @NonNull android.app.role.RoleManagerCallback);
method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
- method public abstract void onClearRoleHolders(@NonNull String, @NonNull android.app.role.RoleManagerCallback);
+ method public abstract void onClearRoleHolders(@NonNull String, int, @NonNull android.app.role.RoleManagerCallback);
method public abstract void onGrantDefaultRoles(@NonNull android.app.role.RoleManagerCallback);
- method public abstract void onRemoveRoleHolder(@NonNull String, @NonNull String, @NonNull android.app.role.RoleManagerCallback);
+ method public abstract void onRemoveRoleHolder(@NonNull String, @NonNull String, int, @NonNull android.app.role.RoleManagerCallback);
method public abstract void onSmsKillSwitchToggled(boolean);
field public static final String SERVICE_INTERFACE = "android.rolecontrollerservice.RoleControllerService";
}
diff --git a/api/test-current.txt b/api/test-current.txt
index f71789d..94e83ea 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -429,11 +429,11 @@
package android.app.role {
public final class RoleManager {
- method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
- method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void clearRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
+ method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
+ method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
- method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
+ method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
}
@@ -934,6 +934,7 @@
}
public class ConnectivityManager {
+ method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 5dcb392b..46917e4 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -252,10 +252,12 @@
status_t BootAnimation::readyToRun() {
mAssets.addDefaultAssets();
- sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
+ mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
+ if (mDisplayToken == nullptr)
+ return -1;
+
DisplayInfo dinfo;
- status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
+ status_t status = SurfaceComposerClient::getDisplayInfo(mDisplayToken, &dinfo);
if (status)
return -1;
@@ -1014,16 +1016,13 @@
// At the end of the animation, we switch to the viewport that DisplayManager will apply
// later. This changes the coordinate system, and means we must move the surface up by
// the inset amount.
- sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
-
Rect layerStackRect(0, 0, mWidth, mHeight - mTargetInset);
Rect displayRect(0, mTargetInset, mWidth, mHeight);
SurfaceComposerClient::Transaction t;
t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset)
.setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight));
- t.setDisplayProjection(dtoken, 0 /* orientation */, layerStackRect, displayRect);
+ t.setDisplayProjection(mDisplayToken, 0 /* orientation */, layerStackRect, displayRect);
t.apply();
mTargetInset = mCurrentInset = 0;
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 04d4f9a..19616cb 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -171,6 +171,7 @@
EGLDisplay mDisplay;
EGLDisplay mContext;
EGLDisplay mSurface;
+ sp<IBinder> mDisplayToken;
sp<SurfaceControl> mFlingerSurfaceControl;
sp<Surface> mFlingerSurface;
bool mClockEnabled;
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 056add5..2fef407 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -43,6 +43,7 @@
"libidmap2/PrettyPrintVisitor.cpp",
"libidmap2/RawPrintVisitor.cpp",
"libidmap2/ResourceUtils.cpp",
+ "libidmap2/Result.cpp",
"libidmap2/Xml.cpp",
"libidmap2/ZipFile.cpp",
],
@@ -93,6 +94,7 @@
"tests/PrettyPrintVisitorTests.cpp",
"tests/RawPrintVisitorTests.cpp",
"tests/ResourceUtilsTests.cpp",
+ "tests/ResultTests.cpp",
"tests/XmlTests.cpp",
"tests/ZipFileTests.cpp",
],
diff --git a/cmds/idmap2/include/idmap2/Result.h b/cmds/idmap2/include/idmap2/Result.h
index 6189ea3..d88dd51 100644
--- a/cmds/idmap2/include/idmap2/Result.h
+++ b/cmds/idmap2/include/idmap2/Result.h
@@ -18,6 +18,11 @@
#define IDMAP2_INCLUDE_IDMAP2_RESULT_H_
#include <optional>
+#include <string>
+#include <utility>
+#include <variant>
+
+#include "android-base/logging.h" // CHECK
namespace android::idmap2 {
@@ -26,6 +31,125 @@
static constexpr std::nullopt_t kResultError = std::nullopt;
+namespace v2 {
+
+using Unit = std::monostate;
+
+class Error {
+ public:
+ explicit Error(const Error& parent) = default;
+
+ // NOLINTNEXTLINE(cert-dcl50-cpp)
+ explicit Error(const char* fmt, ...) __attribute__((__format__(printf, 2, 3)));
+
+ // NOLINTNEXTLINE(cert-dcl50-cpp)
+ explicit Error(const Error& parent, const char* fmt, ...)
+ __attribute__((__format__(printf, 3, 4)));
+
+ inline std::string GetMessage() const {
+ return msg_;
+ }
+
+ private:
+ std::string msg_;
+};
+
+template <typename T>
+class Result {
+ public:
+ Result(const T& value); // NOLINT(runtime/explicit)
+ Result(T&& value) noexcept; // NOLINT(runtime/explicit)
+
+ Result(const Error& error); // NOLINT(runtime/explicit)
+ Result(Error&& error) noexcept; // NOLINT(runtime/explicit)
+
+ Result(const Result& error) = default;
+
+ Result& operator=(const Result& rhs) = default;
+ Result& operator=(Result&& rhs) noexcept = default;
+
+ explicit operator bool() const;
+
+ constexpr const T& operator*() const&;
+ T& operator*() &;
+
+ constexpr const T* operator->() const&;
+ T* operator->() &;
+
+ std::string GetErrorMessage() const;
+ Error GetError() const;
+
+ private:
+ bool is_ok() const;
+
+ std::variant<T, Error> data_;
+};
+
+template <typename T>
+Result<T>::Result(const T& value) : data_(std::in_place_type<T>, value) {
+}
+
+template <typename T>
+Result<T>::Result(T&& value) noexcept : data_(std::in_place_type<T>, std::forward<T>(value)) {
+}
+
+template <typename T>
+Result<T>::Result(const Error& error) : data_(std::in_place_type<Error>, error) {
+}
+
+template <typename T>
+Result<T>::Result(Error&& error) noexcept
+ : data_(std::in_place_type<Error>, std::forward<Error>(error)) {
+}
+
+template <typename T>
+Result<T>::operator bool() const {
+ return is_ok();
+}
+
+template <typename T>
+constexpr const T& Result<T>::operator*() const& {
+ CHECK(is_ok()) << "Result<T>::operator* called in ERROR state";
+ return std::get<T>(data_);
+}
+
+template <typename T>
+T& Result<T>::operator*() & {
+ CHECK(is_ok()) << "Result<T>::operator* called in ERROR state";
+ return std::get<T>(data_);
+}
+
+template <typename T>
+constexpr const T* Result<T>::operator->() const& {
+ CHECK(is_ok()) << "Result<T>::operator-> called in ERROR state";
+ return &std::get<T>(data_);
+}
+
+template <typename T>
+T* Result<T>::operator->() & {
+ CHECK(is_ok()) << "Result<T>::operator-> called in ERROR state";
+ return &std::get<T>(data_);
+}
+
+template <typename T>
+inline std::string Result<T>::GetErrorMessage() const {
+ CHECK(!is_ok()) << "Result<T>::GetErrorMessage called in OK state";
+ return std::get<Error>(data_).GetMessage();
+}
+
+template <typename T>
+inline Error Result<T>::GetError() const {
+ CHECK(!is_ok()) << "Result<T>::GetError called in OK state";
+ return Error(std::get<Error>(data_));
+}
+
+template <typename T>
+inline bool Result<T>::is_ok() const {
+ return std::holds_alternative<T>(data_);
+}
+
+} // namespace v2
+
} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_RESULT_H_
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index fa5ac8e..b19d7a9 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -117,6 +117,12 @@
return loaded_arsc.GetPackageById(id);
}
+Result<uint32_t> GetCrc(const ZipFile& zip) {
+ const Result<uint32_t> a = zip.Crc("resources.arsc");
+ const Result<uint32_t> b = zip.Crc("AndroidManifest.xml");
+ return a && b ? Result<uint32_t>(*a ^ *b) : kResultError;
+}
+
} // namespace
std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
@@ -153,7 +159,7 @@
return false;
}
- Result<uint32_t> target_crc = target_zip->Crc("resources.arsc");
+ Result<uint32_t> target_crc = GetCrc(*target_zip);
if (!target_crc) {
out_error << "error: failed to get target crc" << std::endl;
return false;
@@ -173,7 +179,7 @@
return false;
}
- Result<uint32_t> overlay_crc = overlay_zip->Crc("resources.arsc");
+ Result<uint32_t> overlay_crc = GetCrc(*overlay_zip);
if (!overlay_crc) {
out_error << "error: failed to get overlay crc" << std::endl;
return false;
@@ -356,14 +362,14 @@
header->magic_ = kIdmapMagic;
header->version_ = kIdmapCurrentVersion;
- Result<uint32_t> crc = target_zip->Crc("resources.arsc");
+ Result<uint32_t> crc = GetCrc(*target_zip);
if (!crc) {
out_error << "error: failed to get zip crc for target" << std::endl;
return nullptr;
}
header->target_crc_ = *crc;
- crc = overlay_zip->Crc("resources.arsc");
+ crc = GetCrc(*overlay_zip);
if (!crc) {
out_error << "error: failed to get zip crc for overlay" << std::endl;
return nullptr;
diff --git a/cmds/idmap2/libidmap2/Result.cpp b/cmds/idmap2/libidmap2/Result.cpp
new file mode 100644
index 0000000..a5c9999
--- /dev/null
+++ b/cmds/idmap2/libidmap2/Result.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdarg>
+
+#include "android-base/stringprintf.h"
+
+#include "idmap2/Result.h"
+
+namespace android::idmap2 {
+
+v2::Error::Error(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ base::StringAppendV(&msg_, fmt, ap);
+ va_end(ap);
+}
+
+v2::Error::Error(const Error& parent, const char* fmt, ...) : msg_(parent.msg_) {
+ msg_.append(" -> ");
+
+ va_list ap;
+ va_start(ap, fmt);
+ base::StringAppendV(&msg_, fmt, ap);
+ va_end(ap);
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/static-checks.sh b/cmds/idmap2/static-checks.sh
index ad9830b..5740710 100755
--- a/cmds/idmap2/static-checks.sh
+++ b/cmds/idmap2/static-checks.sh
@@ -73,6 +73,7 @@
local output="$($cpplint --quiet $cpp_files 2>&1 >/dev/null | grep -v \
-e 'Found C system header after C++ system header.' \
-e 'Unknown NOLINT error category: misc-non-private-member-variables-in-classes' \
+ -e 'Unknown NOLINT error category: performance-unnecessary-copy-initialization' \
)"
if [[ "$output" ]]; then
echo "$output"
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 9e27ccd..b40521f 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -191,8 +191,8 @@
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
- ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xab7cf70d);
- ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xdd53ca29);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xa71ccd77);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index b1ca125..a5588c3 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -52,8 +52,8 @@
ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000008: ab7cf70d target crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000000c: d470336b overlay crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000008: dd53ca29 target crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000000c: a71ccd77 overlay crc\n"), std::string::npos);
ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"),
std::string::npos);
}
diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp
new file mode 100644
index 0000000..d82f0c4
--- /dev/null
+++ b/cmds/idmap2/tests/ResultTests.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "idmap2/Result.h"
+
+namespace android::idmap2 {
+
+struct Container {
+ uint32_t value; // NOLINT(misc-non-private-member-variables-in-classes)
+};
+
+// Tests: Error
+
+TEST(ResultTests, ErrorTraits) {
+ ASSERT_TRUE(std::is_move_constructible<v2::Error>::value);
+ ASSERT_TRUE(std::is_move_assignable<v2::Error>::value);
+ ASSERT_TRUE(std::is_copy_constructible<v2::Error>::value);
+ ASSERT_TRUE(std::is_copy_assignable<v2::Error>::value);
+}
+
+TEST(ResultTests, ErrorCtorFormat) {
+ v2::Error e("%s=0x%08x", "resid", 0x7f010002);
+ ASSERT_EQ(e.GetMessage(), "resid=0x7f010002");
+}
+
+TEST(ResultTests, ErrorPropagateParent) {
+ v2::Error e1("foo");
+ ASSERT_EQ(e1.GetMessage(), "foo");
+
+ v2::Error e2(e1, "bar");
+ ASSERT_EQ(e2.GetMessage(), "foo -> bar");
+
+ v2::Error e3(e2); // NOLINT(performance-unnecessary-copy-initialization)
+ ASSERT_EQ(e3.GetMessage(), "foo -> bar");
+
+ v2::Error e4(e3, "%02d", 1);
+ ASSERT_EQ(e4.GetMessage(), "foo -> bar -> 01");
+}
+
+// Tests: Result<T> member functions
+
+// Result(const Result&)
+TEST(ResultTests, CopyConstructor) {
+ v2::Result<uint32_t> r1(42U);
+
+ v2::Result<uint32_t> r2(r1);
+ ASSERT_TRUE(r2);
+ ASSERT_EQ(*r2, 42U);
+
+ v2::Result<uint32_t> r3 = r2;
+ ASSERT_TRUE(r3);
+ ASSERT_EQ(*r3, 42U);
+}
+
+// Result(const T&)
+TEST(ResultTests, Constructor) {
+ uint32_t v = 42U;
+ v2::Result<uint32_t> r1(v);
+ ASSERT_TRUE(r1);
+ ASSERT_EQ(*r1, 42U);
+
+ v2::Error e("foo");
+ v2::Result<uint32_t> r2(e);
+ ASSERT_FALSE(r2);
+ ASSERT_EQ(r2.GetErrorMessage(), "foo");
+}
+
+// Result(const T&&)
+TEST(ResultTests, MoveConstructor) {
+ v2::Result<uint32_t> r1(42U);
+ ASSERT_TRUE(r1);
+ ASSERT_EQ(*r1, 42U);
+
+ v2::Result<uint32_t> r2(v2::Error("foo"));
+ ASSERT_FALSE(r2);
+ ASSERT_EQ(r2.GetErrorMessage(), "foo");
+}
+
+// operator=
+TEST(ResultTests, CopyAssignmentOperator) {
+ // note: 'Result<...> r2 = r1;' calls the copy ctor
+ v2::Result<uint32_t> r1(42U);
+ v2::Result<uint32_t> r2(0U);
+ r2 = r1;
+ ASSERT_TRUE(r2);
+ ASSERT_EQ(*r2, 42U);
+
+ v2::Result<uint32_t> r3(v2::Error("foo"));
+ r2 = r3;
+ ASSERT_FALSE(r2);
+ ASSERT_EQ(r2.GetErrorMessage(), "foo");
+}
+
+TEST(ResultTests, MoveAssignmentOperator) {
+ v2::Result<uint32_t> r(0U);
+ r = v2::Result<uint32_t>(42U);
+ ASSERT_TRUE(r);
+ ASSERT_EQ(*r, 42U);
+
+ r = v2::Result<uint32_t>(v2::Error("foo"));
+ ASSERT_FALSE(r);
+ ASSERT_EQ(r.GetErrorMessage(), "foo");
+}
+
+// operator bool()
+TEST(ResultTests, BoolOperator) {
+ v2::Result<uint32_t> r1(42U);
+ ASSERT_TRUE(r1);
+ ASSERT_EQ(*r1, 42U);
+
+ v2::Result<uint32_t> r2(v2::Error("foo"));
+ ASSERT_FALSE(r2);
+ ASSERT_EQ(r2.GetErrorMessage(), "foo");
+}
+
+// operator*
+TEST(ResultTests, IndirectionOperator) {
+ const v2::Result<uint32_t> r1(42U);
+ ASSERT_TRUE(r1);
+ ASSERT_EQ(*r1, 42U);
+
+ const v2::Result<Container> r2(Container{42U});
+ ASSERT_TRUE(r2);
+ const Container& c = *r2;
+ ASSERT_EQ(c.value, 42U);
+
+ v2::Result<Container> r3(Container{42U});
+ ASSERT_TRUE(r3);
+ ASSERT_EQ((*r3).value, 42U);
+ (*r3).value = 0U;
+ ASSERT_EQ((*r3).value, 0U);
+}
+
+// operator->
+TEST(ResultTests, DereferenceOperator) {
+ const v2::Result<Container> r1(Container{42U});
+ ASSERT_TRUE(r1);
+ ASSERT_EQ(r1->value, 42U);
+
+ v2::Result<Container> r2(Container{42U});
+ ASSERT_TRUE(r2);
+ ASSERT_EQ(r2->value, 42U);
+ r2->value = 0U;
+ ASSERT_EQ(r2->value, 0U);
+}
+
+// Tests: intended use of Result<T>
+
+TEST(ResultTests, ResultTraits) {
+ ASSERT_TRUE(std::is_move_constructible<v2::Result<uint32_t>>::value);
+ ASSERT_TRUE(std::is_move_assignable<v2::Result<uint32_t>>::value);
+ ASSERT_TRUE(std::is_copy_constructible<v2::Result<uint32_t>>::value);
+ ASSERT_TRUE(std::is_copy_assignable<v2::Result<uint32_t>>::value);
+}
+
+TEST(ResultTests, UnitTypeResult) {
+ v2::Result<v2::Unit> r(v2::Unit{});
+ ASSERT_TRUE(r);
+}
+
+struct RefCountData {
+ int ctor; // NOLINT(misc-non-private-member-variables-in-classes)
+ int copy_ctor; // NOLINT(misc-non-private-member-variables-in-classes)
+ int dtor; // NOLINT(misc-non-private-member-variables-in-classes)
+ int move; // NOLINT(misc-non-private-member-variables-in-classes)
+};
+
+class RefCountContainer {
+ public:
+ explicit RefCountContainer(RefCountData& data) : data_(data) {
+ ++data_.ctor;
+ }
+
+ RefCountContainer(RefCountContainer const&) = delete;
+
+ RefCountContainer(RefCountContainer&& rhs) noexcept : data_(rhs.data_) {
+ ++data_.copy_ctor;
+ }
+
+ RefCountContainer& operator=(RefCountContainer const&) = delete;
+
+ RefCountContainer& operator=(RefCountContainer&& rhs) noexcept {
+ data_ = rhs.data_;
+ ++data_.move;
+ return *this;
+ }
+
+ ~RefCountContainer() {
+ ++data_.dtor;
+ }
+
+ private:
+ RefCountData& data_;
+};
+
+TEST(ResultTests, ReferenceCount) {
+ ASSERT_TRUE(std::is_move_constructible<RefCountContainer>::value);
+ ASSERT_TRUE(std::is_move_assignable<RefCountContainer>::value);
+ ASSERT_FALSE(std::is_copy_constructible<RefCountContainer>::value);
+ ASSERT_FALSE(std::is_copy_assignable<RefCountContainer>::value);
+
+ RefCountData rc{0, 0, 0, 0};
+ { v2::Result<RefCountContainer> r(RefCountContainer{rc}); }
+ ASSERT_EQ(rc.ctor, 1);
+ ASSERT_EQ(rc.copy_ctor, 1);
+ ASSERT_EQ(rc.move, 0);
+ ASSERT_EQ(rc.dtor, 2);
+}
+
+v2::Result<Container> CreateContainer(bool succeed) {
+ if (!succeed) {
+ return v2::Error("foo");
+ }
+ return Container{42U};
+}
+
+TEST(ResultTests, FunctionReturn) {
+ auto r1 = CreateContainer(true);
+ ASSERT_TRUE(r1);
+ ASSERT_EQ(r1->value, 42U);
+
+ auto r2 = CreateContainer(false);
+ ASSERT_FALSE(r2);
+ ASSERT_EQ(r2.GetErrorMessage(), "foo");
+ ASSERT_EQ(r2.GetError().GetMessage(), "foo");
+}
+
+v2::Result<Container> FailToCreateContainer() {
+ auto container = CreateContainer(false);
+ if (!container) {
+ return v2::Error(container.GetError(), "bar");
+ }
+ return container;
+}
+
+TEST(ResultTests, CascadeError) {
+ auto container = FailToCreateContainer();
+ ASSERT_FALSE(container);
+ ASSERT_EQ(container.GetErrorMessage(), "foo -> bar");
+}
+
+struct NoCopyContainer {
+ uint32_t value; // NOLINT(misc-non-private-member-variables-in-classes)
+ DISALLOW_COPY_AND_ASSIGN(NoCopyContainer);
+};
+
+v2::Result<std::unique_ptr<NoCopyContainer>> CreateNoCopyContainer(bool succeed) {
+ if (!succeed) {
+ return v2::Error("foo");
+ }
+ std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{0U});
+ p->value = 42U;
+ return std::move(p);
+}
+
+TEST(ResultTests, UniquePtr) {
+ auto r1 = CreateNoCopyContainer(true);
+ ASSERT_TRUE(r1);
+ ASSERT_EQ((*r1)->value, 42U);
+ (*r1)->value = 0U;
+ ASSERT_EQ((*r1)->value, 0U);
+
+ auto r2 = CreateNoCopyContainer(false);
+ ASSERT_FALSE(r2);
+ ASSERT_EQ(r2.GetErrorMessage(), "foo");
+}
+
+} // namespace android::idmap2
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 3d74f8b..c497667 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -46,23 +46,22 @@
using namespace android;
-static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain;
-
#define COLORSPACE_UNKNOWN 0
#define COLORSPACE_SRGB 1
#define COLORSPACE_DISPLAY_P3 2
-static void usage(const char* pname)
+static void usage(const char* pname, PhysicalDisplayId displayId)
{
fprintf(stderr,
"usage: %s [-hp] [-d display-id] [FILENAME]\n"
" -h: this message\n"
" -p: save the file as a png.\n"
- " -d: specify the display id to capture, default %d.\n"
+ " -d: specify the physical display ID to capture (default: %"
+ ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ")\n"
+ " see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
"If FILENAME ends with .png it will be saved as a png.\n"
"If FILENAME is not given, the results will be printed to stdout.\n",
- pname, DEFAULT_DISPLAY_ID
- );
+ pname, displayId);
}
static SkColorType flinger2skia(PixelFormat f)
@@ -127,9 +126,14 @@
int main(int argc, char** argv)
{
+ std::optional<PhysicalDisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
+ if (!displayId) {
+ fprintf(stderr, "Failed to get token for internal display\n");
+ return 1;
+ }
+
const char* pname = argv[0];
bool png = false;
- int32_t displayId = DEFAULT_DISPLAY_ID;
int c;
while ((c = getopt(argc, argv, "phd:")) != -1) {
switch (c) {
@@ -137,11 +141,11 @@
png = true;
break;
case 'd':
- displayId = atoi(optarg);
+ displayId = atoll(optarg);
break;
case '?':
case 'h':
- usage(pname);
+ usage(pname, *displayId);
return 1;
}
}
@@ -166,7 +170,7 @@
}
if (fd == -1) {
- usage(pname);
+ usage(pname, *displayId);
return 1;
}
@@ -192,9 +196,10 @@
ProcessState::self()->setThreadPoolMaxThreadCount(0);
ProcessState::self()->startThreadPool();
- sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
- if (display == NULL) {
- fprintf(stderr, "Unable to get handle for display %d\n", displayId);
+ const sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+ if (display == nullptr) {
+ fprintf(stderr, "Failed to get token for invalid display %"
+ ANDROID_PHYSICAL_DISPLAY_ID_FORMAT "\n", *displayId);
return 1;
}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index ca10482..d6f045e 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -50,6 +50,7 @@
srcs: [
":statsd_aidl",
+ "src/active_config_list.proto",
"src/statsd_config.proto",
"src/FieldValue.cpp",
"src/hash.cpp",
@@ -214,7 +215,7 @@
"tests/anomaly/AnomalyTracker_test.cpp",
"tests/ConfigManager_test.cpp",
"tests/external/puller_util_test.cpp",
- "tests/external/StatsPuller_test.cpp",
+ "tests/external/StatsPuller_test.cpp",
"tests/indexed_priority_queue_test.cpp",
"tests/LogEntryMatcher_test.cpp",
"tests/LogEvent_test.cpp",
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 69cb264..250f5bf 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-#define DEBUG false // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "statslog.h"
#include <android-base/file.h>
#include <dirent.h>
+#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
#include "StatsLogProcessor.h"
-#include "stats_log_util.h"
#include "android-base/stringprintf.h"
+#include "external/StatsPullerManager.h"
#include "guardrail/StatsdStats.h"
#include "metrics/CountMetricProducer.h"
-#include "external/StatsPullerManager.h"
+#include "stats_log_util.h"
#include "stats_util.h"
#include "storage/StorageManager.h"
@@ -67,9 +68,17 @@
const int FIELD_ID_DUMP_REPORT_REASON = 8;
const int FIELD_ID_STRINGS = 9;
+const int FIELD_ID_ACTIVE_CONFIG_LIST = 1;
+const int FIELD_ID_CONFIG_ID = 1;
+const int FIELD_ID_CONFIG_UID = 2;
+const int FIELD_ID_ACTIVE_METRIC = 3;
+const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_TIME_TO_LIVE_NANOS = 2;
+
#define NS_PER_HOUR 3600 * NS_PER_SEC
#define STATS_DATA_DIR "/data/misc/stats-data"
+#define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric"
// Cool down period for writing data to disk to avoid overwriting files.
#define WRITE_DATA_COOL_DOWN_SEC 5
@@ -507,6 +516,70 @@
mOnDiskDataConfigs.insert(key);
}
+void StatsLogProcessor::WriteMetricsActivationToDisk(int64_t currentTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ ProtoOutputStream proto;
+
+ for (const auto& pair : mMetricsManagers) {
+ uint64_t activeConfigListToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_ACTIVE_CONFIG_LIST);
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_ID, (long long)pair.first.GetId());
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_UID, pair.first.GetUid());
+
+ vector<const MetricProducer*> acrtiveMetrics;
+ pair.second->getActiveMetrics(acrtiveMetrics);
+ for (const MetricProducer* metric : acrtiveMetrics) {
+ if (metric->isActive()) {
+ uint64_t metricToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_ACTIVE_METRIC);
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID,
+ (long long)metric->getMetricId());
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_TIME_TO_LIVE_NANOS,
+ (long long)metric->getRemainingTtlNs(currentTimeNs));
+ proto.end(metricToken);
+ }
+ }
+ proto.end(activeConfigListToken);
+ }
+
+ string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
+ StorageManager::deleteFile(file_name.c_str());
+ android::base::unique_fd fd(
+ open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR));
+ if (fd == -1) {
+ ALOGE("Attempt to write %s but failed", file_name.c_str());
+ return;
+ }
+ proto.flush(fd.get());
+}
+
+void StatsLogProcessor::LoadMetricsActivationFromDisk() {
+ string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
+ int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd != -1) {
+ string content;
+ if (android::base::ReadFdToString(fd, &content)) {
+ ActiveConfigList activeConfigList;
+ if (activeConfigList.ParseFromString(content)) {
+ for (int i = 0; i < activeConfigList.active_config_size(); i++) {
+ const auto& config = activeConfigList.active_config(i);
+ ConfigKey key(config.uid(), config.config_id());
+ auto it = mMetricsManagers.find(key);
+ if (it == mMetricsManagers.end()) {
+ ALOGE("No config found for config %s", key.ToString().c_str());
+ continue;
+ }
+ VLOG("Setting active config %s", key.ToString().c_str());
+ it->second->setActiveMetrics(config, mTimeBaseNs);
+ }
+ }
+ VLOG("Successfully loaded %d active configs.", activeConfigList.active_config_size());
+ }
+ close(fd);
+ }
+ StorageManager::deleteFile(file_name.c_str());
+}
+
void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason) {
const int64_t timeNs = getElapsedRealtimeNs();
// Do not write to disk if we already have in the last few seconds.
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index a5ce9b6..77d9a2f 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -80,6 +80,12 @@
/* Flushes data to disk. Data on memory will be gone after written to disk. */
void WriteDataToDisk(const DumpReportReason dumpReportReason);
+ /* Persist metric activation status onto disk. */
+ void WriteMetricsActivationToDisk(int64_t currentTimeNs);
+
+ /* Load metric activation status from disk. */
+ void LoadMetricsActivationFromDisk();
+
// Reset all configs.
void resetConfigs();
@@ -188,6 +194,8 @@
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
+ FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
+
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index b26c713..86bf3ec 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -161,7 +161,8 @@
mConfigManager = new ConfigManager();
mProcessor = new StatsLogProcessor(
mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
- getElapsedRealtimeNs(), [this](const ConfigKey& key) {
+ getElapsedRealtimeNs(),
+ [this](const ConfigKey& key) {
sp<IStatsCompanionService> sc = getStatsCompanionService();
auto receiver = mConfigManager->GetConfigReceiver(key);
if (sc == nullptr) {
@@ -867,6 +868,7 @@
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informDeviceShutdown");
mProcessor->WriteDataToDisk(DEVICE_SHUTDOWN);
+ mProcessor->WriteMetricsActivationToDisk(getElapsedRealtimeNs());
return Status::ok();
}
@@ -901,6 +903,7 @@
void StatsService::Startup() {
mConfigManager->Startup();
+ mProcessor->LoadMetricsActivationFromDisk();
}
void StatsService::Terminate() {
diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto
new file mode 100644
index 0000000..0e9ee03
--- /dev/null
+++ b/cmds/statsd/src/active_config_list.proto
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd;
+option java_package = "com.android.os";
+option java_multiple_files = true;
+option java_outer_classname = "ActiveConfigProto";
+
+message ActiveMetric {
+ // metric id
+ optional int64 metric_id = 1;
+ // Remaining time to live in nano seconds. -1 for infinity.
+ optional int64 time_to_live_nanos = 2;
+}
+
+message ActiveConfig {
+ // config id
+ optional int64 config_id = 1;
+ // config uid
+ optional int32 uid = 2;
+ // metrics
+ repeated ActiveMetric active_metric = 3;
+}
+
+// all configs and their metrics on device.
+message ActiveConfigList {
+ repeated ActiveConfig active_config = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index e33bd8c..2a3eee2 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -82,4 +82,6 @@
optional bool is_uid = 50001 [default = false];
optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC];
+
+ optional bool allow_from_any_uid = 50003 [default = false];
}
\ No newline at end of file
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index fc3aa91..4f17d69 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -110,7 +110,7 @@
PacketWakeupOccurred packet_wakeup_occurred = 44;
WallClockTimeShifted wall_clock_time_shifted = 45;
AnomalyDetected anomaly_detected = 46;
- AppBreadcrumbReported app_breadcrumb_reported = 47;
+ AppBreadcrumbReported app_breadcrumb_reported = 47 [(allow_from_any_uid) = true];
AppStartOccurred app_start_occurred = 48;
AppStartCanceled app_start_canceled = 49;
AppStartFullyDrawn app_start_fully_drawn = 50;
@@ -121,7 +121,7 @@
AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
ShutdownSequenceReported shutdown_sequence_reported = 56;
BootSequenceReported boot_sequence_reported = 57;
- DaveyOccurred davey_occurred = 58;
+ DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true];
OverlayStateChanged overlay_state_changed = 59;
ForegroundServiceStateChanged foreground_service_state_changed = 60;
CallStateChanged call_state_changed = 61;
@@ -239,6 +239,7 @@
PermissionGrantRequestResultReported permission_grant_request_result_reported = 170;
BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171;
DeviceIdentifierAccessDenied device_identifier_access_denied = 172;
+ BubbleDeveloperErrorReported bubble_developer_error_reported = 173;
}
// Pulled events will start at field 10000.
@@ -5344,6 +5345,27 @@
}
/**
+ * Logs System UI bubbles developer errors.
+ *
+ * Logged from:
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+ */
+message BubbleDeveloperErrorReported {
+
+ // The app package that is posting the bubble.
+ optional string package_name = 1;
+
+ // Bubble developer error type enums.
+ enum Error {
+ UNKNOWN = 0;
+ ACTIVITY_INFO_MISSING = 1;
+ ACTIVITY_INFO_NOT_RESIZABLE = 2;
+ DOCUMENT_LAUNCH_NOT_ALWAYS = 3;
+ }
+ optional Error error = 2 [default = UNKNOWN];
+}
+
+/**
* Logs that a constraint for a scheduled job has changed.
*
* Logged from:
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index a5bd5c6..37d5ba0 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -443,6 +443,11 @@
getAtomMetricStats(metricId).badValueType++;
}
+void StatsdStats::noteBucketDropped(int metricId) {
+ lock_guard<std::mutex> lock(mLock);
+ getAtomMetricStats(metricId).bucketDropped++;
+}
+
void StatsdStats::noteConditionChangeInNextBucket(int metricId) {
lock_guard<std::mutex> lock(mLock);
getAtomMetricStats(metricId).conditionChangeInNextBucket++;
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index cb17061..20ea7e5 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -360,6 +360,11 @@
void noteBadValueType(int atomId);
/**
+ * Buckets were dropped due to reclaim memory.
+ */
+ void noteBucketDropped(int metricId);
+
+ /**
* A condition change was too late, arrived in the wrong bucket and was skipped
*/
void noteConditionChangeInNextBucket(int atomId);
@@ -414,6 +419,7 @@
long badValueType = 0;
long conditionChangeInNextBucket = 0;
long invalidatedBucket = 0;
+ long bucketDropped = 0;
} AtomMetricStats;
private:
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 7cc57c1..350745b 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -243,6 +243,7 @@
void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
flushIfNeededLocked(dropTimeNs);
+ StatsdStats::getInstance().noteBucketDropped(mMetricId);
mPastBuckets.clear();
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 69bafc3..6c1c47b 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -444,6 +444,7 @@
void DurationMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
flushIfNeededLocked(dropTimeNs);
+ StatsdStats::getInstance().noteBucketDropped(mMetricId);
mPastBuckets.clear();
}
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index c53c4ce..7e695a6 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -77,6 +77,7 @@
void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
mProto->clear();
+ StatsdStats::getInstance().noteBucketDropped(mMetricId);
}
void EventMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index c9b7165..2609937 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -510,6 +510,7 @@
void GaugeMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
flushIfNeededLocked(dropTimeNs);
+ StatsdStats::getInstance().noteBucketDropped(mMetricId);
mPastBuckets.clear();
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index f87849e..b362e37 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -112,6 +112,28 @@
mIsActive = true;
}
+void MetricProducer::setActiveLocked(int64_t currentTimeNs, int64_t remainingTtlNs) {
+ if (mEventActivationMap.size() == 0) {
+ return;
+ }
+ auto& activation = mEventActivationMap.begin()->second;
+ activation.activation_ns = currentTimeNs + remainingTtlNs - activation.ttl_ns;
+ activation.state = kActive;
+ mIsActive = true;
+ VLOG("setting new activation time to %lld, %lld, %lld", (long long)activation.activation_ns,
+ (long long)currentTimeNs, (long long)remainingTtlNs);
+}
+
+int64_t MetricProducer::getRemainingTtlNsLocked(int64_t currentTimeNs) const {
+ int64_t maxTtl = 0;
+ for (const auto& activation : mEventActivationMap) {
+ if (activation.second.state == kActive) {
+ maxTtl = std::max(maxTtl, activation.second.ttl_ns + activation.second.activation_ns -
+ currentTimeNs);
+ }
+ }
+ return maxTtl;
+}
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 09e2409..ca37bbb 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -179,10 +179,21 @@
mBucketSizeNs = bucketSize;
}
- inline const int64_t& getMetricId() {
+ inline const int64_t& getMetricId() const {
return mMetricId;
}
+ int64_t getRemainingTtlNs(int64_t currentTimeNs) const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return getRemainingTtlNsLocked(currentTimeNs);
+ }
+
+ // Set metric to active for ttlNs.
+ void setActive(int64_t currentTimeNs, int64_t remainingTtlNs) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ setActiveLocked(currentTimeNs, remainingTtlNs);
+ }
+
// Let MetricProducer drop in-memory data to save memory.
// We still need to keep future data valid and anomaly tracking work, which means we will
// have to flush old data, informing anomaly trackers then safely drop old data.
@@ -202,6 +213,11 @@
activateLocked(activationTrackerIndex, elapsedTimestampNs);
}
+ bool isActive() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return isActiveLocked();
+ }
+
void addActivation(int activationTrackerIndex, int64_t ttl_seconds);
void flushIfExpire(int64_t elapsedTimestampNs);
@@ -227,6 +243,10 @@
return mIsActive;
}
+ int64_t getRemainingTtlNsLocked(int64_t currentTimeNs) const;
+
+ void setActiveLocked(int64_t currentTimeNs, int64_t remainingTtlNs);
+
/**
* Flushes the current bucket if the eventTime is after the current bucket's end time. This will
also flush the current partial bucket in memory.
@@ -348,6 +368,8 @@
bool mIsActive;
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
+
+ FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index dd969c0..6ed6ab50 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -122,6 +122,13 @@
StatsdStats::getInstance().noteConfigReceived(
key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(),
mAllAnomalyTrackers.size(), mAnnotations, mConfigValid);
+ // Check active
+ for (const auto& metric : mAllMetricProducers) {
+ if (metric->isActive()) {
+ mIsActive = true;
+ break;
+ }
+ }
}
MetricsManager::~MetricsManager() {
@@ -234,12 +241,22 @@
VLOG("=========================Metric Reports End==========================");
}
-// Consume the stats log if it's interesting to this metric.
-void MetricsManager::onLogEvent(const LogEvent& event) {
- if (!mConfigValid) {
- return;
- }
+bool MetricsManager::checkLogCredentials(const LogEvent& event) {
+ if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) !=
+ android::util::AtomsInfo::kWhitelistedAtoms.end())
+ {
+ return true;
+ }
+ std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
+ if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
+ VLOG("log source %d not on the whitelist", event.GetUid());
+ return false;
+ }
+ return true;
+}
+
+bool MetricsManager::eventSanityCheck(const LogEvent& event) {
if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) {
// Check that app breadcrumb reported fields are valid.
status_t err = NO_ERROR;
@@ -249,23 +266,23 @@
long appHookUid = event.GetLong(event.size()-2, &err);
if (err != NO_ERROR ) {
VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid");
- return;
+ return false;
}
int32_t loggerUid = event.GetUid();
if (loggerUid != appHookUid && loggerUid != AID_STATSD) {
VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d",
appHookUid, loggerUid);
- return;
+ return false;
}
// The state must be from 0,3. This part of code must be manually updated.
long appHookState = event.GetLong(event.size(), &err);
if (err != NO_ERROR ) {
VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field");
- return;
+ return false;
} else if (appHookState < 0 || appHookState > 3) {
VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
- return;
+ return false;
}
} else if (event.GetTagId() == android::util::DAVEY_OCCURRED) {
// Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
@@ -276,36 +293,49 @@
long jankUid = event.GetLong(1, &err);
if (err != NO_ERROR ) {
VLOG("Davey occurred had error when parsing the uid");
- return;
+ return false;
}
int32_t loggerUid = event.GetUid();
if (loggerUid != jankUid && loggerUid != AID_STATSD) {
VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid,
loggerUid);
- return;
+ return false;
}
long duration = event.GetLong(event.size(), &err);
if (err != NO_ERROR ) {
VLOG("Davey occurred had error when parsing the duration");
- return;
+ return false;
} else if (duration > 100000) {
VLOG("Davey duration is unreasonably long: %ld", duration);
- return;
+ return false;
}
- } else {
- std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
- if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
- VLOG("log source %d not on the whitelist", event.GetUid());
- return;
- }
+ }
+
+ return true;
+}
+
+// Consume the stats log if it's interesting to this metric.
+void MetricsManager::onLogEvent(const LogEvent& event) {
+ if (!mConfigValid) {
+ return;
+ }
+
+ if (!checkLogCredentials(event)) {
+ return;
+ }
+
+ if (!eventSanityCheck(event)) {
+ return;
}
int tagId = event.GetTagId();
int64_t eventTimeNs = event.GetElapsedTimestampNs();
+ bool isActive = false;
for (int metric : mMetricIndexesWithActivation) {
mAllMetricProducers[metric]->flushIfExpire(eventTimeNs);
+ isActive |= mAllMetricProducers[metric]->isActive();
}
if (mTagIds.find(tagId) == mTagIds.end()) {
@@ -323,10 +353,13 @@
if (matcherCache[it.first] == MatchingState::kMatched) {
for (int metricIndex : it.second) {
mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs);
+ isActive |= mAllMetricProducers[metricIndex]->isActive();
}
}
}
+ mIsActive = isActive;
+
// A bitmap to see which ConditionTracker needs to be re-evaluated.
vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
@@ -418,6 +451,25 @@
return totalSize;
}
+void MetricsManager::setActiveMetrics(ActiveConfig config, int64_t currentTimeNs) {
+ if (config.active_metric_size() == 0) {
+ ALOGW("No active metric for config %s", mConfigKey.ToString().c_str());
+ return;
+ }
+
+ for (int i = 0; i < config.active_metric_size(); i++) {
+ for (int metric : mMetricIndexesWithActivation) {
+ if (mAllMetricProducers[metric]->getMetricId() == config.active_metric(i).metric_id()) {
+ VLOG("Setting active metric: %lld",
+ (long long)mAllMetricProducers[metric]->getMetricId());
+ mAllMetricProducers[metric]->setActive(
+ currentTimeNs, config.active_metric(i).time_to_live_nanos());
+ mIsActive = true;
+ }
+ }
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index a31efbd..fdc28ea 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -16,12 +16,13 @@
#pragma once
-#include "external/StatsPullerManager.h"
+#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
#include "anomaly/AlarmMonitor.h"
#include "anomaly/AlarmTracker.h"
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionTracker.h"
#include "config/ConfigKey.h"
+#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "logd/LogEvent.h"
#include "matchers/LogMatchingTracker.h"
@@ -48,6 +49,10 @@
// Return whether the configuration is valid.
bool isConfigValid() const;
+ bool checkLogCredentials(const LogEvent& event);
+
+ bool eventSanityCheck(const LogEvent& event);
+
void onLogEvent(const LogEvent& event);
void onAnomalyAlarmFired(
@@ -123,6 +128,20 @@
// Does not change the state.
virtual size_t byteSize();
+ inline bool isActive() const {
+ return mIsActive;
+ }
+
+ inline void getActiveMetrics(std::vector<const MetricProducer*>& metrics) const {
+ for (const auto& metric : mAllMetricProducers) {
+ if (metric->isActive()) {
+ metrics.push_back(metric.get());
+ }
+ }
+ }
+
+ void setActiveMetrics(ActiveConfig config, int64_t currentTimeNs);
+
private:
// For test only.
inline int64_t getTtlEndNs() const { return mTtlEndNs; }
@@ -216,6 +235,9 @@
// The metrics that don't need to be uploaded or even reported.
std::set<int64_t> mNoReportMetricIds;
+ // Any metric active means the config is active.
+ bool mIsActive;
+
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
@@ -247,6 +269,8 @@
FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
+
+ FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 9fb78e7..1bd3ef2 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -175,6 +175,7 @@
void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
flushIfNeededLocked(dropTimeNs);
+ StatsdStats::getInstance().noteBucketDropped(mMetricId);
mPastBuckets.clear();
}
@@ -434,6 +435,10 @@
}
}
mHasGlobalBase = true;
+
+ // We can probably flush the bucket. Since we used bucketEndTime when calling
+ // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed.
+ flushIfNeededLocked(realEventTime);
} else {
VLOG("No need to commit data on condition false.");
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index d9bec5d..0cfefa9 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -61,6 +61,7 @@
if (!mSplitBucketForAppUpgrade) {
return;
}
+ flushIfNeededLocked(eventTimeNs - 1);
if (mIsPulled && mCondition) {
pullAndMatchEventsLocked(eventTimeNs - 1);
}
@@ -212,6 +213,7 @@
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
+ FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated);
FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
@@ -238,6 +240,8 @@
FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed);
FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed);
FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
+ FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
};
} // namespace statsd
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 5b91482..73aab48 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -418,6 +418,7 @@
optional int64 bad_value_type = 5;
optional int64 condition_change_in_next_bucket = 6;
optional int64 invalidated_bucket = 7;
+ optional int64 bucket_dropped = 8;
}
repeated AtomMetricStats atom_metric_stats = 17;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 3cb7563..f76a9ad 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -79,6 +79,7 @@
const int FIELD_ID_BAD_VALUE_TYPE = 5;
const int FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET = 6;
const int FIELD_ID_INVALIDATED_BUCKET = 7;
+const int FIELD_ID_BUCKET_DROPPED = 8;
namespace {
@@ -497,6 +498,8 @@
(long long)pair.second.conditionChangeInNextBucket);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_INVALIDATED_BUCKET,
(long long)pair.second.invalidatedBucket);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_DROPPED,
+ (long long)pair.second.bucketDropped);
protoOutput->end(token);
}
diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc
index cbf2a8d..e0cbd5d 100644
--- a/cmds/statsd/statsd.rc
+++ b/cmds/statsd/statsd.rc
@@ -26,3 +26,4 @@
# Create directory for statsd
mkdir /data/misc/stats-data/ 0770 statsd system
mkdir /data/misc/stats-service/ 0770 statsd system
+ mkdir /data/misc/stats-active-metric/ 0770 statsd system
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index d52be44..64008b5 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -286,6 +286,294 @@
EXPECT_TRUE(noData);
}
+TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) {
+ int uid = 1111;
+
+ // Setup a simple config, no activation
+ StatsdConfig config1;
+ config1.set_id(12341);
+ config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+ *config1.add_atom_matcher() = wakelockAcquireMatcher;
+
+ long metricId1 = 1234561;
+ long metricId2 = 1234562;
+ auto countMetric1 = config1.add_count_metric();
+ countMetric1->set_id(metricId1);
+ countMetric1->set_what(wakelockAcquireMatcher.id());
+ countMetric1->set_bucket(FIVE_MINUTES);
+
+ auto countMetric2 = config1.add_count_metric();
+ countMetric2->set_id(metricId2);
+ countMetric2->set_what(wakelockAcquireMatcher.id());
+ countMetric2->set_bucket(FIVE_MINUTES);
+
+ ConfigKey cfgKey1(uid, 12341);
+ long timeBase1 = 1;
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+
+ // Add another config, with two metrics, one with activation
+ StatsdConfig config2;
+ config2.set_id(12342);
+ config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ *config2.add_atom_matcher() = wakelockAcquireMatcher;
+
+ long metricId3 = 1234561;
+ long metricId4 = 1234562;
+
+ auto countMetric3 = config2.add_count_metric();
+ countMetric3->set_id(metricId3);
+ countMetric3->set_what(wakelockAcquireMatcher.id());
+ countMetric3->set_bucket(FIVE_MINUTES);
+
+ auto countMetric4 = config2.add_count_metric();
+ countMetric4->set_id(metricId4);
+ countMetric4->set_what(wakelockAcquireMatcher.id());
+ countMetric4->set_bucket(FIVE_MINUTES);
+
+ auto metric3Activation = config2.add_metric_activation();
+ metric3Activation->set_metric_id(metricId3);
+ auto metric3ActivationTrigger = metric3Activation->add_event_activation();
+ metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+ metric3ActivationTrigger->set_ttl_seconds(100);
+
+ ConfigKey cfgKey2(uid, 12342);
+
+ // Add another config, with two metrics, both with activations
+ StatsdConfig config3;
+ config3.set_id(12342);
+ config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ *config3.add_atom_matcher() = wakelockAcquireMatcher;
+
+ long metricId5 = 1234565;
+ long metricId6 = 1234566;
+ auto countMetric5 = config3.add_count_metric();
+ countMetric5->set_id(metricId5);
+ countMetric5->set_what(wakelockAcquireMatcher.id());
+ countMetric5->set_bucket(FIVE_MINUTES);
+
+ auto countMetric6 = config3.add_count_metric();
+ countMetric6->set_id(metricId6);
+ countMetric6->set_what(wakelockAcquireMatcher.id());
+ countMetric6->set_bucket(FIVE_MINUTES);
+
+ auto metric5Activation = config3.add_metric_activation();
+ metric5Activation->set_metric_id(metricId5);
+ auto metric5ActivationTrigger = metric5Activation->add_event_activation();
+ metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+ metric5ActivationTrigger->set_ttl_seconds(100);
+
+ auto metric6Activation = config3.add_metric_activation();
+ metric6Activation->set_metric_id(metricId6);
+ auto metric6ActivationTrigger = metric6Activation->add_event_activation();
+ metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+ metric6ActivationTrigger->set_ttl_seconds(200);
+
+ ConfigKey cfgKey3(uid, 12343);
+
+ processor->OnConfigUpdated(2, cfgKey2, config2);
+ processor->OnConfigUpdated(3, cfgKey3, config3);
+
+ EXPECT_EQ(3, processor->mMetricsManagers.size());
+ auto it = processor->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ auto& metricsManager1 = it->second;
+ EXPECT_TRUE(metricsManager1->isActive());
+
+ auto metricIt = metricsManager1->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId1) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+ auto& metricProducer1 = *metricIt;
+ EXPECT_TRUE(metricProducer1->isActive());
+
+ metricIt = metricsManager1->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId2) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+ auto& metricProducer2 = *metricIt;
+ EXPECT_TRUE(metricProducer2->isActive());
+
+ it = processor->mMetricsManagers.find(cfgKey2);
+ EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ auto& metricsManager2 = it->second;
+ EXPECT_TRUE(metricsManager2->isActive());
+
+ metricIt = metricsManager2->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId3) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
+ auto& metricProducer3 = *metricIt;
+ EXPECT_FALSE(metricProducer3->isActive());
+
+ metricIt = metricsManager2->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId4) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
+ auto& metricProducer4 = *metricIt;
+ EXPECT_TRUE(metricProducer4->isActive());
+
+ it = processor->mMetricsManagers.find(cfgKey3);
+ EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ auto& metricsManager3 = it->second;
+ EXPECT_FALSE(metricsManager3->isActive());
+
+ metricIt = metricsManager3->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId5) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
+ auto& metricProducer5 = *metricIt;
+ EXPECT_FALSE(metricProducer5->isActive());
+
+ metricIt = metricsManager3->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId6) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
+ auto& metricProducer6 = *metricIt;
+ EXPECT_FALSE(metricProducer6->isActive());
+
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+ auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
+ processor->OnLogEvent(event.get());
+
+ int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+ EXPECT_TRUE(metricProducer3->isActive());
+ int64_t ttl3 = metricProducer3->getRemainingTtlNs(shutDownTime);
+ EXPECT_EQ(100, ttl3);
+ EXPECT_TRUE(metricProducer5->isActive());
+ int64_t ttl5 = metricProducer5->getRemainingTtlNs(shutDownTime);
+ EXPECT_EQ(100, ttl5);
+ EXPECT_TRUE(metricProducer6->isActive());
+ int64_t ttl6 = metricProducer6->getRemainingTtlNs(shutDownTime);
+ EXPECT_EQ(100 + 100 * NS_PER_SEC, ttl6);
+
+ processor->WriteMetricsActivationToDisk(timeBase1 + 100 * NS_PER_SEC);
+
+ long timeBase2 = 1000;
+ sp<StatsLogProcessor> processor2 =
+ CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+ processor2->OnConfigUpdated(timeBase2, cfgKey2, config2);
+ processor2->OnConfigUpdated(timeBase2, cfgKey3, config3);
+
+ EXPECT_EQ(3, processor2->mMetricsManagers.size());
+ it = processor2->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+ auto& metricsManager1001 = it->second;
+ EXPECT_TRUE(metricsManager1001->isActive());
+
+ metricIt = metricsManager1001->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId1) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+ auto& metricProducer1001 = *metricIt;
+ EXPECT_TRUE(metricProducer1001->isActive());
+
+ metricIt = metricsManager1001->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId2) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+ auto& metricProducer1002 = *metricIt;
+ EXPECT_TRUE(metricProducer1002->isActive());
+
+ it = processor2->mMetricsManagers.find(cfgKey2);
+ EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+ auto& metricsManager1002 = it->second;
+ EXPECT_TRUE(metricsManager1002->isActive());
+
+ metricIt = metricsManager1002->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId3) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
+ auto& metricProducer1003 = *metricIt;
+ EXPECT_FALSE(metricProducer1003->isActive());
+
+ metricIt = metricsManager1002->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId4) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
+ auto& metricProducer1004 = *metricIt;
+ EXPECT_TRUE(metricProducer1004->isActive());
+
+ it = processor2->mMetricsManagers.find(cfgKey3);
+ EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+ auto& metricsManager1003 = it->second;
+ EXPECT_FALSE(metricsManager1003->isActive());
+ EXPECT_EQ(2, metricsManager1003->mAllMetricProducers.size());
+
+ metricIt = metricsManager1003->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId5) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
+ auto& metricProducer1005 = *metricIt;
+ EXPECT_FALSE(metricProducer1005->isActive());
+
+ metricIt = metricsManager1003->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId6) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
+ auto& metricProducer1006 = *metricIt;
+ EXPECT_FALSE(metricProducer1006->isActive());
+
+ EXPECT_FALSE(metricProducer1003->isActive());
+ const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second;
+ EXPECT_EQ(100 * NS_PER_SEC, activation1003.ttl_ns);
+ EXPECT_EQ(0, activation1003.activation_ns);
+ EXPECT_FALSE(metricProducer1005->isActive());
+ const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second;
+ EXPECT_EQ(100 * NS_PER_SEC, activation1005.ttl_ns);
+ EXPECT_EQ(0, activation1005.activation_ns);
+ EXPECT_FALSE(metricProducer1006->isActive());
+ const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second;
+ EXPECT_EQ(200 * NS_PER_SEC, activation1006.ttl_ns);
+ EXPECT_EQ(0, activation1006.activation_ns);
+
+ processor2->LoadMetricsActivationFromDisk();
+
+ EXPECT_TRUE(metricProducer1003->isActive());
+ EXPECT_EQ(timeBase2 + ttl3 - activation1003.ttl_ns, activation1003.activation_ns);
+ EXPECT_TRUE(metricProducer1005->isActive());
+ EXPECT_EQ(timeBase2 + ttl5 - activation1005.ttl_ns, activation1005.activation_ns);
+ EXPECT_TRUE(metricProducer1006->isActive());
+ EXPECT_EQ(timeBase2 + ttl6 - activation1006.ttl_ns, activation1003.activation_ns);
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 6404552..91b98ec 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -167,9 +167,10 @@
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(11, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(8, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -184,11 +185,12 @@
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(23, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(12, curInterval.value.long_value);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -202,11 +204,80 @@
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(36, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(13, curInterval.value.long_value);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+ EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value);
+ EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second[2].values[0].long_value);
+}
+
+TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_max_pull_delay_sec(INT_MAX);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initialize bucket.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+ event->write(tagId);
+ event->write(1);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ // Partial bucket.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
+ event->write(tagId);
+ event->write(5);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+ // First bucket ends.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
+ event->write(tagId);
+ event->write(2);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData, /** success */ true);
+
+ // Partial buckets created in 2nd bucket.
+ valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
+
+ // One full bucket and one partial bucket.
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ vector<ValueBucket> buckets = valueProducer.mPastBuckets.begin()->second;
+ EXPECT_EQ(2UL, buckets.size());
+ // Full bucket (2 - 1)
+ EXPECT_EQ(1, buckets[0].values[0].long_value);
+ // Full bucket (5 - 3)
+ EXPECT_EQ(3, buckets[1].values[0].long_value);
}
/*
@@ -264,9 +335,10 @@
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(11, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(8, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -275,16 +347,15 @@
event->init();
allData.push_back(event);
valueProducer.onDataPulled(allData, /** succeed */ true);
- // has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ // No new data seen, so data has been cleared.
+ EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(11, curInterval.base.long_value);
- // no events caused flush of bucket
- EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(8, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -363,9 +434,10 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(10, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(10, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -378,11 +450,12 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(36, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(26, curInterval.value.long_value);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value);
}
/*
@@ -456,9 +529,10 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(36, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(26, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
}
/*
@@ -532,9 +606,10 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(110, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(10, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
@@ -651,8 +726,8 @@
event->init();
allData.push_back(event);
valueProducer.onDataPulled(allData, /** succeed */ true);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(20L,
valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
}
@@ -694,7 +769,7 @@
valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
EXPECT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucketStartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
}
TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
@@ -1018,9 +1093,9 @@
// tartUpdated:false sum:12
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(23, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(12, curInterval.value.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
// pull 3 come late.
// The previous bucket gets closed with error. (Has start value 23, no ending)
@@ -1681,7 +1756,7 @@
EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval1.hasBase);
EXPECT_EQ(11, interval1.base.long_value);
- EXPECT_EQ(true, interval1.hasValue);
+ EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(8, interval1.value.long_value);
auto it = valueProducer.mCurrentSlicedBucket.begin();
@@ -1695,9 +1770,14 @@
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, interval2.hasBase);
EXPECT_EQ(4, interval2.base.long_value);
- EXPECT_EQ(true, interval2.hasValue);
+ EXPECT_EQ(false, interval2.hasValue);
EXPECT_EQ(4, interval2.value.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
+ auto iterator = valueProducer.mPastBuckets.begin();
+ EXPECT_EQ(8, iterator->second[0].values[0].long_value);
+ iterator++;
+ EXPECT_EQ(4, iterator->second[0].values[0].long_value);
}
/*
@@ -1766,7 +1846,7 @@
EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval1.hasBase);
EXPECT_EQ(11, interval1.base.long_value);
- EXPECT_EQ(true, interval1.hasValue);
+ EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(8, interval1.value.long_value);
auto it = valueProducer.mCurrentSlicedBucket.begin();
@@ -1780,9 +1860,9 @@
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, interval2.hasBase);
EXPECT_EQ(4, interval2.base.long_value);
- EXPECT_EQ(true, interval2.hasValue);
+ EXPECT_EQ(false, interval2.hasValue);
EXPECT_EQ(4, interval2.value.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
// next pull somehow did not happen, skip to end of bucket 3
allData.clear();
@@ -1793,11 +1873,11 @@
allData.push_back(event1);
valueProducer.onDataPulled(allData, /** succeed */ true);
- EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(5, interval2.base.long_value);
+ EXPECT_EQ(4, interval2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
- EXPECT_EQ(false, interval1.hasBase);
+ EXPECT_EQ(true, interval1.hasBase);
EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(true, valueProducer.mHasGlobalBase);
EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
@@ -1817,13 +1897,13 @@
EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(13, interval2.base.long_value);
- EXPECT_EQ(true, interval2.hasValue);
- EXPECT_EQ(8, interval2.value.long_value);
+ EXPECT_EQ(5, interval2.base.long_value);
+ EXPECT_EQ(false, interval2.hasValue);
+ EXPECT_EQ(5, interval2.value.long_value);
EXPECT_EQ(true, interval1.hasBase);
- EXPECT_EQ(5, interval1.base.long_value);
- EXPECT_EQ(true, interval1.hasValue);
- EXPECT_EQ(5, interval1.value.long_value);
+ EXPECT_EQ(13, interval1.base.long_value);
+ EXPECT_EQ(false, interval1.hasValue);
+ EXPECT_EQ(8, interval1.value.long_value);
EXPECT_EQ(true, valueProducer.mHasGlobalBase);
EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
}
@@ -1892,9 +1972,11 @@
EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval1.hasBase);
EXPECT_EQ(11, interval1.base.long_value);
- EXPECT_EQ(true, interval1.hasValue);
+ EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(8, interval1.value.long_value);
- EXPECT_TRUE(interval1.seenNewData);
+ EXPECT_FALSE(interval1.seenNewData);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
auto it = valueProducer.mCurrentSlicedBucket.begin();
for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
@@ -1908,8 +1990,8 @@
EXPECT_EQ(true, interval2.hasBase);
EXPECT_EQ(4, interval2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
- EXPECT_TRUE(interval2.seenNewData);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_FALSE(interval2.seenNewData);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
// next pull somehow did not happen, skip to end of bucket 3
allData.clear();
@@ -1920,35 +2002,34 @@
allData.push_back(event1);
valueProducer.onDataPulled(allData, /** succeed */ true);
- EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
-
- EXPECT_EQ(false, interval1.hasBase);
- EXPECT_EQ(false, interval1.hasValue);
- EXPECT_EQ(8, interval1.value.long_value);
- // on probation now
- EXPECT_FALSE(interval1.seenNewData);
-
+ // Only one interval left. One was trimmed.
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ interval2 = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, interval2.hasBase);
EXPECT_EQ(5, interval2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
- // back to good status
- EXPECT_TRUE(interval2.seenNewData);
+ EXPECT_FALSE(interval2.seenNewData);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
allData.clear();
event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
event1->write(2);
- event1->write(13);
+ event1->write(14);
event1->init();
allData.push_back(event1);
valueProducer.onDataPulled(allData, /** succeed */ true);
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ interval2 = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(13, interval2.base.long_value);
- EXPECT_EQ(true, interval2.hasValue);
- EXPECT_EQ(8, interval2.value.long_value);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(14, interval2.base.long_value);
+ EXPECT_EQ(false, interval2.hasValue);
+ EXPECT_FALSE(interval2.seenNewData);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
+ auto iterator = valueProducer.mPastBuckets.begin();
+ EXPECT_EQ(9, iterator->second[0].values[0].long_value);
+ iterator++;
+ EXPECT_EQ(8, iterator->second[0].values[0].long_value);
}
TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
@@ -2110,7 +2191,7 @@
EXPECT_EQ(false, valueProducer.mHasGlobalBase);
}
-TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
+TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
ValueMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -2133,7 +2214,7 @@
EXPECT_CALL(*pullerManager, Pull(tagId, _))
.WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
event->write(tagId);
event->write(120);
event->init();
@@ -2145,35 +2226,91 @@
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.mCondition = true;
+ valueProducer.mCondition = false;
+
+ // Max delay is set to 0 so pull will exceed max delay.
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
+ EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
+}
+
+TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_condition(StringToId("SCREEN_ON"));
+ metric.set_max_pull_delay_sec(INT_MAX);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucket2StartTimeNs,
+ bucket2StartTimeNs, pullerManager);
+
+ valueProducer.mCondition = false;
+
+ // Event should be skipped since it is from previous bucket.
+ // Pull should not be called.
+ valueProducer.onConditionChanged(true, bucketStartTimeNs);
+ EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
+}
+
+TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_condition(StringToId("SCREEN_ON"));
+ metric.set_max_pull_delay_sec(INT_MAX);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+ event->write(tagId);
+ event->write(100);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ valueProducer.mCondition = false;
+ valueProducer.mHasGlobalBase = false;
+
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
valueProducer.mHasGlobalBase = true;
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(1);
- event->write(110);
- event->init();
- allData.push_back(event);
- valueProducer.onDataPulled(allData, /** succeed */ true);
-
- // has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(110, curInterval.base.long_value);
+ EXPECT_EQ(100, curInterval.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(true, valueProducer.mHasGlobalBase);
-
- valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
-
- // has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(false, curInterval.hasBase);
- EXPECT_EQ(false, valueProducer.mHasGlobalBase);
}
TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) {
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index 920a52d..d29e68e 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.os.BatteryManager;
import android.os.IPowerManager;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -71,7 +72,8 @@
if (val != 0) {
// if the request is not to set it to false, wake up the screen so that
// it can stay on as requested
- pm.wakeUp(SystemClock.uptimeMillis(), "PowerCommand", null);
+ pm.wakeUp(SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_UNKNOWN, "PowerCommand", null);
}
pm.setStayOnSetting(val);
}
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 6061b66..7edd128 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -33456,7 +33456,6 @@
HSPLandroid/view/SurfaceControl;->finalize()V
HSPLandroid/view/SurfaceControl;->getActiveColorMode(Landroid/os/IBinder;)I
HSPLandroid/view/SurfaceControl;->getActiveConfig(Landroid/os/IBinder;)I
-HSPLandroid/view/SurfaceControl;->getBuiltInDisplay(I)Landroid/os/IBinder;
HSPLandroid/view/SurfaceControl;->getDisplayColorModes(Landroid/os/IBinder;)[I
HSPLandroid/view/SurfaceControl;->getDisplayConfigs(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$PhysicalDisplayInfo;
HSPLandroid/view/SurfaceControl;->getHandle()Landroid/os/IBinder;
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 11bb38b..a0d8a12 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -328,17 +328,12 @@
Landroid/content/om/IOverlayManager;->getOverlayInfo(Ljava/lang/String;I)Landroid/content/om/OverlayInfo;
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
-Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->onRemoveCompleted(Ljava/lang/String;Z)V
Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
-Landroid/content/pm/IPackageDataObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
-Landroid/content/pm/IPackageDataObserver$Stub;->TRANSACTION_onRemoveCompleted:I
Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
Landroid/content/pm/IPackageDeleteObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver;
-Landroid/content/pm/IPackageDeleteObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
-Landroid/content/pm/IPackageDeleteObserver$Stub;->TRANSACTION_packageDeleted:I
Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/content/pm/IPackageDeleteObserver2$Stub;-><init>()V
@@ -437,8 +432,6 @@
Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V
Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver;
-Landroid/content/pm/IPackageStatsObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
-Landroid/content/pm/IPackageStatsObserver$Stub;->TRANSACTION_onGetStatsCompleted:I
Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService;
@@ -835,7 +828,6 @@
Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
Landroid/os/IPowerManager;->releaseWakeLock(Landroid/os/IBinder;I)V
Landroid/os/IPowerManager;->userActivity(JII)V
-Landroid/os/IPowerManager;->wakeUp(JLjava/lang/String;Ljava/lang/String;)V
Landroid/os/IRecoverySystem$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IRecoverySystem;
Landroid/os/IRemoteCallback$Stub;-><init>()V
Landroid/os/IRemoteCallback;->sendResult(Landroid/os/Bundle;)V
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index a0464df..ebb03e7 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Looper;
import android.os.Trace;
import android.util.AndroidRuntimeException;
@@ -76,7 +77,13 @@
/**
* Internal constants
*/
- @UnsupportedAppUsage
+
+ /**
+ * System-wide animation scale.
+ *
+ * <p>To check whether animations are enabled system-wise use {@link #areAnimatorsEnabled()}.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static float sDurationScale = 1.0f;
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 428c9b0..3ff6973 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6408,11 +6408,9 @@
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param packageName The name of the package to set as the default SMS application.
* @throws SecurityException if {@code admin} is not a device owner.
- *
- * @hide
*/
- @UnsupportedAppUsage
- public void setDefaultSmsApplication(@NonNull ComponentName admin, String packageName) {
+ public void setDefaultSmsApplication(@NonNull ComponentName admin,
+ @NonNull String packageName) {
throwIfParentInstance("setDefaultSmsApplication");
if (mService != null) {
try {
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index cf62e8d..76dbf7e 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -32,13 +32,14 @@
List<String> getRoleHoldersAsUser(in String roleName, int userId);
- void addRoleHolderAsUser(in String roleName, in String packageName, int userId,
+ void addRoleHolderAsUser(in String roleName, in String packageName, int flags, int userId,
in IRoleManagerCallback callback);
- void removeRoleHolderAsUser(in String roleName, in String packageName, int userId,
+ void removeRoleHolderAsUser(in String roleName, in String packageName, int flags, int userId,
in IRoleManagerCallback callback);
- void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback);
+ void clearRoleHoldersAsUser(in String roleName, int flags, int userId,
+ in IRoleManagerCallback callback);
void addOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, int userId);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index ddd5313..fa2a6a1 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -181,6 +182,22 @@
public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
/**
+ * @hide
+ */
+ @IntDef(flag = true, value = { MANAGE_HOLDERS_FLAG_DONT_KILL_APP })
+ public @interface ManageHoldersFlags {}
+
+ /**
+ * Flag parameter for {@link #addRoleHolderAsUser}, {@link #removeRoleHolderAsUser} and
+ * {@link #clearRoleHoldersAsUser} to indicate that apps should not be killed when changing
+ * their role holder status.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1;
+
+ /**
* The action used to request user approval of a role for an application.
*
* @hide
@@ -305,9 +322,9 @@
*
* @return a list of package names of the role holders, or an empty list if none.
*
- * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
- * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
- * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
+ * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback)
+ * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback)
+ * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, RoleManagerCallback)
*
* @hide
*/
@@ -335,13 +352,14 @@
*
* @param roleName the name of the role to add the role holder for
* @param packageName the package name of the application to add to the role holders
+ * @param flags optional behavior flags
* @param user the user to add the role holder for
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
* @see #getRoleHoldersAsUser(String, UserHandle)
- * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
- * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
+ * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback)
+ * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, RoleManagerCallback)
*
* @hide
*/
@@ -349,15 +367,15 @@
@SystemApi
@TestApi
public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
- @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
- @NonNull RoleManagerCallback callback) {
+ @ManageHoldersFlags int flags, @NonNull UserHandle user,
+ @CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
Preconditions.checkNotNull(user, "user cannot be null");
Preconditions.checkNotNull(executor, "executor cannot be null");
Preconditions.checkNotNull(callback, "callback cannot be null");
try {
- mService.addRoleHolderAsUser(roleName, packageName, user.getIdentifier(),
+ mService.addRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(),
new RoleManagerCallbackDelegate(executor, callback));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -373,13 +391,14 @@
*
* @param roleName the name of the role to remove the role holder for
* @param packageName the package name of the application to remove from the role holders
+ * @param flags optional behavior flags
* @param user the user to remove the role holder for
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
* @see #getRoleHoldersAsUser(String, UserHandle)
- * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
- * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
+ * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback)
+ * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, RoleManagerCallback)
*
* @hide
*/
@@ -387,15 +406,15 @@
@SystemApi
@TestApi
public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
- @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
- @NonNull RoleManagerCallback callback) {
+ @ManageHoldersFlags int flags, @NonNull UserHandle user,
+ @CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
Preconditions.checkNotNull(user, "user cannot be null");
Preconditions.checkNotNull(executor, "executor cannot be null");
Preconditions.checkNotNull(callback, "callback cannot be null");
try {
- mService.removeRoleHolderAsUser(roleName, packageName, user.getIdentifier(),
+ mService.removeRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(),
new RoleManagerCallbackDelegate(executor, callback));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -410,27 +429,29 @@
* {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
*
* @param roleName the name of the role to remove role holders for
+ * @param flags optional behavior flags
* @param user the user to remove role holders for
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
* @see #getRoleHoldersAsUser(String, UserHandle)
- * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
- * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
+ * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback)
+ * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, RoleManagerCallback)
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
@TestApi
- public void clearRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user,
- @CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) {
+ public void clearRoleHoldersAsUser(@NonNull String roleName, @ManageHoldersFlags int flags,
+ @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
+ @NonNull RoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkNotNull(user, "user cannot be null");
Preconditions.checkNotNull(executor, "executor cannot be null");
Preconditions.checkNotNull(callback, "callback cannot be null");
try {
- mService.clearRoleHoldersAsUser(roleName, user.getIdentifier(),
+ mService.clearRoleHoldersAsUser(roleName, flags, user.getIdentifier(),
new RoleManagerCallbackDelegate(executor, callback));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a3021f3..d781a96 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -817,28 +817,6 @@
= "android.intent.action.SHOW_APP_INFO";
/**
- * Activity Action: Start an activity to show the app's detailed usage information for
- * permission protected data.
- *
- * The Intent contains an extra {@link #EXTRA_PERMISSION_USAGE_PERMISSIONS} that is of
- * type {@code String[]} and contains the specific permissions to show information for.
- *
- * Apps should handle this intent if they want to provide more information about permission
- * usage to users beyond the information provided in the manifest.
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_PERMISSION_USAGE_DETAILS =
- "android.intent.action.PERMISSION_USAGE_DETAILS";
-
- /**
- * The name of the extra used to contain the permissions in
- * {@link #ACTION_PERMISSION_USAGE_DETAILS}.
- * @see #ACTION_PERMISSION_USAGE_DETAILS
- */
- public static final String EXTRA_PERMISSION_USAGE_PERMISSIONS =
- "android.intent.extra.PERMISSION_USAGE_PERMISSIONS";
-
- /**
* Represents a shortcut/live folder icon resource.
*
* @see Intent#ACTION_CREATE_SHORTCUT
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 6d22277..27a5b39 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -204,10 +204,7 @@
* {@link PackageManager#GET_PERMISSIONS} was set. This list includes
* all permissions requested, even those that were not granted or known
* by the system at install time.
- *
- * @deprecated Use {@link #usesPermissions}
*/
- @Deprecated
public String[] requestedPermissions;
/**
@@ -217,23 +214,10 @@
* {@link PackageManager#GET_PERMISSIONS} was set. Each value matches
* the corresponding entry in {@link #requestedPermissions}, and will have
* the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
- *
- * @deprecated Use {@link #usesPermissions}
*/
- @Deprecated
public int[] requestedPermissionsFlags;
/**
- * Array of all {@link android.R.styleable#AndroidManifestUsesPermission
- * <uses-permission>} tags included under <manifest>,
- * or null if there were none. This is only filled in if the flag
- * {@link PackageManager#GET_PERMISSIONS} was set. This list includes
- * all permissions requested, even those that were not granted or known
- * by the system at install time.
- */
- public UsesPermissionInfo[] usesPermissions;
-
- /**
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is required for the application to run; the user can not optionally
* disable it. Currently all permissions are required.
@@ -480,7 +464,6 @@
dest.writeTypedArray(permissions, parcelableFlags);
dest.writeStringArray(requestedPermissions);
dest.writeIntArray(requestedPermissionsFlags);
- dest.writeTypedArray(usesPermissions, parcelableFlags);
dest.writeTypedArray(signatures, parcelableFlags);
dest.writeTypedArray(configPreferences, parcelableFlags);
dest.writeTypedArray(reqFeatures, parcelableFlags);
@@ -545,7 +528,6 @@
permissions = source.createTypedArray(PermissionInfo.CREATOR);
requestedPermissions = source.createStringArray();
requestedPermissionsFlags = source.createIntArray();
- usesPermissions = source.createTypedArray(UsesPermissionInfo.CREATOR);
signatures = source.createTypedArray(Signature.CREATOR);
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 81ed110..5020a94 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -791,23 +791,18 @@
pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags);
}
}
- N = p.usesPermissionInfos.size();
+ N = p.requestedPermissions.size();
if (N > 0) {
pi.requestedPermissions = new String[N];
pi.requestedPermissionsFlags = new int[N];
- pi.usesPermissions = new UsesPermissionInfo[N];
for (int i=0; i<N; i++) {
- UsesPermissionInfo info = p.usesPermissionInfos.get(i);
- final String perm = info.getPermission();
+ final String perm = p.requestedPermissions.get(i);
pi.requestedPermissions[i] = perm;
- int permissionFlags = 0;
// The notion of required permissions is deprecated but for compatibility.
- permissionFlags |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+ pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
if (grantedPermissions != null && grantedPermissions.contains(perm)) {
- permissionFlags |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
+ pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
- pi.requestedPermissionsFlags[i] = permissionFlags;
- pi.usesPermissions[i] = new UsesPermissionInfo(info, permissionFlags);
}
}
}
@@ -2175,12 +2170,12 @@
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION)) {
- if (!parseUsesPermission(pkg, res, parser, outError)) {
+ if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
- if (!parseUsesPermission(pkg, res, parser, outError)) {
+ if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
} else if (tagName.equals(TAG_USES_CONFIGURATION)) {
@@ -2498,7 +2493,7 @@
newPermsMsg.append(' ');
}
newPermsMsg.append(npi.name);
- addRequestedPermission(pkg, npi.name);
+ pkg.requestedPermissions.add(npi.name);
pkg.implicitPermissions.add(npi.name);
}
}
@@ -2519,7 +2514,7 @@
for (int in = 0; in < newPerms.size(); in++) {
final String perm = newPerms.get(in);
if (!pkg.requestedPermissions.contains(perm)) {
- addRequestedPermission(pkg, perm);
+ pkg.requestedPermissions.add(perm);
pkg.implicitPermissions.add(perm);
}
}
@@ -2599,13 +2594,13 @@
}
} else {
if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) {
- addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_AUDIO);
+ pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
}
if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) {
- addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_VIDEO);
+ pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
}
if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) {
- addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_IMAGES);
+ pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
}
}
@@ -2645,12 +2640,6 @@
}
/**
- * Helper method for adding a requested permission to a package outside of a uses-permission.
- */
- private void addRequestedPermission(Package pkg, String permission) {
- pkg.requestedPermissions.add(permission);
- pkg.usesPermissionInfos.add(new UsesPermissionInfo(permission));
- }
/**
* Matches a given {@code targetCode} against a set of release codeNames. Target codes can
@@ -2987,8 +2976,8 @@
return certSha256Digests;
}
- private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
- String[] outError) throws XmlPullParserException, IOException {
+ private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesPermission);
@@ -3012,44 +3001,6 @@
final String requiredNotfeature = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0);
- int dataSentOffDevice = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSentOffDevice, 0);
-
- int dataSharedWithThirdParty = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSharedWithThirdParty, 0);
-
- int dataUsedForMonetization = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_dataUsedForMonetization, 0);
-
- int retentionWeeks = -1;
- int retention;
-
- String rawRetention = sa.getString(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime);
-
- if (rawRetention == null) {
- retention = UsesPermissionInfo.RETENTION_UNDEFINED;
- } else if ("notRetained".equals(rawRetention)) {
- retention = UsesPermissionInfo.RETENTION_NOT_RETAINED;
- } else if ("userSelected".equals(rawRetention)) {
- retention = UsesPermissionInfo.RETENTION_USER_SELECTED;
- } else if ("unlimited".equals(rawRetention)) {
- retention = UsesPermissionInfo.RETENTION_UNLIMITED;
- } else {
- // A number of weeks was specified
- retention = UsesPermissionInfo.RETENTION_SPECIFIED;
- retentionWeeks = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime,
- -1);
-
- if (retentionWeeks < 0) {
- outError[0] = "Bad value provided for dataRetentionTime.";
- mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- XmlUtils.skipCurrentTag(parser);
- sa.recycle();
- return false;
- }
- }
sa.recycle();
XmlUtils.skipCurrentTag(parser);
@@ -3082,10 +3033,6 @@
+ parser.getPositionDescription());
}
- UsesPermissionInfo info = new UsesPermissionInfo(name, dataSentOffDevice,
- dataSharedWithThirdParty, dataUsedForMonetization, retention, retentionWeeks);
- pkg.usesPermissionInfos.add(info);
-
return true;
}
@@ -3420,10 +3367,6 @@
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0);
- perm.info.usageInfoRequired = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestPermission_usageInfoRequired, 0)
- != 0;
-
sa.recycle();
if (perm.info.protectionLevel == -1) {
@@ -6625,9 +6568,6 @@
@UnsupportedAppUsage
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
- public final ArrayList<UsesPermissionInfo> usesPermissionInfos =
- new ArrayList<>();
-
/** Permissions requested but not in the manifest. */
public final ArrayList<String> implicitPermissions = new ArrayList<>();
@@ -7159,7 +7099,6 @@
dest.readStringList(requestedPermissions);
internStringArrayList(requestedPermissions);
- dest.readParcelableList(usesPermissionInfos, boot);
dest.readStringList(implicitPermissions);
internStringArrayList(implicitPermissions);
protectedBroadcasts = dest.createStringArrayList();
@@ -7327,7 +7266,6 @@
dest.writeParcelableList(instrumentation, flags);
dest.writeStringList(requestedPermissions);
- dest.writeParcelableList(usesPermissionInfos, flags);
dest.writeStringList(implicitPermissions);
dest.writeStringList(protectedBroadcasts);
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index fb63e0d..e245234 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -20,7 +20,6 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -368,12 +367,6 @@
*/
public CharSequence nonLocalizedDescription;
- /**
- * If {@code true} an application targeting {@link Build.VERSION_CODES#Q} <em>must</em>
- * include permission data usage information in order to be able to be granted this permission.
- */
- public boolean usageInfoRequired;
-
/** @hide */
public static int fixProtectionLevel(int level) {
if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
@@ -475,7 +468,6 @@
descriptionRes = orig.descriptionRes;
requestRes = orig.requestRes;
nonLocalizedDescription = orig.nonLocalizedDescription;
- usageInfoRequired = orig.usageInfoRequired;
}
/**
@@ -540,7 +532,6 @@
dest.writeInt(descriptionRes);
dest.writeInt(requestRes);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
- dest.writeInt(usageInfoRequired ? 1 : 0);
}
/** @hide */
@@ -581,6 +572,5 @@
descriptionRes = source.readInt();
requestRes = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- usageInfoRequired = source.readInt() != 0;
}
}
diff --git a/core/java/android/content/pm/UsesPermissionInfo.java b/core/java/android/content/pm/UsesPermissionInfo.java
deleted file mode 100644
index d08548f..0000000
--- a/core/java/android/content/pm/UsesPermissionInfo.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.IntDef;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.RetentionPolicy;
-/**
- * Information you can retrive about a particular application requested permission. This
- * corresponds to information collected from the AndroidManifest.xml's <uses-permission>
- * tags.
- */
-public final class UsesPermissionInfo extends PackageItemInfo implements Parcelable {
-
- /**
- * Flag for {@link #getFlags()}: the requested permission is currently granted to the
- * application.
- */
- public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 1 << 1;
-
- /** @hide */
- @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_REQUESTED_PERMISSION_GRANTED})
- @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
- public @interface Flags {}
-
- /** An unset value for {@link #getDataSentOffDevice()},
- * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()}
- */
- public static final int USAGE_UNDEFINED = 0;
-
- /**
- * A yes value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()},
- * and {@link #getDataUsedForMonetization()} corresponding to the <code>yes</code> value of
- * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
- * and {@link android.R.attr#dataUsedForMonetization} attributes.
- */
- public static final int USAGE_YES = 1;
-
- /**
- * A user triggered only value for {@link #getDataSentOffDevice()},
- * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()}
- * corresponding to the <code>userTriggered</code> value of
- * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
- * and {@link android.R.attr#dataUsedForMonetization} attributes.
- */
- public static final int USAGE_USER_TRIGGERED = 2;
-
- /**
- * A no value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()},
- * and {@link #getDataUsedForMonetization()} corresponding to the <code>no</code> value of
- * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
- * and {@link android.R.attr#dataUsedForMonetization} attributes.
- */
- public static final int USAGE_NO = 3;
-
- /** @hide */
- @IntDef(prefix = {"USAGE_"}, value = {
- USAGE_UNDEFINED,
- USAGE_YES,
- USAGE_USER_TRIGGERED,
- USAGE_NO})
- @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
- public @interface Usage {}
-
- /**
- * An unset value for {@link #getDataRetention}.
- */
- public static final int RETENTION_UNDEFINED = 0;
-
- /**
- * A data not retained value for {@link #getDataRetention()} corresponding to the
- * <code>notRetained</code> value of {@link android.R.attr#dataRetentionTime}.
- */
- public static final int RETENTION_NOT_RETAINED = 1;
-
- /**
- * A user selected value for {@link #getDataRetention()} corresponding to the
- * <code>userSelected</code> value of {@link android.R.attr#dataRetentionTime}.
- */
- public static final int RETENTION_USER_SELECTED = 2;
-
- /**
- * An unlimited value for {@link #getDataRetention()} corresponding to the
- * <code>unlimited</code> value of {@link android.R.attr#dataRetentionTime}.
- */
- public static final int RETENTION_UNLIMITED = 3;
-
- /**
- * A specified value for {@link #getDataRetention()} corresponding to providing the number of
- * weeks data is retained in {@link android.R.attr#dataRetentionTime}. The number of weeks
- * is available in {@link #getDataRetentionWeeks()}.
- */
- public static final int RETENTION_SPECIFIED = 4;
-
- /** @hide */
- @IntDef(prefix = {"RETENTION_"}, value = {
- RETENTION_UNDEFINED,
- RETENTION_NOT_RETAINED,
- RETENTION_USER_SELECTED,
- RETENTION_UNLIMITED,
- RETENTION_SPECIFIED})
- @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
- public @interface Retention {}
-
- private final String mPermission;
- private final @Flags int mFlags;
- private final @Usage int mDataSentOffDevice;
- private final @Usage int mDataSharedWithThirdParty;
- private final @Usage int mDataUsedForMonetization;
- private final @Retention int mDataRetention;
- private final int mDataRetentionWeeks;
-
- /** @hide */
- public UsesPermissionInfo(String permission) {
- mPermission = permission;
- mDataSentOffDevice = USAGE_UNDEFINED;
- mDataSharedWithThirdParty = USAGE_UNDEFINED;
- mDataUsedForMonetization = USAGE_UNDEFINED;
- mDataRetention = RETENTION_UNDEFINED;
- mDataRetentionWeeks = -1;
- mFlags = 0;
- }
-
- /** @hide */
- public UsesPermissionInfo(String permission,
- @Usage int dataSentOffDevice, @Usage int dataSharedWithThirdParty,
- @Usage int dataUsedForMonetization, @Retention int dataRetention,
- int dataRetentionWeeks) {
- mPermission = permission;
- mDataSentOffDevice = dataSentOffDevice;
- mDataSharedWithThirdParty = dataSharedWithThirdParty;
- mDataUsedForMonetization = dataUsedForMonetization;
- mDataRetention = dataRetention;
- mDataRetentionWeeks = dataRetentionWeeks;
- mFlags = 0;
- }
-
- /** @hide */
- public UsesPermissionInfo(UsesPermissionInfo orig) {
- this(orig, orig.mFlags);
- }
-
- /** @hide */
- public UsesPermissionInfo(UsesPermissionInfo orig, int flags) {
- super(orig);
- mPermission = orig.mPermission;
- mFlags = flags;
- mDataSentOffDevice = orig.mDataSentOffDevice;
- mDataSharedWithThirdParty = orig.mDataSharedWithThirdParty;
- mDataUsedForMonetization = orig.mDataUsedForMonetization;
- mDataRetention = orig.mDataRetention;
- mDataRetentionWeeks = orig.mDataRetentionWeeks;
- }
-
- /**
- * The name of the requested permission.
- */
- public String getPermission() {
- return mPermission;
- }
-
- public @Flags int getFlags() {
- return mFlags;
- }
-
- /**
- * If the application sends the data guarded by this permission off the device.
- *
- * See {@link android.R.attr#dataSentOffDevice}
- */
- public @Usage int getDataSentOffDevice() {
- return mDataSentOffDevice;
- }
-
- /**
- * If the application or its services shares the data guarded by this permission with third
- * parties.
- *
- * See {@link android.R.attr#dataSharedWithThirdParty}
- */
- public @Usage int getDataSharedWithThirdParty() {
- return mDataSharedWithThirdParty;
- }
-
- /**
- * If the application or its services use the data guarded by this permission for monetization
- * purposes.
- *
- * See {@link android.R.attr#dataUsedForMonetization}
- */
- public @Usage int getDataUsedForMonetization() {
- return mDataUsedForMonetization;
- }
-
- /**
- * How long the application or its services store the data guarded by this permission.
- * If set to {@link #RETENTION_SPECIFIED} {@link #getDataRetentionWeeks()} will contain the
- * number of weeks the data is stored.
- *
- * See {@link android.R.attr#dataRetentionTime}
- */
- public @Retention int getDataRetention() {
- return mDataRetention;
- }
-
- /**
- * If {@link #getDataRetention()} is {@link #RETENTION_SPECIFIED} the number of weeks the
- * application or its services store data guarded by this permission.
- *
- * @throws IllegalStateException if {@link #getDataRetention} is not
- * {@link #RETENTION_SPECIFIED}.
- */
- public int getDataRetentionWeeks() {
- if (mDataRetention != RETENTION_SPECIFIED) {
- throw new IllegalStateException("Data retention weeks not specified");
- }
- return mDataRetentionWeeks;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeString(mPermission);
- dest.writeInt(mFlags);
- dest.writeInt(mDataSentOffDevice);
- dest.writeInt(mDataSharedWithThirdParty);
- dest.writeInt(mDataUsedForMonetization);
- dest.writeInt(mDataRetention);
- dest.writeInt(mDataRetentionWeeks);
- }
-
- private UsesPermissionInfo(Parcel source) {
- super(source);
- mPermission = source.readString();
- mFlags = source.readInt();
- mDataSentOffDevice = source.readInt();
- mDataSharedWithThirdParty = source.readInt();
- mDataUsedForMonetization = source.readInt();
- mDataRetention = source.readInt();
- mDataRetentionWeeks = source.readInt();
- }
-
- public static final Creator<UsesPermissionInfo> CREATOR =
- new Creator<UsesPermissionInfo>() {
- @Override
- public UsesPermissionInfo createFromParcel(Parcel source) {
- return new UsesPermissionInfo(source);
- }
- @Override
- public UsesPermissionInfo[] newArray(int size) {
- return new UsesPermissionInfo[size];
- }
- };
-}
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 0ec4018..1d0ab5a 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -22,6 +22,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.IntArray;
+import android.util.SparseLongArray;
import java.util.ArrayList;
@@ -73,6 +74,18 @@
*/
private final boolean mIsApex;
+ /*
+ * The list of users the package is installed for.
+ */
+ // NOTE: Not a part of the Parcelable representation of this object.
+ private final IntArray mInstalledUsers;
+
+ /**
+ * A mapping between user and an inode of theirs CE data snapshot.
+ */
+ // NOTE: Not a part of the Parcelable representation of this object.
+ private final SparseLongArray mCeSnapshotInodes;
+
/**
* Returns the name of the package to roll back from.
*/
@@ -126,15 +139,33 @@
}
/** @hide */
+ public IntArray getInstalledUsers() {
+ return mInstalledUsers;
+ }
+
+ /** @hide */
+ public SparseLongArray getCeSnapshotInodes() {
+ return mCeSnapshotInodes;
+ }
+
+ /** @hide */
+ public void putCeSnapshotInode(int userId, long ceSnapshotInode) {
+ mCeSnapshotInodes.put(userId, ceSnapshotInode);
+ }
+
+ /** @hide */
public PackageRollbackInfo(VersionedPackage packageRolledBackFrom,
VersionedPackage packageRolledBackTo,
@NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
- boolean isApex) {
+ boolean isApex, @NonNull IntArray installedUsers,
+ @NonNull SparseLongArray ceSnapshotInodes) {
this.mVersionRolledBackFrom = packageRolledBackFrom;
this.mVersionRolledBackTo = packageRolledBackTo;
this.mPendingBackups = pendingBackups;
this.mPendingRestores = pendingRestores;
this.mIsApex = isApex;
+ this.mInstalledUsers = installedUsers;
+ this.mCeSnapshotInodes = ceSnapshotInodes;
}
private PackageRollbackInfo(Parcel in) {
@@ -143,6 +174,8 @@
this.mIsApex = in.readBoolean();
this.mPendingRestores = null;
this.mPendingBackups = null;
+ this.mInstalledUsers = null;
+ this.mCeSnapshotInodes = null;
}
@Override
diff --git a/core/java/android/hardware/display/NightDisplayListener.java b/core/java/android/hardware/display/NightDisplayListener.java
new file mode 100644
index 0000000..468f833
--- /dev/null
+++ b/core/java/android/hardware/display/NightDisplayListener.java
@@ -0,0 +1,153 @@
+/*
+ * 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.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings.Secure;
+
+import java.time.LocalTime;
+
+/**
+ * @hide
+ */
+public class NightDisplayListener {
+
+ private final Context mContext;
+ private final int mUserId;
+ private final ColorDisplayManager mManager;
+
+ private ContentObserver mContentObserver;
+ private Callback mCallback;
+
+ public NightDisplayListener(@NonNull Context context) {
+ this(context, ActivityManager.getCurrentUser());
+ }
+
+ public NightDisplayListener(@NonNull Context context, @UserIdInt int userId) {
+ mContext = context.getApplicationContext();
+ mUserId = userId;
+ mManager = mContext.getSystemService(ColorDisplayManager.class);
+ }
+
+ /**
+ * Register a callback to be invoked whenever the Night display settings are changed.
+ */
+ public void setCallback(Callback callback) {
+ final Callback oldCallback = mCallback;
+ if (oldCallback != callback) {
+ mCallback = callback;
+
+ if (mContentObserver == null) {
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ onSettingChanged(uri);
+ }
+ };
+ }
+
+ if (callback == null) {
+ // Stop listening for changes now that there IS NOT a callback.
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+ } else if (oldCallback == null) {
+ // Start listening for changes now that there IS a callback.
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
+ false /* notifyForDescendants */, mContentObserver, mUserId);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
+ false /* notifyForDescendants */, mContentObserver, mUserId);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
+ false /* notifyForDescendants */, mContentObserver, mUserId);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
+ false /* notifyForDescendants */, mContentObserver, mUserId);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
+ false /* notifyForDescendants */, mContentObserver, mUserId);
+ }
+ }
+ }
+
+ private void onSettingChanged(Uri uri) {
+ final String setting = uri == null ? null : uri.getLastPathSegment();
+ if (setting == null || mCallback == null) {
+ return;
+ }
+
+ switch (setting) {
+ case Secure.NIGHT_DISPLAY_ACTIVATED:
+ mCallback.onActivated(mManager.isNightDisplayActivated());
+ break;
+ case Secure.NIGHT_DISPLAY_AUTO_MODE:
+ mCallback.onAutoModeChanged(mManager.getNightDisplayAutoMode());
+ break;
+ case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
+ mCallback.onCustomStartTimeChanged(mManager.getNightDisplayCustomStartTime());
+ break;
+ case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
+ mCallback.onCustomEndTimeChanged(mManager.getNightDisplayCustomEndTime());
+ break;
+ case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
+ mCallback.onColorTemperatureChanged(mManager.getNightDisplayColorTemperature());
+ break;
+ }
+ }
+
+ /**
+ * Callback invoked whenever the Night display settings are changed.
+ */
+ public interface Callback {
+ /**
+ * Callback invoked when the activated state changes.
+ *
+ * @param activated {@code true} if Night display is activated
+ */
+ default void onActivated(boolean activated) {}
+ /**
+ * Callback invoked when the auto mode changes.
+ *
+ * @param autoMode the auto mode to use
+ */
+ default void onAutoModeChanged(int autoMode) {}
+ /**
+ * Callback invoked when the time to automatically activate Night display changes.
+ *
+ * @param startTime the local time to automatically activate Night display
+ */
+ default void onCustomStartTimeChanged(LocalTime startTime) {}
+ /**
+ * Callback invoked when the time to automatically deactivate Night display changes.
+ *
+ * @param endTime the local time to automatically deactivate Night display
+ */
+ default void onCustomEndTimeChanged(LocalTime endTime) {}
+
+ /**
+ * Callback invoked when the color temperature changes.
+ *
+ * @param colorTemperature the color temperature to tint the screen
+ */
+ default void onColorTemperatureChanged(int colorTemperature) {}
+ }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 8a141e2..2aca55a 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3923,6 +3923,25 @@
}
/**
+ * Requests that the system open the captive portal app with the specified extras.
+ *
+ * <p>This endpoint is exclusively for use by the NetworkStack and is protected by the
+ * corresponding permission.
+ * @param appExtras Extras to include in the app start intent.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+ public void startCaptivePortalApp(Bundle appExtras) {
+ try {
+ mService.startCaptivePortalAppInternal(appExtras);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Determine whether the device is configured to avoid bad wifi.
* @hide
*/
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3a405d3..872671f 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -27,6 +27,7 @@
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.ProxyInfo;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
@@ -167,6 +168,7 @@
void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
void setAvoidUnvalidated(in Network network);
void startCaptivePortalApp(in Network network);
+ void startCaptivePortalAppInternal(in Bundle appExtras);
boolean getAvoidBadWifi();
int getMultipathPreference(in Network Network);
diff --git a/core/java/android/os/BatterySaverPolicyConfig.java b/core/java/android/os/BatterySaverPolicyConfig.java
index b6e2b69..a107a7a 100644
--- a/core/java/android/os/BatterySaverPolicyConfig.java
+++ b/core/java/android/os/BatterySaverPolicyConfig.java
@@ -47,10 +47,11 @@
private final boolean mEnableAdjustBrightness;
private final boolean mEnableDataSaver;
private final boolean mEnableFirewall;
+ private final boolean mEnableNightMode;
private final boolean mEnableQuickDoze;
private final boolean mForceAllAppsStandby;
private final boolean mForceBackgroundCheck;
- private final int mGpsMode;
+ private final int mLocationMode;
private BatterySaverPolicyConfig(Builder in) {
mAdjustBrightnessFactor = Math.max(0, Math.min(in.mAdjustBrightnessFactor, 1f));
@@ -67,11 +68,12 @@
mEnableAdjustBrightness = in.mEnableAdjustBrightness;
mEnableDataSaver = in.mEnableDataSaver;
mEnableFirewall = in.mEnableFirewall;
+ mEnableNightMode = in.mEnableNightMode;
mEnableQuickDoze = in.mEnableQuickDoze;
mForceAllAppsStandby = in.mForceAllAppsStandby;
mForceBackgroundCheck = in.mForceBackgroundCheck;
- mGpsMode = Math.max(PowerManager.MIN_LOCATION_MODE,
- Math.min(in.mGpsMode, PowerManager.MAX_LOCATION_MODE));
+ mLocationMode = Math.max(PowerManager.MIN_LOCATION_MODE,
+ Math.min(in.mLocationMode, PowerManager.MAX_LOCATION_MODE));
}
private BatterySaverPolicyConfig(Parcel in) {
@@ -101,10 +103,11 @@
mEnableAdjustBrightness = in.readBoolean();
mEnableDataSaver = in.readBoolean();
mEnableFirewall = in.readBoolean();
+ mEnableNightMode = in.readBoolean();
mEnableQuickDoze = in.readBoolean();
mForceAllAppsStandby = in.readBoolean();
mForceBackgroundCheck = in.readBoolean();
- mGpsMode = Math.max(PowerManager.MIN_LOCATION_MODE,
+ mLocationMode = Math.max(PowerManager.MIN_LOCATION_MODE,
Math.min(in.readInt(), PowerManager.MAX_LOCATION_MODE));
}
@@ -150,10 +153,11 @@
dest.writeBoolean(mEnableAdjustBrightness);
dest.writeBoolean(mEnableDataSaver);
dest.writeBoolean(mEnableFirewall);
+ dest.writeBoolean(mEnableNightMode);
dest.writeBoolean(mEnableQuickDoze);
dest.writeBoolean(mForceAllAppsStandby);
dest.writeBoolean(mForceBackgroundCheck);
- dest.writeInt(mGpsMode);
+ dest.writeInt(mLocationMode);
}
@Override
@@ -168,11 +172,12 @@
+ "animation_disabled=" + mDisableAnimation + ","
+ "aod_disabled=" + mDisableAod + ","
+ "datasaver_disabled=" + !mEnableDataSaver + ","
+ + "enable_night_mode=" + mEnableNightMode + ","
+ "firewall_disabled=" + !mEnableFirewall + ","
+ "force_all_apps_standby=" + mForceAllAppsStandby + ","
+ "force_background_check=" + mForceBackgroundCheck + ","
+ "fullbackup_deferred=" + mDeferFullBackup + ","
- + "gps_mode=" + mGpsMode + ","
+ + "gps_mode=" + mLocationMode + ","
+ "keyvaluebackup_deferred=" + mDeferKeyValueBackup + ","
+ "launch_boost_disabled=" + mDisableLaunchBoost + ","
+ "optional_sensors_disabled=" + mDisableOptionalSensors + ","
@@ -260,6 +265,11 @@
return mEnableFirewall;
}
+ /** Whether or not to enable night mode while in Battery Saver. */
+ public boolean getEnableNightMode() {
+ return mEnableNightMode;
+ }
+
/** Whether or not to enable Quick Doze while in Battery Saver. */
public boolean getEnableQuickDoze() {
return mEnableQuickDoze;
@@ -275,9 +285,9 @@
return mForceBackgroundCheck;
}
- /** The GPS mode while in Battery Saver. */
- public int getGpsMode() {
- return mGpsMode;
+ /** The location mode while in Battery Saver. */
+ public int getLocationMode() {
+ return mLocationMode;
}
/** Builder class for constructing {@link BatterySaverPolicyConfig} objects. */
@@ -297,10 +307,11 @@
private boolean mEnableAdjustBrightness = false;
private boolean mEnableDataSaver = false;
private boolean mEnableFirewall = false;
+ private boolean mEnableNightMode = false;
private boolean mEnableQuickDoze = false;
private boolean mForceAllAppsStandby = false;
private boolean mForceBackgroundCheck = false;
- private int mGpsMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+ private int mLocationMode = PowerManager.LOCATION_MODE_NO_CHANGE;
public Builder() {
}
@@ -424,6 +435,13 @@
return this;
}
+ /** Set whether or not to enable night mode while in Battery Saver. */
+ @NonNull
+ public Builder setEnableNightMode(boolean enableNightMode) {
+ mEnableNightMode = enableNightMode;
+ return this;
+ }
+
/** Set whether or not to enable Quick Doze while in Battery Saver. */
@NonNull
public Builder setEnableQuickDoze(boolean enableQuickDoze) {
@@ -445,10 +463,10 @@
return this;
}
- /** Set the GPS mode while in Battery Saver. */
+ /** Set the location mode while in Battery Saver. */
@NonNull
- public Builder setGpsMode(@PowerManager.LocationPowerSaveMode int gpsMode) {
- mGpsMode = gpsMode;
+ public Builder setLocationMode(@PowerManager.LocationPowerSaveMode int locationMode) {
+ mLocationMode = locationMode;
return this;
}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 093897a..bdef575 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -42,7 +42,7 @@
boolean isWakeLockLevelSupported(int level);
void userActivity(long time, int event, int flags);
- void wakeUp(long time, String reason, String opPackageName);
+ void wakeUp(long time, int reason, String details, String opPackageName);
void goToSleep(long time, int reason, int flags);
void nap(long time);
boolean isInteractive();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index be673ad..2ecf9d1 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -435,6 +435,106 @@
public static final int GO_TO_SLEEP_FLAG_NO_DOZE = 1 << 0;
/**
+ * @hide
+ */
+ @IntDef(prefix = { "WAKE_REASON_" }, value = {
+ WAKE_REASON_UNKNOWN,
+ WAKE_REASON_POWER_BUTTON,
+ WAKE_REASON_APPLICATION,
+ WAKE_REASON_PLUGGED_IN,
+ WAKE_REASON_GESTURE,
+ WAKE_REASON_CAMERA_LAUNCH,
+ WAKE_REASON_WAKE_KEY,
+ WAKE_REASON_WAKE_MOTION,
+ WAKE_REASON_HDMI,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WakeReason{}
+
+ /**
+ * Wake up reason code: Waking for an unknown reason.
+ * @hide
+ */
+ public static final int WAKE_REASON_UNKNOWN = 0;
+
+ /**
+ * Wake up reason code: Waking up due to power button press.
+ * @hide
+ */
+ public static final int WAKE_REASON_POWER_BUTTON = 1;
+
+ /**
+ * Wake up reason code: Waking up because an application requested it.
+ * @hide
+ */
+ public static final int WAKE_REASON_APPLICATION = 2;
+
+ /**
+ * Wake up reason code: Waking up due to being plugged in or docked on a wireless charger.
+ * @hide
+ */
+ public static final int WAKE_REASON_PLUGGED_IN = 3;
+
+ /**
+ * Wake up reason code: Waking up due to a user performed gesture (e.g. douple tapping on the
+ * screen).
+ * @hide
+ */
+ public static final int WAKE_REASON_GESTURE = 4;
+
+ /**
+ * Wake up reason code: Waking up due to the camera being launched.
+ * @hide
+ */
+ public static final int WAKE_REASON_CAMERA_LAUNCH = 5;
+
+ /**
+ * Wake up reason code: Waking up because a wake key other than power was pressed.
+ * @hide
+ */
+ public static final int WAKE_REASON_WAKE_KEY = 6;
+
+ /**
+ * Wake up reason code: Waking up because a wake motion was performed.
+ *
+ * For example, a trackball that was set to wake the device up was spun.
+ * @hide
+ */
+ public static final int WAKE_REASON_WAKE_MOTION = 7;
+
+ /**
+ * Wake up reason code: Waking due to HDMI.
+ * @hide
+ */
+ public static final int WAKE_REASON_HDMI = 8;
+
+ /**
+ * Wake up reason code: Waking due to the lid being opened.
+ * @hide
+ */
+ public static final int WAKE_REASON_LID = 9;
+
+ /**
+ * Convert the wake reason to a string for debugging purposes.
+ * @hide
+ */
+ public static String wakeReasonToString(@WakeReason int wakeReason) {
+ switch (wakeReason) {
+ case WAKE_REASON_UNKNOWN: return "WAKE_REASON_UNKNOWN";
+ case WAKE_REASON_POWER_BUTTON: return "WAKE_REASON_POWER_BUTTON";
+ case WAKE_REASON_APPLICATION: return "WAKE_REASON_APPLICATION";
+ case WAKE_REASON_PLUGGED_IN: return "WAKE_REASON_PLUGGED_IN";
+ case WAKE_REASON_GESTURE: return "WAKE_REASON_GESTURE";
+ case WAKE_REASON_CAMERA_LAUNCH: return "WAKE_REASON_CAMERA_LAUNCH";
+ case WAKE_REASON_WAKE_KEY: return "WAKE_REASON_WAKE_KEY";
+ case WAKE_REASON_WAKE_MOTION: return "WAKE_REASON_WAKE_MOTION";
+ case WAKE_REASON_HDMI: return "WAKE_REASON_HDMI";
+ case WAKE_REASON_LID: return "WAKE_REASON_LID";
+ default: return Integer.toString(wakeReason);
+ }
+ }
+
+ /**
* The value to pass as the 'reason' argument to reboot() to reboot into
* recovery mode for tasks other than applying system updates, such as
* doing factory resets.
@@ -975,22 +1075,68 @@
* @see #userActivity
* @see #goToSleep
*
+ * @deprecated Use {@link #wakeUp(long, int, String)} instead.
* @removed Requires signature permission.
*/
+ @Deprecated
public void wakeUp(long time) {
- try {
- mService.wakeUp(time, "wakeUp", mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ wakeUp(time, WAKE_REASON_UNKNOWN, "wakeUp");
}
/**
+ * Forces the device to wake up from sleep.
+ * <p>
+ * If the device is currently asleep, wakes it up, otherwise does nothing.
+ * This is what happens when the power key is pressed to turn on the screen.
+ * </p><p>
+ * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+ * </p>
+ *
+ * @param time The time when the request to wake up was issued, in the
+ * {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly
+ * order the wake up request with other power management functions. It should be set
+ * to the timestamp of the input event that caused the request to wake up.
+ *
+ * @param details A free form string to explain the specific details behind the wake up for
+ * debugging purposes.
+ *
+ * @see #userActivity
+ * @see #goToSleep
+ *
+ * @deprecated Use {@link #wakeUp(long, int, String)} instead.
* @hide
*/
- public void wakeUp(long time, String reason) {
+ @Deprecated
+ public void wakeUp(long time, String details) {
+ wakeUp(time, WAKE_REASON_UNKNOWN, details);
+ }
+
+ /**
+ * Forces the device to wake up from sleep.
+ * <p>
+ * If the device is currently asleep, wakes it up, otherwise does nothing.
+ * This is what happens when the power key is pressed to turn on the screen.
+ * </p><p>
+ * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+ * </p>
+ *
+ * @param time The time when the request to wake up was issued, in the
+ * {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly
+ * order the wake up request with other power management functions. It should be set
+ * to the timestamp of the input event that caused the request to wake up.
+ *
+ * @param reason The reason for the wake up.
+ *
+ * @param details A free form string to explain the specific details behind the wake up for
+ * debugging purposes.
+ *
+ * @see #userActivity
+ * @see #goToSleep
+ * @hide
+ */
+ public void wakeUp(long time, @WakeReason int reason, String details) {
try {
- mService.wakeUp(time, reason, mContext.getOpPackageName());
+ mService.wakeUp(time, reason, details, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index de54a8aa..f63c0adb 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -772,19 +772,6 @@
* @param callBlockReason The reason why the call is blocked.
* @param callScreeningAppName The call screening application name which block the call.
* @param callScreeningComponentName The call screening component name which block the call.
- * @param callIdPackageName The package name of the
- * {@link android.telecom.CallScreeningService} which provided
- * {@link CallIdentification}.
- * @param callIdAppName The app name of the {@link android.telecom.CallScreeningService}
- * which provided {@link CallIdentification}.
- * @param callIdName The caller name provided by the
- * {@link android.telecom.CallScreeningService}.
- * @param callIdDescription The caller description provided by the
- * {@link android.telecom.CallScreeningService}.
- * @param callIdDetails The caller details provided by the
- * {@link android.telecom.CallScreeningService}.
- * @param callIdCallType The caller type provided by the
- * {@link android.telecom.CallScreeningService}.
*
* @result The URI of the call log entry belonging to the user that made or received this
* call. This could be of the shadow provider. Do not return it to non-system apps,
@@ -803,37 +790,10 @@
number, userToBeInsertedTo, addForAllUsers));
}
final ContentResolver resolver = context.getContentResolver();
- int numberPresentation = PRESENTATION_ALLOWED;
- TelecomManager tm = null;
- try {
- tm = TelecomManager.from(context);
- } catch (UnsupportedOperationException e) {}
+ String accountAddress = getLogAccountAddress(context, accountHandle);
- String accountAddress = null;
- if (tm != null && accountHandle != null) {
- PhoneAccount account = tm.getPhoneAccount(accountHandle);
- if (account != null) {
- Uri address = account.getSubscriptionAddress();
- if (address != null) {
- accountAddress = address.getSchemeSpecificPart();
- }
- }
- }
-
- // Remap network specified number presentation types
- // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
- // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
- // from any future radio changes.
- // If the number field is empty set the presentation type to Unknown.
- if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
- numberPresentation = PRESENTATION_RESTRICTED;
- } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
- numberPresentation = PRESENTATION_PAYPHONE;
- } else if (TextUtils.isEmpty(number)
- || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
- numberPresentation = PRESENTATION_UNKNOWN;
- }
+ int numberPresentation = getLogNumberPresentation(number, presentation);
if (numberPresentation != PRESENTATION_ALLOWED) {
number = "";
if (ci != null) {
@@ -1138,8 +1098,7 @@
if (TextUtils.isEmpty(countryIso)) {
return;
}
- final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number,
- getCurrentCountryIso(context));
+ final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
if (TextUtils.isEmpty(normalizedNumber)) {
return;
}
@@ -1148,6 +1107,54 @@
resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId});
}
+ /**
+ * Remap network specified number presentation types
+ * PhoneConstants.PRESENTATION_xxx to calllog number presentation types
+ * Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
+ * from any future radio changes.
+ * If the number field is empty set the presentation type to Unknown.
+ */
+ private static int getLogNumberPresentation(String number, int presentation) {
+ if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
+ return presentation;
+ }
+
+ if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
+ return presentation;
+ }
+
+ if (TextUtils.isEmpty(number)
+ || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
+ return PRESENTATION_UNKNOWN;
+ }
+
+ return PRESENTATION_ALLOWED;
+ }
+
+ private static String getLogAccountAddress(Context context,
+ PhoneAccountHandle accountHandle) {
+ TelecomManager tm = null;
+ try {
+ tm = TelecomManager.from(context);
+ } catch (UnsupportedOperationException e) {
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "No TelecomManager found to get account address.");
+ }
+ }
+
+ String accountAddress = null;
+ if (tm != null && accountHandle != null) {
+ PhoneAccount account = tm.getPhoneAccount(accountHandle);
+ if (account != null) {
+ Uri address = account.getSubscriptionAddress();
+ if (address != null) {
+ accountAddress = address.getSchemeSpecificPart();
+ }
+ }
+ }
+ return accountAddress;
+ }
+
private static String getCurrentCountryIso(Context context) {
String countryIso = null;
final CountryDetector detector = (CountryDetector) context.getSystemService(
diff --git a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
index 4e98201..40852ea 100644
--- a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
+++ b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
@@ -23,13 +23,13 @@
*/
oneway interface IRoleControllerService {
- void onAddRoleHolder(in String roleName, in String packageName,
+ void onAddRoleHolder(in String roleName, in String packageName, int flags,
in IRoleManagerCallback callback);
- void onRemoveRoleHolder(in String roleName, in String packageName,
+ void onRemoveRoleHolder(in String roleName, in String packageName, int flags,
in IRoleManagerCallback callback);
- void onClearRoleHolders(in String roleName, in IRoleManagerCallback callback);
+ void onClearRoleHolders(in String roleName, int flags, in IRoleManagerCallback callback);
void onGrantDefaultRoles(in IRoleManagerCallback callback);
diff --git a/core/java/android/rolecontrollerservice/RoleControllerService.java b/core/java/android/rolecontrollerservice/RoleControllerService.java
index 5403cfa..c846b07 100644
--- a/core/java/android/rolecontrollerservice/RoleControllerService.java
+++ b/core/java/android/rolecontrollerservice/RoleControllerService.java
@@ -61,32 +61,33 @@
return new IRoleControllerService.Stub() {
@Override
- public void onAddRoleHolder(String roleName, String packageName,
+ public void onAddRoleHolder(String roleName, String packageName, int flags,
IRoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName,
"packageName cannot be null or empty");
Preconditions.checkNotNull(callback, "callback cannot be null");
- RoleControllerService.this.onAddRoleHolder(roleName, packageName,
+ RoleControllerService.this.onAddRoleHolder(roleName, packageName, flags,
new RoleManagerCallbackDelegate(callback));
}
@Override
- public void onRemoveRoleHolder(String roleName, String packageName,
+ public void onRemoveRoleHolder(String roleName, String packageName, int flags,
IRoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName,
"packageName cannot be null or empty");
Preconditions.checkNotNull(callback, "callback cannot be null");
- RoleControllerService.this.onRemoveRoleHolder(roleName, packageName,
+ RoleControllerService.this.onRemoveRoleHolder(roleName, packageName, flags,
new RoleManagerCallbackDelegate(callback));
}
@Override
- public void onClearRoleHolders(String roleName, IRoleManagerCallback callback) {
+ public void onClearRoleHolders(String roleName, int flags,
+ IRoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkNotNull(callback, "callback cannot be null");
- RoleControllerService.this.onClearRoleHolders(roleName,
+ RoleControllerService.this.onClearRoleHolders(roleName, flags,
new RoleManagerCallbackDelegate(callback));
}
@@ -113,37 +114,41 @@
*
* @param roleName the name of the role to add the role holder for
* @param packageName the package name of the application to add to the role holders
+ * @param flags optional behavior flags
* @param callback the callback for whether this call is successful
*
- * @see RoleManager#addRoleHolderAsUser(String, String, UserHandle, Executor,
+ * @see RoleManager#addRoleHolderAsUser(String, String, int, UserHandle, Executor,
* RoleManagerCallback)
*/
public abstract void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @NonNull RoleManagerCallback callback);
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RoleManagerCallback callback);
/**
* Remove a specific application from the holders of a role.
*
* @param roleName the name of the role to remove the role holder for
* @param packageName the package name of the application to remove from the role holders
+ * @param flags optional behavior flags
* @param callback the callback for whether this call is successful
*
- * @see RoleManager#removeRoleHolderAsUser(String, String, UserHandle, Executor,
+ * @see RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, Executor,
* RoleManagerCallback)
*/
public abstract void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @NonNull RoleManagerCallback callback);
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RoleManagerCallback callback);
/**
* Remove all holders of a role.
*
* @param roleName the name of the role to remove role holders for
+ * @param flags optional behavior flags
* @param callback the callback for whether this call is successful
*
- * @see RoleManager#clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
+ * @see RoleManager#clearRoleHoldersAsUser(String, int, UserHandle, Executor,
+ * RoleManagerCallback)
*/
public abstract void onClearRoleHolders(@NonNull String roleName,
- @NonNull RoleManagerCallback callback);
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RoleManagerCallback callback);
/**
* Cleanup appop/permissions state in response to sms kill switch toggle
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 8e0f522..81d066d 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -234,7 +234,12 @@
}
/**
- * Implementation specific {@code dump}.
+ * Implementation specific {@code dump}. The child class can override the method to provide
+ * additional information about the Service's state into the dumpsys output.
+ *
+ * @param pw The PrintWriter to which you should dump your state. This will be closed for
+ * you after you return.
+ * @param args additional arguments to the dump request.
*/
protected void dump(@NonNull PrintWriter pw,
@SuppressWarnings("unused") @NonNull String[] args) {
diff --git a/core/java/android/service/dreams/Sandman.java b/core/java/android/service/dreams/Sandman.java
index eeb340b..efb8923 100644
--- a/core/java/android/service/dreams/Sandman.java
+++ b/core/java/android/service/dreams/Sandman.java
@@ -91,8 +91,9 @@
// and the UI mode manager starting a dream. We want the system to already
// be awake by the time this happens. Otherwise the dream may not start.
PowerManager powerManager =
- (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ context.getSystemService(PowerManager.class);
powerManager.wakeUp(SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_PLUGGED_IN,
"android.service.dreams:DREAM");
} else {
Slog.i(TAG, "Activating dream by user request.");
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 949328f..915a18e 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -94,7 +94,8 @@
private final DecorationInfo mDecorationInfo = new DecorationInfo();
private final ArrayList<DecorationInfo> mDecorations = new ArrayList<>();
- @UnsupportedAppUsage
+ /** Not allowed to access. If it's for memory leak workaround, it was already fixed M. */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static final TextLine[] sCached = new TextLine[3];
/**
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 59e562f..62ed901 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -40,6 +40,7 @@
import android.view.View.AttachInfo;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeIdManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -154,13 +155,7 @@
}
private boolean isShown(View view) {
- // The first two checks are made also made by isShown() which
- // however traverses the tree up to the parent to catch that.
- // Therefore, we do some fail fast check to minimize the up
- // tree traversal.
- return (view.mAttachInfo != null
- && view.mAttachInfo.mWindowVisibility == View.VISIBLE
- && view.isShown());
+ return (view != null) && (view.getWindowVisibility() == View.VISIBLE && view.isShown());
}
public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
@@ -340,13 +335,8 @@
return;
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
- View root = null;
- if (accessibilityViewId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
- root = mViewRootImpl.mView;
- } else {
- root = findViewByAccessibilityId(accessibilityViewId);
- }
- if (root != null && isShown(root)) {
+ final View root = findViewByAccessibilityId(accessibilityViewId);
+ if (root != null) {
mPrefetcher.prefetchAccessibilityNodeInfos(
root, virtualDescendantId, flags, infos, arguments);
}
@@ -396,12 +386,7 @@
return;
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
- View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
- root = findViewByAccessibilityId(accessibilityViewId);
- } else {
- root = mViewRootImpl.mView;
- }
+ final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null) {
final int resolvedViewId = root.getContext().getResources()
.getIdentifier(viewId, null, null);
@@ -462,13 +447,8 @@
return;
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
- View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
- root = findViewByAccessibilityId(accessibilityViewId);
- } else {
- root = mViewRootImpl.mView;
- }
- if (root != null && isShown(root)) {
+ final View root = findViewByAccessibilityId(accessibilityViewId);
+ if (root != null) {
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
if (provider != null) {
infos = provider.findAccessibilityNodeInfosByText(text,
@@ -550,13 +530,8 @@
return;
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
- View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
- root = findViewByAccessibilityId(accessibilityViewId);
- } else {
- root = mViewRootImpl.mView;
- }
- if (root != null && isShown(root)) {
+ final View root = findViewByAccessibilityId(accessibilityViewId);
+ if (root != null) {
switch (focusType) {
case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
View host = mViewRootImpl.mAccessibilityFocusedHost;
@@ -583,7 +558,7 @@
} break;
case AccessibilityNodeInfo.FOCUS_INPUT: {
View target = root.findFocus();
- if (target == null || !isShown(target)) {
+ if (!isShown(target)) {
break;
}
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
@@ -645,13 +620,8 @@
return;
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
- View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
- root = findViewByAccessibilityId(accessibilityViewId);
- } else {
- root = mViewRootImpl.mView;
- }
- if (root != null && isShown(root)) {
+ final View root = findViewByAccessibilityId(accessibilityViewId);
+ if (root != null) {
View nextView = root.focusSearch(direction);
if (nextView != null) {
next = nextView.createAccessibilityNodeInfo();
@@ -705,13 +675,8 @@
return;
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
- View target = null;
- if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
- target = findViewByAccessibilityId(accessibilityViewId);
- } else {
- target = mViewRootImpl.mView;
- }
- if (target != null && isShown(target)) {
+ final View target = findViewByAccessibilityId(accessibilityViewId);
+ if (target != null) {
if (action == R.id.accessibilityActionClickOnClickableSpan) {
// Handle this hidden action separately
succeeded = handleClickableSpanActionUiThread(
@@ -791,15 +756,13 @@
}
private View findViewByAccessibilityId(int accessibilityId) {
- View root = mViewRootImpl.mView;
- if (root == null) {
- return null;
+ if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
+ return mViewRootImpl.mView;
+ } else {
+ final View foundView =
+ AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
+ return isShown(foundView) ? foundView : null;
}
- View foundView = root.findViewByAccessibilityId(accessibilityId);
- if (foundView != null && !isShown(foundView)) {
- return null;
- }
- return foundView;
}
private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
@@ -1171,7 +1134,7 @@
}
View child = children.get(i);
if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
- && isShown(child)) {
+ && isShown(child)) {
AccessibilityNodeInfo info = null;
AccessibilityNodeProvider provider =
child.getAccessibilityNodeProvider();
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index ccd0fc1..03e8a0f 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -22,7 +22,6 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.graphics.FrameInfo;
-import android.graphics.Insets;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Build;
import android.os.Handler;
@@ -914,25 +913,11 @@
super(looper, vsyncSource);
}
+ // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
+ // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
+ // for the internal display implicitly.
@Override
- public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
- // Ignore vsync from secondary display.
- // This can be problematic because the call to scheduleVsync() is a one-shot.
- // We need to ensure that we will still receive the vsync from the primary
- // display which is the one we really care about. Ideally we should schedule
- // vsync for a particular display.
- // At this time Surface Flinger won't send us vsyncs for secondary displays
- // but that could change in the future so let's log a message to help us remember
- // that we need to fix this.
- if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
- Log.d(TAG, "Received vsync from secondary display, but we don't support "
- + "this case yet. Choreographer needs a way to explicitly request "
- + "vsync for a specific display to ensure it doesn't lose track "
- + "of its scheduled vsync.");
- scheduleVsync();
- return;
- }
-
+ public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index edd3f1a..3e8002f 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -136,12 +136,11 @@
*
* @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()}
* timebase.
- * @param builtInDisplayId The surface flinger built-in display id such as
- * {@link SurfaceControl#BUILT_IN_DISPLAY_ID_MAIN}.
+ * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
* @param frame The frame number. Increases by one for each vertical sync interval.
*/
@UnsupportedAppUsage
- public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
+ public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
}
/**
@@ -149,12 +148,11 @@
*
* @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
* timebase.
- * @param builtInDisplayId The surface flinger built-in display id such as
- * {@link SurfaceControl#BUILT_IN_DISPLAY_ID_HDMI}.
+ * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
* @param connected True if the display is connected, false if it disconnected.
*/
@UnsupportedAppUsage
- public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
+ public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
}
/**
@@ -174,14 +172,14 @@
// Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage
- private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
- onVsync(timestampNanos, builtInDisplayId, frame);
+ private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
+ onVsync(timestampNanos, physicalDisplayId, frame);
}
// Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage
- private void dispatchHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
- onHotplug(timestampNanos, builtInDisplayId, connected);
+ private void dispatchHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
+ onHotplug(timestampNanos, physicalDisplayId, connected);
}
}
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 78ad0da..1dbc46b 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -24,6 +24,7 @@
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
+import android.os.Build;
import android.util.SparseIntArray;
import com.android.internal.util.VirtualRefBasePtr;
@@ -282,7 +283,7 @@
throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
public void setTarget(View view) {
mViewTarget = view;
setTarget(mViewTarget.mRenderNode);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 8061cc3..c0a4028 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -44,6 +44,7 @@
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
+import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -131,7 +132,8 @@
private static native boolean nativeClearAnimationFrameStats();
private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats);
- private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId);
+ private static native long[] nativeGetPhysicalDisplayIds();
+ private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
private static native IBinder nativeCreateDisplay(String name, boolean secure);
private static native void nativeDestroyDisplay(IBinder displayToken);
private static native void nativeSetDisplaySurface(long transactionObj,
@@ -329,24 +331,6 @@
*/
private static final int SURFACE_OPAQUE = 0x02;
-
- /* built-in physical display ids (keep in sync with ISurfaceComposer.h)
- * these are different from the logical display ids used elsewhere in the framework */
-
- /**
- * Built-in physical display id: Main display.
- * Use only with {@link SurfaceControl#getBuiltInDisplay(int)}.
- * @hide
- */
- public static final int BUILT_IN_DISPLAY_ID_MAIN = 0;
-
- /**
- * Built-in physical display id: Attached HDMI display.
- * Use only with {@link SurfaceControl#getBuiltInDisplay(int)}.
- * @hide
- */
- public static final int BUILT_IN_DISPLAY_ID_HDMI = 1;
-
// Display power modes.
/**
* Display power mode off: used while blanking the screen.
@@ -1729,9 +1713,28 @@
/**
* @hide
*/
- @UnsupportedAppUsage
- public static IBinder getBuiltInDisplay(int builtInDisplayId) {
- return nativeGetBuiltInDisplay(builtInDisplayId);
+ public static long[] getPhysicalDisplayIds() {
+ return nativeGetPhysicalDisplayIds();
+ }
+
+ /**
+ * @hide
+ */
+ public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
+ return nativeGetPhysicalDisplayToken(physicalDisplayId);
+ }
+
+ /**
+ * TODO(116025192): Remove this stopgap once framework is display-agnostic.
+ *
+ * @hide
+ */
+ public static IBinder getInternalDisplayToken() {
+ final long[] physicalDisplayIds = getPhysicalDisplayIds();
+ if (physicalDisplayIds.length == 0) {
+ return null;
+ }
+ return getPhysicalDisplayToken(physicalDisplayIds[0]);
}
/**
@@ -1790,8 +1793,12 @@
public static Bitmap screenshot(Rect sourceCrop, int width, int height,
boolean useIdentityTransform, int rotation) {
// TODO: should take the display as a parameter
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(
- SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
+ final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+ if (displayToken == null) {
+ Log.w(TAG, "Failed to take screenshot because internal display is disconnected");
+ return null;
+ }
+
if (rotation == ROTATION_90 || rotation == ROTATION_270) {
rotation = (rotation == ROTATION_90) ? ROTATION_270 : ROTATION_90;
}
@@ -2194,7 +2201,7 @@
/**
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
sc.checkNotReleased();
nativeSetLayerStack(mNativeObject, sc.mNativeObject, layerStack);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e56861e..c641552 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -105,6 +105,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEventSource;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeIdManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -19181,6 +19182,7 @@
jumpDrawablesToCurrentState();
+ AccessibilityNodeIdManager.getInstance().registerViewWithId(this, getAccessibilityViewId());
resetSubtreeAccessibilityStateChanged();
// rebuild, since Outline not maintained while View is detached
@@ -19573,6 +19575,8 @@
if ((mViewFlags & TOOLTIP) == TOOLTIP) {
hideTooltip();
}
+
+ AccessibilityNodeIdManager.getInstance().unregisterViewWithId(getAccessibilityViewId());
}
private void cleanupDraw() {
@@ -24087,24 +24091,6 @@
}
/**
- * Finds a view by its unuque and stable accessibility id.
- *
- * @param accessibilityId The searched accessibility id.
- * @return The found view.
- */
- @UnsupportedAppUsage
- final <T extends View> T findViewByAccessibilityId(int accessibilityId) {
- if (accessibilityId < 0) {
- return null;
- }
- T view = findViewByAccessibilityIdTraversal(accessibilityId);
- if (view != null) {
- return view.includeForAccessibility() ? view : null;
- }
- return null;
- }
-
- /**
* Performs the traversal to find a view by its unique and stable accessibility id.
*
* <strong>Note:</strong>This method does not stop at the root namespace
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 47528a0..67cca56 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -95,6 +95,7 @@
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import android.view.accessibility.AccessibilityManager.HighTextContrastChangeListener;
+import android.view.accessibility.AccessibilityNodeIdManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -7954,17 +7955,14 @@
// Intercept accessibility focus events fired by virtual nodes to keep
// track of accessibility focus position in such nodes.
final int eventType = event.getEventType();
+ final View source = getSourceForAccessibilityEvent(event);
switch (eventType) {
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
- final long sourceNodeId = event.getSourceNodeId();
- final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
- sourceNodeId);
- View source = mView.findViewByAccessibilityId(accessibilityViewId);
if (source != null) {
AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
if (provider != null) {
final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
- sourceNodeId);
+ event.getSourceNodeId());
final AccessibilityNodeInfo node;
node = provider.createAccessibilityNodeInfo(virtualNodeId);
setAccessibilityFocus(source, node);
@@ -7972,15 +7970,8 @@
}
} break;
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
- final long sourceNodeId = event.getSourceNodeId();
- final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
- sourceNodeId);
- View source = mView.findViewByAccessibilityId(accessibilityViewId);
- if (source != null) {
- AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
- if (provider != null) {
- setAccessibilityFocus(null, null);
- }
+ if (source != null && source.getAccessibilityNodeProvider() != null) {
+ setAccessibilityFocus(null, null);
}
} break;
@@ -7993,6 +7984,13 @@
return true;
}
+ private View getSourceForAccessibilityEvent(AccessibilityEvent event) {
+ final long sourceNodeId = event.getSourceNodeId();
+ final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
+ sourceNodeId);
+ return AccessibilityNodeIdManager.getInstance().findView(accessibilityViewId);
+ }
+
/**
* Updates the focused virtual view, when necessary, in response to a
* content changed event.
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 16bafe2..35ed7bf 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -16,6 +16,11 @@
package android.view;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Constants for interfacing with WindowManagerService and WindowManagerPolicyInternal.
* @hide
@@ -89,6 +94,35 @@
/** Screen turned off because of timeout */
int OFF_BECAUSE_OF_TIMEOUT = 3;
+ @IntDef(prefix = { "ON_BECAUSE_OF_" }, value = {
+ ON_BECAUSE_OF_USER,
+ ON_BECAUSE_OF_APPLICATION,
+ ON_BECAUSE_OF_UNKNOWN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OnReason{}
+
+ /** Convert the on reason to a human readable format */
+ static String onReasonToString(@OnReason int why) {
+ switch (why) {
+ case ON_BECAUSE_OF_USER:
+ return "ON_BECAUSE_OF_USER";
+ case ON_BECAUSE_OF_APPLICATION:
+ return "ON_BECAUSE_OF_APPLICATION";
+ case ON_BECAUSE_OF_UNKNOWN:
+ return "ON_BECAUSE_OF_UNKNOWN";
+ default:
+ return Integer.toString(why);
+ }
+ }
+
+ /** Screen turned on because of a user-initiated action. */
+ int ON_BECAUSE_OF_USER = 1;
+ /** Screen turned on because of an application request or event */
+ int ON_BECAUSE_OF_APPLICATION = 2;
+ /** Screen turned on for an unknown reason */
+ int ON_BECAUSE_OF_UNKNOWN = 3;
+
int APPLICATION_LAYER = 2;
int APPLICATION_MEDIA_SUBLAYER = -2;
int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 6aafa34..06207a9 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -36,6 +36,7 @@
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -183,7 +184,7 @@
boolean mIsTouchExplorationEnabled;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768939)
boolean mIsHighTextContrastEnabled;
AccessibilityPolicy mAccessibilityPolicy;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeIdManager.java b/core/java/android/view/accessibility/AccessibilityNodeIdManager.java
new file mode 100644
index 0000000..1ac7047
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityNodeIdManager.java
@@ -0,0 +1,68 @@
+/*
+ * 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.accessibility;
+
+import android.util.SparseArray;
+import android.view.View;
+
+/** @hide */
+public final class AccessibilityNodeIdManager {
+ private SparseArray<View> mIdsToViews = new SparseArray<>();
+ private static AccessibilityNodeIdManager sIdManager;
+
+ /**
+ * Gets singleton.
+ * @return The instance.
+ */
+ public static synchronized AccessibilityNodeIdManager getInstance() {
+ if (sIdManager == null) {
+ sIdManager = new AccessibilityNodeIdManager();
+ }
+ return sIdManager;
+ }
+
+ private AccessibilityNodeIdManager() {
+ }
+
+ /**
+ * Register view to be kept track of by the accessibility system.
+ * Must be paired with unregisterView, otherwise this will leak.
+ * @param view The view to be registered.
+ * @param id The accessibilityViewId of the view.
+ */
+ public void registerViewWithId(View view, int id) {
+ mIdsToViews.append(id, view);
+ }
+
+ /**
+ * Unregister view, accessibility won't keep track of this view after this call.
+ * @param id The id returned from registerView when the view as first associated.
+ */
+ public void unregisterViewWithId(int id) {
+ mIdsToViews.remove(id);
+ }
+
+ /**
+ * Accessibility uses this to find the view in the hierarchy.
+ * @param id The accessibility view id.
+ * @return The view.
+ */
+ public View findView(int id) {
+ final View view = mIdsToViews.get(id);
+ return view != null && view.includeForAccessibility() ? view : null;
+ }
+}
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index dfac35d..22254cd 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -138,8 +138,11 @@
/**
* Adds an autofill id to the this event, merging the single id into a list if necessary.
- * @hide */
+ *
+ * @hide
+ */
public ContentCaptureEvent addAutofillId(@NonNull AutofillId id) {
+ Preconditions.checkNotNull(id);
if (mIds == null) {
mIds = new ArrayList<>();
if (mId == null) {
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index eef841d..924bb9a 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -600,7 +600,7 @@
/** @hide */
public static @Nullable ViewNode readFromParcel(@NonNull Parcel parcel) {
final long nodeFlags = parcel.readLong();
- return nodeFlags == 0 ? new ViewNode() : new ViewNode(nodeFlags, parcel);
+ return nodeFlags == 0 ? null : new ViewNode(nodeFlags, parcel);
}
/** @hide */
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index aee4b1f..8a09788 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -339,7 +339,11 @@
// For scheduling work on the main thread. This also serves as our
// global lock.
- @UnsupportedAppUsage
+ // Remark on @UnsupportedAppUsage: there were context leaks on old versions
+ // of android (b/37043700), so developers used this field to perform manual clean up.
+ // Leaks were fixed, hacks were backported to AppCompatActivity,
+ // so an access to the field is closed.
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
final H mH;
// Our generic input connection if the current target does not have its own.
@@ -375,13 +379,15 @@
* This is the view that should currently be served by an input method,
* regardless of the state of setting that up.
*/
- @UnsupportedAppUsage
+ // See comment to mH field in regard to @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
View mServedView;
/**
* This is then next view that will be served by the input method, when
* we get around to updating things.
*/
- @UnsupportedAppUsage
+ // See comment to mH field in regard to @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
View mNextServedView;
/**
* This is set when we are in the process of connecting, to determine
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 4dd7d3a..542df45 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -442,8 +442,10 @@
/**
* Handles one frame of a fling
+ *
+ * To interrupt a fling early you should use smoothScrollBy(0,0) instead
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private FlingRunnable mFlingRunnable;
/**
@@ -483,7 +485,7 @@
/**
* Optional callback to notify client when scroll position has changed
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769353)
private OnScrollListener mOnScrollListener;
/**
@@ -1611,15 +1613,6 @@
return false;
}
- /** @hide */
- @Override
- public View findViewByAccessibilityIdTraversal(int accessibilityId) {
- if (accessibilityId == getAccessibilityViewId()) {
- return this;
- }
- return super.findViewByAccessibilityIdTraversal(accessibilityId);
- }
-
/**
* Indicates whether the children's drawing cache is used during a scroll.
* By default, the drawing cache is enabled but this will consume more memory.
@@ -4688,7 +4681,8 @@
mScroller = new OverScroller(getContext());
}
- @UnsupportedAppUsage
+ // Use AbsListView#fling(int) instead
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
void start(int initialVelocity) {
int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0;
mLastFlingY = initialY;
@@ -4766,7 +4760,8 @@
postOnAnimation(this);
}
- @UnsupportedAppUsage
+ // To interrupt a fling early you should use smoothScrollBy(0,0) instead
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
void endFling() {
mTouchMode = TOUCH_MODE_REST;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index bf65ec0..bbbe369 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -123,7 +123,7 @@
private int mColumnWidth;
@UnsupportedAppUsage
private int mRequestedColumnWidth;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769395)
private int mRequestedNumColumns;
private View mReferenceView = null;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index fc4e9ec..24bc9f1 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -111,7 +111,7 @@
* layout is dirty. This prevents the scroll from being wrong if the child has not been
* laid out before requesting focus.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769715)
private View mChildToScrollTo = null;
/**
@@ -1384,16 +1384,20 @@
*
* @param child the View to scroll to
*/
- private void scrollToChild(View child) {
- child.getDrawingRect(mTempRect);
+ public void scrollToDescendant(View child) {
+ if (!mIsLayoutDirty) {
+ child.getDrawingRect(mTempRect);
- /* Offset from child's local coordinates to ScrollView coordinates */
- offsetDescendantRectToMyCoords(child, mTempRect);
+ /* Offset from child's local coordinates to ScrollView coordinates */
+ offsetDescendantRectToMyCoords(child, mTempRect);
- int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
+ int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
- if (scrollDelta != 0) {
- scrollBy(0, scrollDelta);
+ if (scrollDelta != 0) {
+ scrollBy(0, scrollDelta);
+ }
+ } else {
+ mChildToScrollTo = child;
}
}
@@ -1488,7 +1492,7 @@
public void requestChildFocus(View child, View focused) {
if (focused != null && focused.getRevealOnFocusHint()) {
if (!mIsLayoutDirty) {
- scrollToChild(focused);
+ scrollToDescendant(focused);
} else {
// The child may not be laid out yet, we can't compute the scroll yet
mChildToScrollTo = focused;
@@ -1569,7 +1573,7 @@
mIsLayoutDirty = false;
// Give a child focus if it needs it
if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
- scrollToChild(mChildToScrollTo);
+ scrollToDescendant(mChildToScrollTo);
}
mChildToScrollTo = null;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3da450d..d876001 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -712,7 +712,7 @@
@UnsupportedAppUsage
private ChangeWatcher mChangeWatcher;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(trackingBug = 123769451)
private ArrayList<TextWatcher> mListeners;
// display attributes
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 0879af3..119a015 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -54,6 +54,7 @@
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -186,9 +187,12 @@
private @interface ContentPreviewType {
}
- private static final int CONTENT_PREVIEW_IMAGE = 0;
- private static final int CONTENT_PREVIEW_FILE = 1;
- private static final int CONTENT_PREVIEW_TEXT = 2;
+ // Starting at 1 since 0 is considered "undefined" for some of the database transformations
+ // of tron logs.
+ private static final int CONTENT_PREVIEW_IMAGE = 1;
+ private static final int CONTENT_PREVIEW_FILE = 2;
+ private static final int CONTENT_PREVIEW_TEXT = 3;
+ protected MetricsLogger mMetricsLogger;
private final Handler mChooserHandler = new Handler() {
@Override
@@ -413,11 +417,12 @@
}
});
- MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN);
-
mChooserShownTime = System.currentTimeMillis();
final long systemCost = mChooserShownTime - intentReceivedTime;
- MetricsLogger.histogram(null, "system_cost_for_smart_sharing", (int) systemCost);
+
+ getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)
+ .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
+ .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) {
final IntentFilter filter = getTargetIntentFilter();
@@ -470,6 +475,9 @@
}
int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
+
+ getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)
+ .setSubtype(previewType));
displayContentPreview(previewType, targetIntent);
}
@@ -1180,6 +1188,13 @@
}
}
+ protected MetricsLogger getMetricsLogger() {
+ if (mMetricsLogger == null) {
+ mMetricsLogger = new MetricsLogger();
+ }
+ return mMetricsLogger;
+ }
+
public class ChooserListController extends ResolverListController {
public ChooserListController(Context context,
PackageManager pm,
@@ -1726,6 +1741,8 @@
if (show != mShowServiceTargets) {
mShowServiceTargets = show;
notifyDataSetChanged();
+ getMetricsLogger().write(
+ new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN_DIRECT_TARGET));
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index b881aef..8b669d5 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -99,6 +99,11 @@
*/
public static final int PROFILE_FROM_SHELL = 1 << 15;
+ /*
+ * Enable using the ART app image startup cache
+ */
+ public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
+
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
/** Default external storage should be mounted. */
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 9ba56b8..4ac7f50 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -334,9 +334,14 @@
}
}
- private class HiddenApiUsageLogger implements VMRuntime.HiddenApiUsageLogger {
+ private static class HiddenApiUsageLogger implements VMRuntime.HiddenApiUsageLogger {
private final MetricsLogger mMetricsLogger = new MetricsLogger();
+ private static HiddenApiUsageLogger sInstance = new HiddenApiUsageLogger();
+
+ public static HiddenApiUsageLogger getInstance() {
+ return HiddenApiUsageLogger.sInstance;
+ }
public void hiddenApiUsed(String packageName, String signature,
int accessMethod, boolean accessDenied) {
@@ -370,7 +375,7 @@
private void handleHiddenApiAccessLogSampleRate(int samplingRate) {
try {
ZygoteInit.setHiddenApiAccessLogSampleRate(samplingRate);
- ZygoteInit.setHiddenApiUsageLogger(new HiddenApiUsageLogger());
+ ZygoteInit.setHiddenApiUsageLogger(HiddenApiUsageLogger.getInstance());
mSocketOutStream.writeInt(0);
} catch (IOException ioe) {
throw new IllegalStateException("Error writing to command socket", ioe);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9f23797..e132abd 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -41,6 +41,7 @@
import android.system.StructCapUserData;
import android.system.StructCapUserHeader;
import android.text.Hyphenator;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -84,6 +85,8 @@
private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+ private static final String PROPERTY_USE_APP_IMAGE_STARTUP_CACHE =
+ "persist.device_config.runtime_native.use_app_image_startup_cache";
private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
@@ -705,6 +708,13 @@
parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
+ String use_app_image_cache = SystemProperties.get(
+ PROPERTY_USE_APP_IMAGE_STARTUP_CACHE, "");
+ // Property defaults to true currently.
+ if (!TextUtils.isEmpty(use_app_image_cache) && !use_app_image_cache.equals("false")) {
+ parsedArgs.mRuntimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
+ }
+
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.mUid, parsedArgs.mGid,
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index b7e656b..ee8637d8 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -17,15 +17,12 @@
package com.android.internal.widget;
-import com.android.internal.R;
-
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -45,8 +42,13 @@
import android.widget.AbsListView;
import android.widget.OverScroller;
+import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
public class ResolverDrawerLayout extends ViewGroup {
private static final String TAG = "ResolverDrawerLayout";
+ private MetricsLogger mMetricsLogger;
/**
* Max width of the whole drawer layout
@@ -496,6 +498,9 @@
final boolean isCollapsedNew = newPos != 0;
if (isCollapsedOld != isCollapsedNew) {
onCollapsedChanged(isCollapsedNew);
+ getMetricsLogger().write(
+ new LogMaker(MetricsEvent.ACTION_SHARESHEET_COLLAPSED_CHANGED)
+ .setSubtype(isCollapsedNew ? 1 : 0));
}
postInvalidateOnAnimation();
return dy;
@@ -1037,4 +1042,11 @@
dispatchOnDismissed();
}
}
+
+ private MetricsLogger getMetricsLogger() {
+ if (mMetricsLogger == null) {
+ mMetricsLogger = new MetricsLogger();
+ }
+ return mMetricsLogger;
+ }
}
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index c1b5aae..191472d 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -59,8 +59,8 @@
sp<MessageQueue> mMessageQueue;
DisplayEventReceiver mReceiver;
- virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count);
- virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);
+ void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+ void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
};
@@ -84,28 +84,30 @@
DisplayEventDispatcher::dispose();
}
-void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
+void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
+ uint32_t count) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
if (receiverObj.get()) {
ALOGV("receiver %p ~ Invoking vsync handler.", this);
env->CallVoidMethod(receiverObj.get(),
- gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
+ gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count);
ALOGV("receiver %p ~ Returned from vsync handler.", this);
}
mMessageQueue->raiseAndClearException(env, "dispatchVsync");
}
-void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) {
+void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
+ bool connected) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
if (receiverObj.get()) {
ALOGV("receiver %p ~ Invoking hotplug handler.", this);
env->CallVoidMethod(receiverObj.get(),
- gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, id, connected);
+ gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, displayId, connected);
ALOGV("receiver %p ~ Returned from hotplug handler.", this);
}
@@ -175,9 +177,9 @@
gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env,
- gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JII)V");
+ gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
- gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JIZ)V");
+ gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
return res;
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index fad2fe0..68be005 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -483,8 +483,29 @@
transaction->setLayerStack(ctrl, layerStack);
}
-static jobject nativeGetBuiltInDisplay(JNIEnv* env, jclass clazz, jint id) {
- sp<IBinder> token(SurfaceComposerClient::getBuiltInDisplay(id));
+static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
+ const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
+ jlongArray array = env->NewLongArray(displayIds.size());
+ if (array == nullptr) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+ return nullptr;
+ }
+
+ if (displayIds.empty()) {
+ return array;
+ }
+
+ jlong* values = env->GetLongArrayElements(array, 0);
+ for (size_t i = 0; i < displayIds.size(); ++i) {
+ values[i] = static_cast<jlong>(displayIds[i]);
+ }
+
+ env->ReleaseLongArrayElements(array, values, 0);
+ return array;
+}
+
+static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
+ sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(physicalDisplayId);
return javaObjectForIBinder(env, token);
}
@@ -1145,8 +1166,10 @@
(void*)nativeSetCornerRadius },
{"nativeSetLayerStack", "(JJI)V",
(void*)nativeSetLayerStack },
- {"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;",
- (void*)nativeGetBuiltInDisplay },
+ {"nativeGetPhysicalDisplayIds", "()[J",
+ (void*)nativeGetPhysicalDisplayIds },
+ {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
+ (void*)nativeGetPhysicalDisplayToken },
{"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;",
(void*)nativeCreateDisplay },
{"nativeDestroyDisplay", "(Landroid/os/IBinder;)V",
@@ -1314,4 +1337,4 @@
return err;
}
-};
+} // namespace android
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 2c058cd..8c73630 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -1202,12 +1202,12 @@
static JavaVM* mJvm = nullptr;
-static void attachRenderThreadToJvm() {
+static void attachRenderThreadToJvm(const char* name) {
LOG_ALWAYS_FATAL_IF(!mJvm, "No jvm but we set the hook??");
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_4;
- args.name = NULL;
+ args.name = name;
args.group = NULL;
JNIEnv* env;
mJvm->AttachCurrentThreadAsDaemon(&env, (void*) &args);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5a65028..0ef4f87 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -647,6 +647,23 @@
static void PreparePkgSpecificDirs(const std::vector<std::string>& packageNames,
const std::vector<std::string>& volumeLabels,
bool mountAllObbs, userid_t userId, fail_fn_t fail_fn) {
+ if (volumeLabels.size() > 0) {
+ std::string sandboxDataDir = StringPrintf("/storage/%s", volumeLabels[0].c_str());
+ if (volumeLabels[0] == "emulated") {
+ StringAppendF(&sandboxDataDir, "/%d", userId);
+ }
+ StringAppendF(&sandboxDataDir, "/Android/data/%s", packageNames[0].c_str());
+ struct stat sb;
+ if (TEMP_FAILURE_RETRY(lstat(sandboxDataDir.c_str(), &sb)) == -1) {
+ if (errno == ENOENT) {
+ ALOGD("Sandbox not fully prepared for %s", sandboxDataDir.c_str());
+ return;
+ } else {
+ fail_fn(CREATE_ERROR("Failed to lstat %s: %s",
+ sandboxDataDir.c_str(), strerror(errno)));
+ }
+ }
+ }
for (auto& label : volumeLabels) {
std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str());
std::string mntTarget = StringPrintf("/storage/%s", label.c_str());
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60b04cf..07dd26e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -662,8 +662,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readContacts"
android:description="@string/permdesc_readContacts"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write the user's contacts data.
<p>Protection level: dangerous
@@ -694,8 +693,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCalendar"
android:description="@string/permdesc_readCalendar"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write the user's calendar data.
<p>Protection level: dangerous
@@ -736,8 +734,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveSms"
android:description="@string/permdesc_receiveSms"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Allows an application to read SMS messages.
<p>Protection level: dangerous
@@ -746,8 +743,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readSms"
android:description="@string/permdesc_readSms"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Allows an application to receive WAP push messages.
<p>Protection level: dangerous
@@ -756,8 +752,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveWapPush"
android:description="@string/permdesc_receiveWapPush"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Allows an application to monitor incoming MMS messages.
<p>Protection level: dangerous
@@ -766,8 +761,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveMms"
android:description="@string/permdesc_receiveMms"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
messages and to register a content observer to get notifications when
@@ -785,8 +779,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCellBroadcasts"
android:description="@string/permdesc_readCellBroadcasts"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing external storage -->
@@ -867,8 +860,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_audioRead"
android:description="@string/permdesc_audioRead"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Runtime permission controlling access to the user's shared visual media
collection, including images and videos. -->
@@ -884,16 +876,14 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_imagesRead"
android:description="@string/permdesc_imagesRead"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Allows an application to read the user's shared video collection. -->
<permission android:name="android.permission.READ_MEDIA_VIDEO"
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_videoRead"
android:description="@string/permdesc_videoRead"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Allows an application to access any geographic locations persisted in the
user's shared collection. -->
@@ -901,8 +891,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_mediaLocation"
android:description="@string/permdesc_mediaLocation"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- @hide @SystemApi @TestApi
Allows an application to modify OBB files visible to other apps. -->
@@ -934,8 +923,7 @@
android:label="@string/permlab_accessFineLocation"
android:description="@string/permdesc_accessFineLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
- android:protectionLevel="dangerous|instant"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous|instant" />
<!-- Allows an app to access approximate location.
Alternatively, you might want {@link #ACCESS_FINE_LOCATION}.
@@ -946,8 +934,7 @@
android:label="@string/permlab_accessCoarseLocation"
android:description="@string/permdesc_accessCoarseLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
- android:protectionLevel="dangerous|instant"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous|instant" />
<!-- Allows an app to access location in the background. If you
are requesting this, you should also request {@link #ACCESS_FINE_LOCATION}.
@@ -959,8 +946,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessBackgroundLocation"
android:description="@string/permdesc_accessBackgroundLocation"
- android:protectionLevel="dangerous|instant"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous|instant" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the call log -->
@@ -1001,8 +987,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCallLog"
android:description="@string/permdesc_readCallLog"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write (but not read) the user's
call log data.
@@ -1032,8 +1017,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_processOutgoingCalls"
android:description="@string/permdesc_processOutgoingCalls"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device telephony -->
@@ -1065,8 +1049,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneState"
android:description="@string/permdesc_readPhoneState"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
@@ -1075,8 +1058,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneNumbers"
android:description="@string/permdesc_readPhoneNumbers"
- android:protectionLevel="dangerous|instant"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous|instant" />
<!-- Allows an application to initiate a phone call without going through
the Dialer user interface for the user to confirm the call.
@@ -1178,8 +1160,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_recordAudio"
android:description="@string/permdesc_recordAudio"
- android:protectionLevel="dangerous|instant"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous|instant" />
<!-- ====================================================================== -->
<!-- Permissions for activity recognition -->
@@ -1202,8 +1183,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_activityRecognition"
android:description="@string/permdesc_activityRecognition"
- android:protectionLevel="dangerous|instant"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous|instant" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the UCE Service -->
@@ -1252,8 +1232,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_camera"
android:description="@string/permdesc_camera"
- android:protectionLevel="dangerous|instant"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous|instant" />
<!-- ====================================================================== -->
@@ -1277,8 +1256,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_bodySensors"
android:description="@string/permdesc_bodySensors"
- android:protectionLevel="dangerous"
- android:usageInfoRequired="true" />
+ android:protectionLevel="dangerous" />
<!-- Allows an app to use fingerprint hardware.
<p>Protection level: normal
@@ -1780,8 +1758,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:protectionLevel="dangerous"
android:description="@string/permdesc_getAccounts"
- android:label="@string/permlab_getAccounts"
- android:usageInfoRequired="true" />
+ android:label="@string/permlab_getAccounts" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!-- Allows applications to call into AccountAuthenticators.
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index fa3a549..46e14b4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2426,6 +2426,8 @@
</attr>
<attr name="__removed3" />
+ <attr name="__removed4" />
+ <attr name="__removed5" />
<!-- Describes the content of a view so that a autofill service can fill in the appropriate
data. Multiple hints can be combined in a comma separated list or an array of strings
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 881688b..53cae63 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1689,10 +1689,6 @@
<attr name="request" />
<attr name="protectionLevel" />
<attr name="permissionFlags" />
- <!-- If {@code true} applications that target Q <em>must</em> specify the permission usage
- attributes in their {@code uses-permission} elements or the permission will not be
- granted. -->
- <attr name="usageInfoRequired" format="boolean" />
</declare-styleable>
<!-- The <code>permission-group</code> tag declares a logical grouping of
@@ -1792,81 +1788,6 @@
requested. If it does support the feature, it will be as if the manifest didn't
request it at all. -->
<attr name="requiredNotFeature" format="string" />
-
- <!-- Specify if the app uploads data, or derived data, guarded by this permission.
-
- If the permission is defined with {@link android.R.attr#usageInfoRequired}
- {@code true} this <em>must</em> be specified by apps that target Android Q or the
- permission will not be granted, it will be as if the manifest didn't request it at all.
- -->
- <attr name="dataSentOffDevice">
- <!-- The application may send data, or derived data, guarded by this permission off of the
- device. -->
- <enum name="yes" value="1" />
- <!-- The application may send data, or derived data, guarded by this permission off of the
- device, however it will only do so when explicitly triggered by a user action. -->
- <enum name="userTriggered" value="2" />
- <!-- The application does not send data, or derived data, guarded by this permission off
- of the device. -->
- <enum name="no" value="3" />
- </attr>
-
- <!-- Specify if the application or its related off-device services provide data,
- or derived data, guarded by this permission to third parties outside of the developer's
- organization that do not qualify as data processors.
-
- If the permission is defined with {@link android.R.attr#usageInfoRequired}
- {@code true} this <em>must</em> be specified by apps that target Android Q or the
- permission will not be granted, it will be as if the manifest didn't request it at all.
- -->
- <attr name="dataSharedWithThirdParty">
- <!-- The application or its services may provide data, or derived data, guarded by this
- permission to third party organizations. -->
- <enum name="yes" value="1" />
- <!-- The application or its services may provide data, or derived data, guarded by this
- permission to third party organizations, however it will only do so when explicitly
- triggered by a user action. -->
- <enum name="userTriggered" value="2" />
- <!-- The application or its services does not provide data, or derived data, guarded by
- this permission to third party organizations. -->
- <enum name="no" value="3" />
- </attr>
-
- <!-- Specify if the application or its related off-device services use data,
- or derived data, guarded by this permission for monetization purposes.
-
- For example, if the data is sold to another party or used for targeting advertisements
- this must be set to {@code yes}.
-
- If the permission is defined with {@link android.R.attr#usageInfoRequired}
- {@code true} this <em>must</em> be specified by apps that target Android Q or the
- permission will not be granted, it will be as if the manifest didn't request it at all.
- -->
- <attr name="dataUsedForMonetization">
- <!-- The application or its services may use data, or derived data, guarded by this
- permission for monetization purposes. -->
- <enum name="yes" value="1" />
- <!-- The application or its services may use data, or derived data, guarded by this
- permission for monetization purposes, however it will only do so when explicity
- triggered by a user action. -->
- <enum name="userTriggered" value="2" />
- <!-- The application or its services does not use data, or derived data, guarded by
- this permission for monetization purposes. -->
- <enum name="no" value="3" />
- </attr>
-
- <!-- Specify how long the application or its related off-device services store
- data, or derived data, guarded by this permission.
-
- This can be one of "notRetained", "userSelected", "unlimited", or a number
- representing the number of weeks the data is retained.
-
- If the permission is defined with {@link android.R.attr#usageInfoRequired}
- {@code true} this <em>must</em> be specified by apps that target Android Q or the
- permission will not be granted, it will be as if the manifest didn't request it at all.
- -->
- <attr name="dataRetentionTime" format="string" />
-
</declare-styleable>
<!-- The <code>uses-configuration</code> tag specifies
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9f4fece..ec53811 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -963,7 +963,7 @@
<bool name="config_nightDisplayAvailable">@bool/config_setColorTransformAccelerated</bool>
<!-- Default mode to control how Night display is automatically activated.
- One of the following values (see ColorDisplayController.java):
+ One of the following values (see ColorDisplayManager.java):
0 - AUTO_MODE_DISABLED
1 - AUTO_MODE_CUSTOM_TIME
2 - AUTO_MODE_TWILIGHT
@@ -1052,7 +1052,7 @@
</string-array>
- <!-- Indicate available ColorDisplayController.COLOR_MODE_xxx. -->
+ <!-- Indicate available ColorDisplayManager.COLOR_MODE_xxx. -->
<integer-array name="config_availableColorModes">
<!-- Example:
<item>0</item>
@@ -1594,7 +1594,7 @@
<integer-array name="config_screenBrighteningThresholds">
<item>100</item>
</integer-array>
-
+
<!-- Array of hysteresis constraint values for darkening, represented as tenths of a
percent. The length of this array is assumed to be one greater than
config_screenThresholdLevels. The darkening threshold is calculated as
@@ -3425,8 +3425,10 @@
<!-- Flag indicates that whether non-system apps can be installed on internal storage. -->
<bool name="config_allow3rdPartyAppOnInternal">true</bool>
- <!-- Package name of the default cell broadcast receiver -->
- <string name="config_defaultCellBroadcastReceiverPkg" translatable="false">com.android.cellbroadcastreceiver</string>
+ <!-- Package names of the default cell broadcast receivers -->
+ <string-array name="config_defaultCellBroadcastReceiverPkgs" translatable="false">
+ <item>com.android.cellbroadcastreceiver</item>
+ </string-array>
<!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
<string name="config_icon_mask" translatable="false">"M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58, 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z"</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f84f1f1..e6d478a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2925,11 +2925,11 @@
<public name="importantForContentCapture" />
<public name="supportsMultipleDisplays" />
<public name="useAppZygote" />
- <public name="usageInfoRequired" />
- <public name="dataSentOffDevice" />
- <public name="dataSharedWithThirdParty" />
- <public name="dataUsedForMonetization" />
- <public name="dataRetentionTime" />
+ <public name="__removed1" />
+ <public name="__removed2" />
+ <public name="__removed3" />
+ <public name="__removed4" />
+ <public name="__removed5" />
<public name="selectionDividerHeight" />
<public name="foregroundServiceType" />
<public name="hasFragileUserData" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 401a7fe..1da9149 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3151,7 +3151,7 @@
<java-symbol type="drawable" name="lockscreen_selected" />
<java-symbol type="string" name="notification_header_divider_symbol_with_spaces" />
- <java-symbol type="string" name="config_defaultCellBroadcastReceiverPkg" />
+ <java-symbol type="array" name="config_defaultCellBroadcastReceiverPkgs" />
<java-symbol type="color" name="notification_primary_text_color_light" />
<java-symbol type="color" name="notification_primary_text_color_dark" />
@@ -3595,7 +3595,7 @@
<!-- For Secondary Launcher -->
<java-symbol type="string" name="config_secondaryHomeComponent" />
-
+
<java-symbol type="string" name="dynamic_mode_notification_channel_name" />
<java-symbol type="string" name="dynamic_mode_notification_title" />
<java-symbol type="string" name="dynamic_mode_notification_summary" />
diff --git a/core/tests/coretests/src/android/view/accessibility/FindViewByIdTest.java b/core/tests/coretests/src/android/view/accessibility/FindViewByIdTest.java
new file mode 100644
index 0000000..da6ecb4
--- /dev/null
+++ b/core/tests/coretests/src/android/view/accessibility/FindViewByIdTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.accessibility;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@MediumTest
+public class FindViewByIdTest {
+
+ @Rule
+ public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
+
+ private Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ private Activity getActivity() {
+ return mActivityRule.getActivity();
+ }
+
+ @UiThreadTest
+ @Test
+ public void testFindViewById() {
+ LinearLayout contentView = new LinearLayout(getContext());
+ getActivity().setContentView(contentView);
+ View child1 = new View(getContext());
+ View child2 = new View(getContext());
+ child1.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ child2.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+
+ contentView.addView(child1);
+ contentView.addView(child2);
+ View result = AccessibilityNodeIdManager.getInstance().findView(
+ child2.getAccessibilityViewId());
+ assertEquals(result, child2);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testFindViewByIdReturnNullIfRemovedFromHierarchy() {
+ LinearLayout contentView = new LinearLayout(getContext());
+ getActivity().setContentView(contentView);
+ View child1 = new View(getContext());
+ View child2 = new View(getContext());
+ contentView.addView(child1);
+ contentView.addView(child2);
+ child1.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ child2.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+
+ contentView.removeView(child1);
+ View result = AccessibilityNodeIdManager.getInstance().findView(
+ child1.getAccessibilityViewId());
+ assertNull(result);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testFindViewByIdReturnNullIfNotImportant() {
+ LinearLayout contentView = new LinearLayout(getContext());
+ getActivity().setContentView(contentView);
+ View child1 = new View(getContext());
+ View child2 = new View(getContext());
+ child2.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+
+ contentView.addView(child1);
+ contentView.addView(child2);
+
+ View result = AccessibilityNodeIdManager.getInstance().findView(
+ child1.getAccessibilityViewId());
+ assertNull(result);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
new file mode 100644
index 0000000..f325d89
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
@@ -0,0 +1,229 @@
+/*
+ * 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.contentcapture;
+
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.view.autofill.AutofillId;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+
+/**
+ * Unit test for {@link ContentCaptureEvent}.
+ *
+ * <p>To run it:
+ * {@code atest FrameworksCoreTests:android.view.contentcapture.ContentCaptureEventTest}
+ */
+@RunWith(JUnit4.class)
+public class ContentCaptureEventTest {
+
+ private static final long MY_EPOCH = SystemClock.uptimeMillis();
+
+ // Not using @Mock because it's final - no need to be fancy here....
+ private final ContentCaptureContext mClientContext = new ContentCaptureContext.Builder()
+ .setAction("WHATEVER").build();
+
+ @Test
+ public void testSetAutofillId_null() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED);
+
+ assertThrows(NullPointerException.class, () -> event.setAutofillId(null));
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).isNull();
+ }
+
+ @Test
+ public void testSetAutofillIds_null() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED);
+
+ assertThrows(NullPointerException.class, () -> event.setAutofillIds(null));
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).isNull();
+ }
+
+ @Test
+ public void testAddAutofillId_null() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED);
+
+ assertThrows(NullPointerException.class, () -> event.addAutofillId(null));
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).isNull();
+ }
+
+ @Test
+ public void testSetAutofillId() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED);
+
+ final AutofillId id = new AutofillId(108);
+ event.setAutofillId(id);
+ assertThat(event.getId()).isEqualTo(id);
+ assertThat(event.getIds()).isNull();
+ }
+
+ @Test
+ public void testSetAutofillIds() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED);
+
+ final AutofillId id = new AutofillId(108);
+ final ArrayList<AutofillId> ids = new ArrayList<>(1);
+ ids.add(id);
+ event.setAutofillIds(ids);
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).containsExactly(id);
+ }
+
+ @Test
+ public void testAddAutofillId() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED);
+
+ final AutofillId id1 = new AutofillId(108);
+ event.addAutofillId(id1);
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).containsExactly(id1);
+
+ final AutofillId id2 = new AutofillId(666);
+ event.addAutofillId(id2);
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).containsExactly(id1, id2).inOrder();
+ }
+
+ @Test
+ public void testAddAutofillId_afterSetId() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED);
+
+ final AutofillId id1 = new AutofillId(108);
+ event.setAutofillId(id1);
+ assertThat(event.getId()).isEqualTo(id1);
+ assertThat(event.getIds()).isNull();
+
+ final AutofillId id2 = new AutofillId(666);
+ event.addAutofillId(id2);
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).containsExactly(id1, id2).inOrder();
+ }
+
+ @Test
+ public void testAddAutofillId_afterSetIds() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_VIEW_DISAPPEARED);
+
+ final AutofillId id1 = new AutofillId(108);
+ final ArrayList<AutofillId> ids = new ArrayList<>(1);
+ ids.add(id1);
+ event.setAutofillIds(ids);
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).containsExactly(id1);
+
+ final AutofillId id2 = new AutofillId(666);
+ event.addAutofillId(id2);
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).containsExactly(id1, id2).inOrder();
+ }
+
+ @Test
+ public void testSessionStarted_directly() {
+ final ContentCaptureEvent event = newEventForSessionStarted();
+ assertSessionStartedEvent(event);
+ }
+
+ @Test
+ public void testSessionStarted_throughParcel() {
+ final ContentCaptureEvent event = newEventForSessionStarted();
+ final ContentCaptureEvent clone = cloneThroughParcel(event);
+ assertSessionStartedEvent(clone);
+ }
+
+ private ContentCaptureEvent newEventForSessionStarted() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_SESSION_STARTED)
+ .setClientContext(mClientContext)
+ .setParentSessionId("108");
+ assertThat(event).isNotNull();
+ return event;
+ }
+
+ private void assertSessionStartedEvent(ContentCaptureEvent event) {
+ assertThat(event.getType()).isEqualTo(TYPE_SESSION_STARTED);
+ assertThat(event.getEventTime()).isAtLeast(MY_EPOCH);
+ assertThat(event.getSessionId()).isEqualTo("42");
+ assertThat(event.getParentSessionId()).isEqualTo("108");
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).isNull();
+ assertThat(event.getText()).isNull();
+ assertThat(event.getViewNode()).isNull();
+ final ContentCaptureContext clientContext = event.getClientContext();
+ assertThat(clientContext.getAction()).isEqualTo("WHATEVER");
+ }
+
+ @Test
+ public void testSessionFinished_directly() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_SESSION_FINISHED)
+ .setParentSessionId("108");
+ assertThat(event).isNotNull();
+ assertSessionFinishedEvent(event);
+ }
+
+ @Test
+ public void testSessionFinished_throughParcel() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_SESSION_FINISHED)
+ .setClientContext(mClientContext) // should not be writting to parcel
+ .setParentSessionId("108");
+ assertThat(event).isNotNull();
+ final ContentCaptureEvent clone = cloneThroughParcel(event);
+ assertSessionFinishedEvent(clone);
+ }
+
+ private void assertSessionFinishedEvent(ContentCaptureEvent event) {
+ assertThat(event.getType()).isEqualTo(TYPE_SESSION_FINISHED);
+ assertThat(event.getEventTime()).isAtLeast(MY_EPOCH);
+ assertThat(event.getSessionId()).isEqualTo("42");
+ assertThat(event.getParentSessionId()).isEqualTo("108");
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).isNull();
+ assertThat(event.getText()).isNull();
+ assertThat(event.getViewNode()).isNull();
+ assertThat(event.getClientContext()).isNull();
+ }
+
+ private ContentCaptureEvent cloneThroughParcel(ContentCaptureEvent event) {
+ Parcel parcel = Parcel.obtain();
+
+ try {
+ // Write to parcel
+ parcel.setDataPosition(0); // Sanity / paranoid check
+ event.writeToParcel(parcel, 0);
+
+ // Read from parcel
+ parcel.setDataPosition(0);
+ ContentCaptureEvent clone = ContentCaptureEvent.CREATOR.createFromParcel(parcel);
+ assertThat(clone).isNotNull();
+ return clone;
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index b6f56ad..3d59835a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -27,7 +27,9 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,6 +45,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.metrics.LogMaker;
import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -51,11 +54,14 @@
import com.android.internal.R;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.util.ArrayList;
@@ -66,6 +72,11 @@
*/
@RunWith(AndroidJUnit4.class)
public class ChooserActivityTest {
+
+ private static final int CONTENT_PREVIEW_IMAGE = 1;
+ private static final int CONTENT_PREVIEW_FILE = 2;
+ private static final int CONTENT_PREVIEW_TEXT = 3;
+
@Rule
public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
new ActivityTestRule<>(ChooserWrapperActivity.class, false,
@@ -402,16 +413,15 @@
createResolvedComponentsForTestWithOtherProfile(1);
when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(R.id.copy_button)).perform(click());
-
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
Context.CLIPBOARD_SERVICE);
ClipData clipData = clipboard.getPrimaryClip();
@@ -488,8 +498,8 @@
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(R.id.content_preview_image_1_large)).check(matches(isDisplayed()));
@@ -498,6 +508,93 @@
onView(withId(R.id.content_preview_image_3_small)).check(matches(isDisplayed()));
}
+ @Test
+ public void testOnCreateLogging() {
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ MetricsLogger mockLogger = sOverrides.metricsLogger;
+ ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test"));
+ waitForIdle();
+ verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture());
+ assertThat(logMakerCaptor.getAllValues().get(0).getCategory(),
+ is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN));
+ assertThat(logMakerCaptor
+ .getAllValues().get(0)
+ .getTaggedData(MetricsProto.MetricsEvent.FIELD_TIME_TO_APP_TARGETS),
+ is(notNullValue()));
+ assertThat(logMakerCaptor
+ .getAllValues().get(0)
+ .getTaggedData(MetricsProto.MetricsEvent.FIELD_SHARESHEET_MIMETYPE),
+ is("TestType"));
+ }
+
+ @Test
+ public void testEmptyPreviewLogging() {
+ Intent sendIntent = createSendTextIntentWithPreview(null, null);
+
+ MetricsLogger mockLogger = sOverrides.metricsLogger;
+ ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "empty preview logger test"));
+ waitForIdle();
+ verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture());
+ // First invocation is from onCreate
+ assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
+ is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
+ assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
+ is(CONTENT_PREVIEW_TEXT));
+ }
+
+ @Test
+ public void testTitlePreviewLogging() {
+ Intent sendIntent = createSendTextIntentWithPreview("TestTitle", null);
+
+ MetricsLogger mockLogger = sOverrides.metricsLogger;
+ ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture());
+ // First invocation is from onCreate
+ assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
+ is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
+ assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
+ is(CONTENT_PREVIEW_TEXT));
+ }
+
+ @Test
+ public void testImagePreviewLogging() {
+ Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + com.android.frameworks.coretests.R.drawable.test320x240);
+
+ ArrayList<Uri> uris = new ArrayList<>();
+ uris.add(uri);
+
+ Intent sendIntent = createSendImageIntentWithPreview(uris);
+ sOverrides.previewThumbnail = createBitmap();
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ MetricsLogger mockLogger = sOverrides.metricsLogger;
+ ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture());
+ // First invocation is from onCreate
+ assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
+ is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
+ assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
+ is(CONTENT_PREVIEW_IMAGE));
+ assertThat(logMakerCaptor.getAllValues().get(2).getCategory(),
+ is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
+ assertThat(logMakerCaptor.getAllValues().get(2).getSubtype(),
+ is(CONTENT_PREVIEW_IMAGE));
+ }
+
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index ec8122f..f60467b 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -25,6 +25,8 @@
import android.net.Uri;
import android.util.Size;
+import com.android.internal.logging.MetricsLogger;
+
import java.util.function.Function;
public class ChooserWrapperActivity extends ChooserActivity {
@@ -94,6 +96,11 @@
return super.isImageType(mimeType);
}
+ @Override
+ protected MetricsLogger getMetricsLogger() {
+ return sOverrides.metricsLogger;
+ }
+
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
@@ -106,6 +113,7 @@
public ResolverListController resolverListController;
public Boolean isVoiceInteraction;
public Bitmap previewThumbnail;
+ public MetricsLogger metricsLogger;
public void reset() {
onSafelyStartCallback = null;
@@ -113,6 +121,7 @@
createPackageManager = null;
previewThumbnail = null;
resolverListController = mock(ResolverListController.class);
+ metricsLogger = mock(MetricsLogger.class);
}
}
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 0f35918..6770ae1 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -104,16 +104,6 @@
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
- <privapp-permissions package="com.android.omadm.service">
- <permission name="android.permission.CHANGE_CONFIGURATION"/>
- <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
- <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
- <permission name="android.permission.WRITE_APN_SETTINGS"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.packageinstaller">
<permission name="android.permission.DELETE_PACKAGES"/>
<permission name="android.permission.INSTALL_PACKAGES"/>
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 229923c..5d8ba93 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -36,7 +36,10 @@
* A family of typefaces with different styles.
*
* @hide
+ *
+ * @deprecated Use {@link android.graphics.fonts.FontFamily} instead.
*/
+@Deprecated
public class FontFamily {
private static String TAG = "FontFamily";
@@ -51,20 +54,28 @@
/**
* @hide
+ *
+ * This cannot be deleted because it's in use by AndroidX.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(trackingBug = 123768928)
public long mNativePtr;
// Points native font family builder. Must be zero after freezing this family.
private long mBuilderPtr;
- @UnsupportedAppUsage
+ /**
+ * This cannot be deleted because it's in use by AndroidX.
+ */
+ @UnsupportedAppUsage(trackingBug = 123768928)
public FontFamily() {
mBuilderPtr = nInitBuilder(null, 0);
mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
}
- @UnsupportedAppUsage
+ /**
+ * This cannot be deleted because it's in use by AndroidX.
+ */
+ @UnsupportedAppUsage(trackingBug = 123768928)
public FontFamily(@Nullable String[] langs, int variant) {
final String langsString;
if (langs == null || langs.length == 0) {
@@ -83,8 +94,10 @@
*
* @return boolean returns false if some error happens in native code, e.g. broken font file is
* passed, etc.
+ *
+ * This cannot be deleted because it's in use by AndroidX.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(trackingBug = 123768928)
public boolean freeze() {
if (mBuilderPtr == 0) {
throw new IllegalStateException("This FontFamily is already frozen");
@@ -98,7 +111,10 @@
return mNativePtr != 0;
}
- @UnsupportedAppUsage
+ /**
+ * This cannot be deleted because it's in use by AndroidX.
+ */
+ @UnsupportedAppUsage(trackingBug = 123768928)
public void abortCreation() {
if (mBuilderPtr == 0) {
throw new IllegalStateException("This FontFamily is already frozen or abandoned");
@@ -107,6 +123,10 @@
mBuilderPtr = 0;
}
+ /**
+ * This cannot be deleted because it's in use by AndroidX.
+ */
+ @UnsupportedAppUsage(trackingBug = 123768928)
public boolean addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight,
int italic) {
if (mBuilderPtr == 0) {
@@ -128,7 +148,10 @@
}
}
- @UnsupportedAppUsage
+ /**
+ * This cannot be deleted because it's in use by AndroidX.
+ */
+ @UnsupportedAppUsage(trackingBug = 123768928)
public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
int weight, int italic) {
if (mBuilderPtr == 0) {
@@ -153,8 +176,10 @@
* @param isItalic Whether this font is italic. If the weight is set to 0, this will be resolved
* using the OS/2 table in the font.
* @return
+ *
+ * This cannot be deleted because it's in use by AndroidX.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(trackingBug = 123768928)
public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie,
boolean isAsset, int ttcIndex, int weight, int isItalic,
FontVariationAxis[] axes) {
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index d07088b..7c9529b 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -92,7 +92,13 @@
/** The NORMAL style of the default monospace typeface. */
public static final Typeface MONOSPACE;
- @UnsupportedAppUsage
+ /**
+ * The default {@link Typeface}s for different text styles.
+ * Call {@link #defaultFromStyle(int)} to get the default typeface for the given text style.
+ * It shouldn't be changed for app wide typeface settings. Please use theme and font XML for
+ * the same purpose.
+ */
+ @UnsupportedAppUsage(trackingBug = 123769446)
static Typeface[] sDefaults;
/**
@@ -125,7 +131,11 @@
static final Map<String, Typeface> sSystemFontMap;
// We cannot support sSystemFallbackMap since we will migrate to public FontFamily API.
- @UnsupportedAppUsage
+ /**
+ * @deprecated Use {@link android.graphics.fonts.FontFamily} instead.
+ */
+ @UnsupportedAppUsage(trackingBug = 123768928)
+ @Deprecated
static final Map<String, android.graphics.FontFamily[]> sSystemFallbackMap =
Collections.emptyMap();
@@ -993,7 +1003,7 @@
* @deprecated
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(trackingBug = 123768928)
private static Typeface createFromFamilies(android.graphics.FontFamily[] families) {
long[] ptrArray = new long[families.length];
for (int i = 0; i < families.length; i++) {
@@ -1019,8 +1029,11 @@
/**
* This method is used by supportlib-v27.
+ *
+ * @deprecated Use {@link android.graphics.fonts.FontFamily} instead.
*/
@UnsupportedAppUsage(trackingBug = 123768395)
+ @Deprecated
private static Typeface createFromFamiliesWithDefault(
android.graphics.FontFamily[] families, int weight, int italic) {
return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic);
@@ -1038,8 +1051,11 @@
* the first family's font is used. If the first family has multiple fonts, the
* closest to the regular weight and upright font is used.
* @param families array of font families
+ *
+ * @deprecated Use {@link android.graphics.fonts.FontFamily} instead.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(trackingBug = 123768928)
+ @Deprecated
private static Typeface createFromFamiliesWithDefault(android.graphics.FontFamily[] families,
String fallbackName, int weight, int italic) {
android.graphics.fonts.FontFamily[] fallback = SystemFonts.getSystemFallback(fallbackName);
diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp
index 7708e43..3b9a348 100644
--- a/libs/androidfw/DisplayEventDispatcher.cpp
+++ b/libs/androidfw/DisplayEventDispatcher.cpp
@@ -68,7 +68,7 @@
// Drain all pending events.
nsecs_t vsyncTimestamp;
- int32_t vsyncDisplayId;
+ PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "",
@@ -101,10 +101,11 @@
// Drain all pending events, keep the last vsync.
nsecs_t vsyncTimestamp;
- int32_t vsyncDisplayId;
+ PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
- ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", id=%d, count=%d",
+ ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%"
+ ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
mWaitingForVsync = false;
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
@@ -114,7 +115,7 @@
}
bool DisplayEventDispatcher::processPendingEvents(
- nsecs_t* outTimestamp, int32_t* outId, uint32_t* outCount) {
+ nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) {
bool gotVsync = false;
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
ssize_t n;
@@ -128,11 +129,11 @@
// ones. That's fine, we only care about the most recent.
gotVsync = true;
*outTimestamp = ev.header.timestamp;
- *outId = ev.header.id;
+ *outDisplayId = ev.header.displayId;
*outCount = ev.vsync.count;
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
- dispatchHotplug(ev.header.timestamp, ev.header.id, ev.hotplug.connected);
+ dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
break;
default:
ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
index bf35aa3..d2addba 100644
--- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
+++ b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
@@ -37,10 +37,12 @@
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
- virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) = 0;
- virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) = 0;
+ virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
+ virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
+ bool connected) = 0;
virtual int handleEvent(int receiveFd, int events, void* data);
- bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount);
+ bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
+ uint32_t* outCount);
};
}
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 4c67513..cf5d7ce 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -55,9 +55,12 @@
return sDummyDisplay;
}
+ const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
+ LOG_ALWAYS_FATAL_IF(token == nullptr,
+ "Failed to get display info because internal display is disconnected");
+
DisplayInfo displayInfo;
- sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
- status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &displayInfo);
+ status_t status = SurfaceComposerClient::getDisplayInfo(token, &displayInfo);
LOG_ALWAYS_FATAL_IF(status, "Failed to get display info, error %d", status);
return displayInfo;
}
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 2a48837..76c5660 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -164,14 +164,15 @@
* with reading incorrect data from EGLImage backed SkImage (likely a driver bug).
*/
sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
- SkBudgeted::kYes, bitmap->info());
+ SkBudgeted::kYes, bitmap->info(), 0,
+ kTopLeft_GrSurfaceOrigin, nullptr);
// if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we
// attempt to do the intermediate rendering step in 8888
if (!tmpSurface.get()) {
SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
- tmpInfo);
+ tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
if (!tmpSurface.get()) {
ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
return false;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 1f24f0e..3904ed2 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -329,7 +329,7 @@
bool RenderThread::threadLoop() {
setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
if (gOnStartHook) {
- gOnStartHook();
+ gOnStartHook("RenderThread");
}
initThreadLocals();
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index d062dba..b182928 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -75,7 +75,7 @@
class DummyVsyncSource;
-typedef void (*JVMAttachHook)();
+typedef void (*JVMAttachHook)(const char* name);
class RenderThread : private ThreadBase {
PREVENT_COPY_AND_ASSIGN(RenderThread);
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 92b6cbd..0a54aca 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -37,11 +37,13 @@
0, // presentationDeadline
};
-DisplayInfo getBuiltInDisplay() {
+DisplayInfo getInternalDisplay() {
#if !HWUI_NULL_GPU
DisplayInfo display;
- sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
- status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display);
+ const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
+ LOG_ALWAYS_FATAL_IF(token == nullptr,
+ "Failed to get display info because internal display is disconnected\n");
+ status_t status = SurfaceComposerClient::getDisplayInfo(token, &display);
LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
return display;
#else
diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h
index 0996f4d..116d4de 100644
--- a/libs/hwui/tests/common/TestContext.h
+++ b/libs/hwui/tests/common/TestContext.h
@@ -36,7 +36,7 @@
extern DisplayInfo gDisplay;
#define dp(x) ((x)*android::uirenderer::test::gDisplay.density)
-DisplayInfo getBuiltInDisplay();
+DisplayInfo getInternalDisplay();
class TestContext {
public:
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 5fa008b..0e61899e 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -109,7 +109,7 @@
void run(const TestScene::Info& info, const TestScene::Options& opts,
benchmark::BenchmarkReporter* reporter) {
// Switch to the real display
- gDisplay = getBuiltInDisplay();
+ gDisplay = getInternalDisplay();
Properties::forceDrawFrame = true;
TestContext testContext;
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index 6493d49..de10ff1 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -87,7 +87,7 @@
setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
auto onStartHook = renderthread::RenderThread::getOnStartHook();
if (onStartHook) {
- onStartHook();
+ onStartHook(mName.c_str());
}
return NO_ERROR;
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 1c6210e..761b625 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -353,11 +353,6 @@
// no route flags set, use default as described in Builder.setRouteFlags(int)
mRouteFlags = ROUTE_FLAG_LOOP_BACK;
}
- // can't do loop back AND render at same time in this implementation
- if (mRouteFlags == (ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK)) {
- throw new IllegalArgumentException("Unsupported route behavior combination 0x" +
- Integer.toHexString(mRouteFlags));
- }
if (mFormat == null) {
// FIXME Can we eliminate this? Will AudioMix work with an unspecified sample rate?
int rate = AudioSystem.getPrimaryOutputSamplingRate();
@@ -377,11 +372,11 @@
throw new IllegalArgumentException("Unsupported device on non-playback mix");
}
} else {
- if ((mRouteFlags & ROUTE_FLAG_RENDER) == ROUTE_FLAG_RENDER) {
+ if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_RENDER) {
throw new IllegalArgumentException(
"Can't have flag ROUTE_FLAG_RENDER without an audio device");
}
- if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_LOOP_BACK) {
+ if ((mRouteFlags & ROUTE_FLAG_LOOP_BACK) == ROUTE_FLAG_LOOP_BACK) {
if (mRule.getTargetMixType() == MIX_TYPE_PLAYERS) {
mDeviceSystemType = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
} else if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) {
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 406f9dd..f07f1e8 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -153,8 +153,12 @@
if (nameIsType) {
mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);
+ if (mCodec == nullptr || mCodec->getName(&mNameAtCreation) != OK) {
+ mNameAtCreation = "(null)";
+ }
} else {
mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);
+ mNameAtCreation = name;
}
CHECK((mCodec != NULL) != (mInitStatus != OK));
}
@@ -699,9 +703,8 @@
return err;
}
- // TODO: get alias
ScopedLocalRef<jstring> nameObject(env,
- env->NewStringUTF(codecInfo->getCodecName()));
+ env->NewStringUTF(mNameAtCreation.c_str()));
ScopedLocalRef<jstring> canonicalNameObject(env,
env->NewStringUTF(codecInfo->getCodecName()));
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 0a53f1a..de08550 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -155,6 +155,7 @@
sp<ALooper> mLooper;
sp<MediaCodec> mCodec;
+ AString mNameAtCreation;
sp<AMessage> mCallbackNotification;
sp<AMessage> mOnFrameRenderedNotification;
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index cf14942..6b8f745 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -24,6 +24,10 @@
#include <media/IMediaCodecList.h>
#include <media/MediaCodecInfo.h>
+#include <utils/Vector.h>
+
+#include <vector>
+
#include "android_runtime/AndroidRuntime.h"
#include "jni.h"
#include <nativehelper/JNIHelp.h>
@@ -31,25 +35,91 @@
using namespace android;
-static sp<IMediaCodecList> getCodecList(JNIEnv *env) {
- sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
- if (mcl == NULL) {
- // This should never happen unless something is really wrong
- jniThrowException(
- env, "java/lang/RuntimeException", "cannot get MediaCodecList");
+/**
+ * This object unwraps codec aliases into individual codec infos as the Java interface handles
+ * aliases in this way.
+ */
+class JavaMediaCodecListWrapper {
+public:
+ struct Info {
+ sp<MediaCodecInfo> info;
+ AString alias;
+ };
+
+ const Info getCodecInfo(size_t index) const {
+ if (index < mInfoList.size()) {
+ return mInfoList[index];
+ }
+ // return
+ return Info { nullptr /* info */, "(none)" /* alias */ };
}
- return mcl;
+
+ size_t countCodecs() const {
+ return mInfoList.size();
+ }
+
+ sp<IMediaCodecList> getCodecList() const {
+ return mCodecList;
+ }
+
+ size_t findCodecByName(AString name) const {
+ auto it = mInfoIndex.find(name);
+ return it == mInfoIndex.end() ? -ENOENT : it->second;
+ }
+
+ JavaMediaCodecListWrapper(sp<IMediaCodecList> mcl)
+ : mCodecList(mcl) {
+ size_t numCodecs = mcl->countCodecs();
+ for (size_t ix = 0; ix < numCodecs; ++ix) {
+ sp<MediaCodecInfo> info = mcl->getCodecInfo(ix);
+ Vector<AString> namesAndAliases;
+ info->getAliases(&namesAndAliases);
+ namesAndAliases.insertAt(0);
+ namesAndAliases.editItemAt(0) = info->getCodecName();
+ for (const AString &nameOrAlias : namesAndAliases) {
+ if (mInfoIndex.count(nameOrAlias) > 0) {
+ // skip duplicate names or aliases
+ continue;
+ }
+ mInfoIndex.emplace(nameOrAlias, mInfoList.size());
+ mInfoList.emplace_back(Info { info, nameOrAlias });
+ }
+ }
+ }
+
+private:
+ sp<IMediaCodecList> mCodecList;
+ std::vector<Info> mInfoList;
+ std::map<AString, size_t> mInfoIndex;
+};
+
+static std::mutex sMutex;
+static std::unique_ptr<JavaMediaCodecListWrapper> sListWrapper;
+
+static const JavaMediaCodecListWrapper *getCodecList(JNIEnv *env) {
+ std::lock_guard<std::mutex> lock(sMutex);
+ if (sListWrapper == nullptr) {
+ sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
+ if (mcl == NULL) {
+ // This should never happen unless something is really wrong
+ jniThrowException(
+ env, "java/lang/RuntimeException", "cannot get MediaCodecList");
+ }
+
+ sListWrapper.reset(new JavaMediaCodecListWrapper(mcl));
+ }
+ return sListWrapper.get();
}
-static sp<MediaCodecInfo> getCodecInfo(JNIEnv *env, jint index) {
- sp<IMediaCodecList> mcl = getCodecList(env);
- if (mcl == NULL) {
+static JavaMediaCodecListWrapper::Info getCodecInfo(JNIEnv *env, jint index) {
+ const JavaMediaCodecListWrapper *mcl = getCodecList(env);
+ if (mcl == nullptr) {
// Runtime exception already pending.
- return NULL;
+ return JavaMediaCodecListWrapper::Info { nullptr /* info */, "(none)" /* alias */ };
}
- sp<MediaCodecInfo> info = mcl->getCodecInfo(index);
- if (info == NULL) {
+ JavaMediaCodecListWrapper::Info info = mcl->getCodecInfo(index);
+ if (info.info == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
}
@@ -58,36 +128,36 @@
static jint android_media_MediaCodecList_getCodecCount(
JNIEnv *env, jobject /* thiz */) {
- sp<IMediaCodecList> mcl = getCodecList(env);
+ const JavaMediaCodecListWrapper *mcl = getCodecList(env);
if (mcl == NULL) {
// Runtime exception already pending.
return 0;
}
+
return mcl->countCodecs();
}
static jstring android_media_MediaCodecList_getCodecName(
JNIEnv *env, jobject /* thiz */, jint index) {
- sp<MediaCodecInfo> info = getCodecInfo(env, index);
- if (info == NULL) {
+ JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
+ if (info.info == NULL) {
// Runtime exception already pending.
return NULL;
}
- // TODO: support aliases
- const char *name = info->getCodecName();
+ const char *name = info.alias.c_str();
return env->NewStringUTF(name);
}
static jstring android_media_MediaCodecList_getCanonicalName(
JNIEnv *env, jobject /* thiz */, jint index) {
- sp<MediaCodecInfo> info = getCodecInfo(env, index);
- if (info == NULL) {
+ JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
+ if (info.info == NULL) {
// Runtime exception already pending.
return NULL;
}
- const char *name = info->getCodecName();
+ const char *name = info.info->getCodecName();
return env->NewStringUTF(name);
}
@@ -104,7 +174,7 @@
return -ENOENT;
}
- sp<IMediaCodecList> mcl = getCodecList(env);
+ const JavaMediaCodecListWrapper *mcl = getCodecList(env);
if (mcl == NULL) {
// Runtime exception already pending.
env->ReleaseStringUTFChars(name, nameStr);
@@ -118,25 +188,25 @@
static jboolean android_media_MediaCodecList_getAttributes(
JNIEnv *env, jobject /* thiz */, jint index) {
- sp<MediaCodecInfo> info = getCodecInfo(env, index);
- if (info == NULL) {
+ JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
+ if (info.info == NULL) {
// Runtime exception already pending.
return 0;
}
- return info->getAttributes();
+ return info.info->getAttributes();
}
static jarray android_media_MediaCodecList_getSupportedTypes(
JNIEnv *env, jobject /* thiz */, jint index) {
- sp<MediaCodecInfo> info = getCodecInfo(env, index);
- if (info == NULL) {
+ JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
+ if (info.info == NULL) {
// Runtime exception already pending.
return NULL;
}
Vector<AString> types;
- info->getSupportedMediaTypes(&types);
+ info.info->getSupportedMediaTypes(&types);
jclass clazz = env->FindClass("java/lang/String");
CHECK(clazz != NULL);
@@ -160,8 +230,8 @@
return NULL;
}
- sp<MediaCodecInfo> info = getCodecInfo(env, index);
- if (info == NULL) {
+ JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
+ if (info.info == NULL) {
// Runtime exception already pending.
return NULL;
}
@@ -181,7 +251,7 @@
// TODO query default-format also from codec/codec list
const sp<MediaCodecInfo::Capabilities> &capabilities =
- info->getCapabilitiesFor(typeStr);
+ info.info->getCapabilitiesFor(typeStr);
env->ReleaseStringUTFChars(type, typeStr);
typeStr = NULL;
if (capabilities == NULL) {
@@ -192,7 +262,7 @@
capabilities->getSupportedColorFormats(&colorFormats);
capabilities->getSupportedProfileLevels(&profileLevels);
sp<AMessage> details = capabilities->getDetails();
- bool isEncoder = info->isEncoder();
+ bool isEncoder = info.info->isEncoder();
jobject defaultFormatObj = NULL;
if (ConvertMessageToMap(env, defaultFormat, &defaultFormatObj)) {
@@ -267,13 +337,13 @@
}
static jobject android_media_MediaCodecList_getGlobalSettings(JNIEnv *env, jobject /* thiz */) {
- sp<IMediaCodecList> mcl = getCodecList(env);
+ const JavaMediaCodecListWrapper *mcl = getCodecList(env);
if (mcl == NULL) {
// Runtime exception already pending.
return NULL;
}
- const sp<AMessage> settings = mcl->getGlobalSettings();
+ const sp<AMessage> settings = mcl->getCodecList()->getGlobalSettings();
if (settings == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "cannot get global settings");
return NULL;
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index c3629da..2db575b 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -24,6 +24,7 @@
#include <android/choreographer.h>
#include <androidfw/DisplayEventDispatcher.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
#include <utils/Looper.h>
#include <utils/Mutex.h>
#include <utils/Timers.h>
@@ -67,8 +68,8 @@
explicit Choreographer(const sp<Looper>& looper);
Choreographer(const Choreographer&) = delete;
- virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count);
- virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);
+ void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+ void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void scheduleCallbacks();
@@ -139,13 +140,10 @@
}
}
-
-void Choreographer::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t) {
- if (id != ISurfaceComposer::eDisplayIdMain) {
- ALOGV("choreographer %p ~ ignoring vsync signal for non-main display (id=%d)", this, id);
- scheduleVsync();
- return;
- }
+// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
+// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
+// the internal display implicitly.
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
std::vector<FrameCallback> callbacks{};
{
AutoMutex _l{mLock};
@@ -160,9 +158,10 @@
}
}
-void Choreographer::dispatchHotplug(nsecs_t, int32_t id, bool connected) {
- ALOGV("choreographer %p ~ received hotplug event (id=%" PRId32 ", connected=%s), ignoring.",
- this, id, toString(connected));
+void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
+ ALOGV("choreographer %p ~ received hotplug event (displayId=%"
+ ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
+ this, displayId, toString(connected));
}
void Choreographer::handleMessage(const Message& message) {
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 416ef42..7d2934b 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -46,7 +46,13 @@
static bool getWideColorSupport(const sp<SurfaceControl>& surfaceControl) {
sp<SurfaceComposerClient> client = surfaceControl->getClient();
- sp<IBinder> display(client->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+
+ const sp<IBinder> display = client->getInternalDisplayToken();
+ if (display == nullptr) {
+ ALOGE("unable to get wide color support for disconnected internal display");
+ return false;
+ }
+
bool isWideColorDisplay = false;
status_t err = client->isWideColorDisplay(display, &isWideColorDisplay);
if (err) {
@@ -58,7 +64,12 @@
static bool getHdrSupport(const sp<SurfaceControl>& surfaceControl) {
sp<SurfaceComposerClient> client = surfaceControl->getClient();
- sp<IBinder> display(client->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+
+ const sp<IBinder> display = client->getInternalDisplayToken();
+ if (display == nullptr) {
+ ALOGE("unable to get hdr capabilities for disconnected internal display");
+ return false;
+ }
HdrCapabilities hdrCapabilities;
status_t err = client->getHdrCapabilities(display, &hdrCapabilities);
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java b/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java
index 7ba0f7a..f95891c 100644
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java
+++ b/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java
@@ -15,6 +15,7 @@
*/
package android.ext.services.notification;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_MIN;
@@ -91,7 +92,7 @@
}
// TODO: is from signature app
if (entry.getSbn().getUid() < Process.FIRST_APPLICATION_UID) {
- if (entry.getImportance() >= IMPORTANCE_HIGH) {
+ if (entry.getImportance() >= IMPORTANCE_DEFAULT) {
return CATEGORY_SYSTEM;
} else {
return CATEGORY_SYSTEM_LOW;
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java
index c59885e..05af6e7 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java
@@ -18,6 +18,7 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.os.Process.FIRST_APPLICATION_UID;
@@ -172,23 +173,23 @@
public void testSystemCategory() {
NotificationCategorizer nc = new NotificationCategorizer();
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_HIGH));
- when(mEntry.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
+ when(mEntry.getImportance()).thenReturn(IMPORTANCE_DEFAULT);
when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID - 1);
assertEquals(NotificationCategorizer.CATEGORY_SYSTEM, nc.getCategory(mEntry));
assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_SYSTEM));
when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID);
- assertEquals(NotificationCategorizer.CATEGORY_HIGH, nc.getCategory(mEntry));
+ assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
}
@Test
public void testSystemLowCategory() {
NotificationCategorizer nc = new NotificationCategorizer();
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.getImportance()).thenReturn(IMPORTANCE_DEFAULT);
+ when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_LOW));
+ when(mEntry.getImportance()).thenReturn(IMPORTANCE_LOW);
when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID - 1);
assertEquals(NotificationCategorizer.CATEGORY_SYSTEM_LOW, nc.getCategory(mEntry));
diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
index 8801741..923f162 100644
--- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
@@ -23,6 +23,7 @@
import static android.system.OsConstants.ETH_P_IP;
import static android.system.OsConstants.ETH_P_IPV6;
import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_RAW;
@@ -56,6 +57,7 @@
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -150,7 +152,9 @@
DROPPED_IPV6_NON_ICMP_MULTICAST,
DROPPED_802_3_FRAME,
DROPPED_ETHERTYPE_BLACKLISTED,
- DROPPED_ARP_REPLY_SPA_NO_HOST;
+ DROPPED_ARP_REPLY_SPA_NO_HOST,
+ DROPPED_IPV4_KEEPALIVE_ACK,
+ DROPPED_IPV6_KEEPALIVE_ACK;
// Returns the negative byte offset from the end of the APF data segment for
// a given counter.
@@ -286,6 +290,7 @@
private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
private static final int IPV4_ANY_HOST_ADDRESS = 0;
private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
+ private static final int IPV4_HEADER_LEN = 20; // Without options
// Traffic class and Flow label are not byte aligned. Luckily we
// don't care about either value so we'll consider bytes 1-3 of the
@@ -306,6 +311,8 @@
private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
private static final int UDP_HEADER_LEN = 8;
+ private static final int TCP_HEADER_SIZE_OFFSET = 12;
+
private static final int DHCP_CLIENT_PORT = 68;
// NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
@@ -789,7 +796,7 @@
boolean isExpired() {
// TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
- // have to calculte the filter lifetime specially as a fraction of 0 is still 0.
+ // have to calculate the filter lifetime specially as a fraction of 0 is still 0.
return currentLifetime() <= 0;
}
@@ -848,11 +855,147 @@
}
}
+ // A class to hold keepalive ack information.
+ private abstract static class TcpKeepaliveAck {
+ // Note that the offset starts from IP header.
+ // These must be added ether header length when generating program.
+ static final int IP_HEADER_OFFSET = 0;
+
+ protected static class TcpKeepaliveAckData {
+ public final byte[] srcAddress;
+ public final int srcPort;
+ public final byte[] dstAddress;
+ public final int dstPort;
+ public final int seq;
+ public final int ack;
+ // Create the characteristics of the ack packet from the sent keepalive packet.
+ TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+ srcAddress = sentKeepalivePacket.dstAddress;
+ srcPort = sentKeepalivePacket.dstPort;
+ dstAddress = sentKeepalivePacket.srcAddress;
+ dstPort = sentKeepalivePacket.srcPort;
+ seq = sentKeepalivePacket.ack;
+ ack = sentKeepalivePacket.seq + 1;
+ }
+ }
+
+ protected final TcpKeepaliveAckData mPacket;
+ protected final byte[] mSrcDstAddr;
+
+ TcpKeepaliveAck(final TcpKeepaliveAckData packet, final byte[] srcDstAddr) {
+ mPacket = packet;
+ mSrcDstAddr = srcDstAddr;
+ }
+
+ static byte[] concatArrays(final byte[]... arr) {
+ int size = 0;
+ for (byte[] a : arr) {
+ size += a.length;
+ }
+ final byte[] result = new byte[size];
+ int offset = 0;
+ for (byte[] a : arr) {
+ System.arraycopy(a, 0, result, offset, a.length);
+ offset += a.length;
+ }
+ return result;
+ }
+
+ public String toString() {
+ return String.format("%s(%d) -> %s(%d), seq=%d, ack=%d",
+ mPacket.srcAddress,
+ mPacket.srcPort,
+ mPacket.dstAddress,
+ mPacket.dstPort,
+ mPacket.seq,
+ mPacket.ack);
+ }
+
+ // Append a filter for this keepalive ack to {@code gen}.
+ // Jump to drop if it matches the keepalive ack.
+ // Jump to the next filter if packet doesn't match the keepalive ack.
+ abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
+ }
+
+ private class TcpKeepaliveAckV4 extends TcpKeepaliveAck {
+ private static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12;
+ private static final int IPV4_TCP_SRC_PORT_OFFSET = 0;
+ private static final int IPV4_TCP_DST_PORT_OFFSET = 2;
+ private static final int IPV4_TCP_SEQ_OFFSET = 4;
+ private static final int IPV4_TCP_ACK_OFFSET = 8;
+
+ TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+ this(new TcpKeepaliveAckData(sentKeepalivePacket));
+ }
+ TcpKeepaliveAckV4(final TcpKeepaliveAckData packet) {
+ super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
+ }
+
+ @Override
+ void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked();
+ gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
+ gen.addJumpIfR0NotEquals(IPPROTO_TCP, nextFilterLabel);
+ gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel);
+
+ // Pass the packet if it's not zero-sized :
+ // Load the IP header size into R1
+ gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+ // Load the TCP header size into R0 (it's indexed by R1)
+ gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET);
+ // Size offset is in the top nibble, but it must be multiplied by 4, and the two
+ // top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2.
+ gen.addRightShift(2);
+ // R0 += R1 -> R0 contains TCP + IP headers lenght
+ gen.addAddR1();
+ // Add the Ethernet header length to R0.
+ gen.addLoadImmediate(Register.R1, ETH_HEADER_LEN);
+ gen.addAddR1();
+ // Compare total length of headers to the size of the packet.
+ gen.addLoadFromMemory(Register.R1, gen.PACKET_SIZE_MEMORY_SLOT);
+ gen.addNeg(Register.R0);
+ gen.addAddR1();
+ gen.addJumpIfR0NotEquals(0, nextFilterLabel);
+
+ // Add IPv4 header length
+ gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+ gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SRC_PORT_OFFSET);
+ gen.addJumpIfR0NotEquals(mPacket.srcPort, nextFilterLabel);
+ gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_DST_PORT_OFFSET);
+ gen.addJumpIfR0NotEquals(mPacket.dstPort, nextFilterLabel);
+ gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SEQ_OFFSET);
+ gen.addJumpIfR0NotEquals(mPacket.seq, nextFilterLabel);
+ gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_ACK_OFFSET);
+ gen.addJumpIfR0NotEquals(mPacket.ack, nextFilterLabel);
+
+ maybeSetupCounter(gen, Counter.DROPPED_IPV4_KEEPALIVE_ACK);
+ gen.addJump(mCountAndDropLabel);
+ gen.defineLabel(nextFilterLabel);
+ }
+ }
+
+ private class TcpKeepaliveAckV6 extends TcpKeepaliveAck {
+ TcpKeepaliveAckV6(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+ this(new TcpKeepaliveAckData(sentKeepalivePacket));
+ }
+ TcpKeepaliveAckV6(final TcpKeepaliveAckData packet) {
+ super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
+ }
+
+ @Override
+ void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ throw new UnsupportedOperationException("IPv6 Keepalive is not supported yet");
+ }
+ }
+
// Maximum number of RAs to filter for.
private static final int MAX_RAS = 10;
@GuardedBy("this")
- private ArrayList<Ra> mRas = new ArrayList<Ra>();
+ private ArrayList<Ra> mRas = new ArrayList<>();
+ @GuardedBy("this")
+ private SparseArray<TcpKeepaliveAck> mKeepaliveAcks = new SparseArray<>();
// There is always some marginal benefit to updating the installed APF program when an RA is
// seen because we can extend the program's lifetime slightly, but there is some cost to
@@ -981,6 +1124,8 @@
// drop
// if it's IPv4 broadcast:
// drop
+ // if keepalive ack
+ // drop
// pass
if (mMulticastFilter) {
@@ -1024,6 +1169,9 @@
gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
}
+ // If any keepalive filters,
+ generateKeepaliveFilter(gen);
+
// If L2 broadcast packet, drop.
// TODO: can we invert this condition to fall through to the common pass case below?
maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST);
@@ -1031,6 +1179,8 @@
gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
gen.addJump(mCountAndDropLabel);
+ } else {
+ generateKeepaliveFilter(gen);
}
// Otherwise, pass
@@ -1038,6 +1188,13 @@
gen.addJump(mCountAndPassLabel);
}
+ private void generateKeepaliveFilter(ApfGenerator gen) throws IllegalInstructionException {
+ // Drop IPv4 Keepalive acks
+ for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
+ final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i);
+ if (ack instanceof TcpKeepaliveAckV4) ack.generateFilterLocked(gen);
+ }
+ }
/**
* Generate filter code to process IPv6 packets. Execution of this code ends in either the
@@ -1058,6 +1215,8 @@
// drop
// if it's ICMPv6 NA to ff02::1:
// drop
+ // if keepalive ack
+ // drop
gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
@@ -1113,6 +1272,12 @@
maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
gen.addJump(mCountAndDropLabel);
gen.defineLabel(skipUnsolicitedMulticastNALabel);
+
+ // Drop IPv6 Keepalive acks
+ for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
+ final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i);
+ if (ack instanceof TcpKeepaliveAckV6) ack.generateFilterLocked(gen);
+ }
}
/**
@@ -1491,16 +1656,23 @@
}
/**
- * Add keepalive packet filter.
+ * Add keepalive ack packet filter.
+ * This will add a filter to drop acks to the keepalive packet passed as an argument.
*
* @param slot The index used to access the filter.
- * @param pkt Parameters needed to compose the filter.
+ * @param sentKeepalivePacket The attributes of the sent keepalive packet.
*/
- public synchronized void addKeepalivePacketFilter(int slot,
- TcpKeepalivePacketDataParcelable pkt) {
- // TODO: implement this.
- Log.e(TAG, "APF function is not implemented: addKeepalivePacketFilter(" + slot + ", "
- + pkt + ")");
+ public synchronized void addKeepalivePacketFilter(final int slot,
+ final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+ log("Adding keepalive ack(" + slot + ")");
+ if (null != mKeepaliveAcks.get(slot)) {
+ throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied");
+ }
+ final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6;
+ mKeepaliveAcks.put(slot, (ipVersion == 4)
+ ? new TcpKeepaliveAckV4(sentKeepalivePacket)
+ : new TcpKeepaliveAckV6(sentKeepalivePacket));
+ installNewProgramLocked();
}
/**
@@ -1509,8 +1681,8 @@
* @param slot The index used to access the filter.
*/
public synchronized void removeKeepalivePacketFilter(int slot) {
- // TODO: implement this.
- Log.e(TAG, "APF function is not implemented: removeKeepalivePacketFilter(" + slot + ")");
+ mKeepaliveAcks.remove(slot);
+ installNewProgramLocked();
}
static public long counterValue(byte[] data, Counter counter)
@@ -1565,6 +1737,17 @@
}
pw.decreaseIndent();
+ pw.println("Keepalive filter:");
+ pw.increaseIndent();
+ for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
+ final TcpKeepaliveAck keepaliveAck = mKeepaliveAcks.valueAt(i);
+ pw.print("Slot ");
+ pw.print(mKeepaliveAcks.keyAt(i));
+ pw.print(" : ");
+ pw.println(keepaliveAck);
+ }
+ pw.decreaseIndent();
+
if (DBG) {
pw.println("Last program:");
pw.increaseIndent();
diff --git a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
index 87a1b5e..809327a 100644
--- a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
@@ -476,7 +476,7 @@
/**
* Add an instruction to the end of the program to load 16-bits from the packet into
- * {@code register}. The offset of the loaded 16-bits from the begining of the packet is
+ * {@code register}. The offset of the loaded 16-bits from the beginning of the packet is
* the sum of {@code offset} and the value in register R1.
*/
public ApfGenerator addLoad16Indexed(Register register, int offset) {
@@ -488,7 +488,7 @@
/**
* Add an instruction to the end of the program to load 32-bits from the packet into
- * {@code register}. The offset of the loaded 32-bits from the begining of the packet is
+ * {@code register}. The offset of the loaded 32-bits from the beginning of the packet is
* the sum of {@code offset} and the value in register R1.
*/
public ApfGenerator addLoad32Indexed(Register register, int offset) {
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index dbffa6d..0d6d080 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -61,6 +61,7 @@
import android.net.util.Stopwatch;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
+import android.os.Bundle;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -674,11 +675,11 @@
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
- final Intent intent = new Intent(
- ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+ final Bundle appExtras = new Bundle();
// OneAddressPerFamilyNetwork is not parcelable across processes.
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork));
- intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
+ appExtras.putParcelable(
+ ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork));
+ appExtras.putParcelable(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
new CaptivePortal(new ICaptivePortal.Stub() {
@Override
public void appResponse(int response) {
@@ -700,16 +701,14 @@
}
}));
final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
- intent.putExtra(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
+ appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
if (probeRes.probeSpec != null) {
final String encodedSpec = probeRes.probeSpec.getEncodedSpec();
- intent.putExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
+ appExtras.putString(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
}
- intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
+ appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
mCaptivePortalUserAgent);
- intent.setFlags(
- Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ mCm.startCaptivePortalApp(appExtras);
return HANDLED;
default:
return NOT_HANDLED;
diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
index f76e412..a4a1000 100644
--- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
+++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
@@ -39,13 +39,14 @@
import android.content.Context;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.SocketKeepalive;
+import android.net.TcpKeepalivePacketData;
+import android.net.TcpKeepalivePacketData.TcpSocketInfo;
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
import android.net.ip.IIpClientCallbacks;
-import android.net.ip.IpClient;
import android.net.ip.IpClient.IpClientCallbacksWrapper;
-import android.net.ip.IpClientCallbacks;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
import android.net.util.InterfaceParams;
@@ -1003,15 +1004,31 @@
private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+ private static final int IPV4_HEADER_LEN = 20;
private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
+ private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2;
private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
+ private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12;
private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
+ private static final int IPV4_TCP_HEADER_LEN = 20;
+ private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;
+ private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0;
+ private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2;
+ private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
+ private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
+ private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12;
private static final byte[] IPV4_BROADCAST_ADDRESS =
{(byte) 255, (byte) 255, (byte) 255, (byte) 255};
private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
private static final int IPV6_HEADER_LEN = 40;
+ private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
+ private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+ private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0;
+ private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2;
+ private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4;
+ private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8;
// The IPv6 all nodes address ff02::1
private static final byte[] IPV6_ALL_NODES_ADDRESS =
{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
@@ -1491,6 +1508,200 @@
return packet.array();
}
+ private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 5};
+ private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 6};
+ private static final byte[] IPV4_ANOTHER_ADDR = {10, 0 , 0, 7};
+ private static final byte[] IPV6_KEEPALIVE_SRC_ADDR =
+ {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf1};
+ private static final byte[] IPV6_KEEPALIVE_DST_ADDR =
+ {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf2};
+ private static final byte[] IPV6_ANOTHER_ADDR =
+ {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf5};
+
+ @Test
+ public void testApfFilterKeepaliveAck() throws Exception {
+ final MockIpClientCallback cb = new MockIpClientCallback();
+ final ApfConfiguration config = getDefaultConfig();
+ config.multicastFilter = DROP_MULTICAST;
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
+ byte[] program;
+ final int srcPort = 12345;
+ final int dstPort = 54321;
+ final int seqNum = 2123456789;
+ final int ackNum = 1234567890;
+ final int anotherSrcPort = 23456;
+ final int anotherDstPort = 65432;
+ final int anotherSeqNum = 2123456780;
+ final int anotherAckNum = 1123456789;
+ final int slot1 = 1;
+ final int slot2 = 2;
+ final int window = 14480;
+ final int windowScale = 4;
+
+ // src: 10.0.0.5, port: 12345
+ // dst: 10.0.0.6, port: 54321
+ InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR);
+ InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR);
+
+ final TcpSocketInfo v4Tsi = new TcpSocketInfo(
+ srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale);
+ final TcpKeepalivePacketData ipv4TcpKeepalivePacket =
+ TcpKeepalivePacketData.tcpKeepalivePacket(v4Tsi);
+
+ apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable());
+ program = cb.getApfProgram();
+
+ // Verify IPv4 keepalive ack packet is dropped
+ // src: 10.0.0.6, port: 54321
+ // dst: 10.0.0.5, port: 12345
+ assertDrop(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
+ // Verify IPv4 non-keepalive ack packet from the same source address is passed
+ assertPass(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
+ assertPass(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1, 10 /* dataLength */));
+ // Verify IPv4 packet from another address is passed
+ assertPass(program,
+ ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+ anotherDstPort, anotherSeqNum, anotherAckNum));
+
+ // Remove IPv4 keepalive filter
+ apfFilter.removeKeepalivePacketFilter(slot1);
+
+ try {
+ // src: 2404:0:0:0:0:0:faf1, port: 12345
+ // dst: 2404:0:0:0:0:0:faf2, port: 54321
+ srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR);
+ dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR);
+ final TcpSocketInfo v6Tsi = new TcpSocketInfo(
+ srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale);
+ final TcpKeepalivePacketData ipv6TcpKeepalivePacket =
+ TcpKeepalivePacketData.tcpKeepalivePacket(v6Tsi);
+ apfFilter.addKeepalivePacketFilter(slot1, ipv6TcpKeepalivePacket.toStableParcelable());
+ program = cb.getApfProgram();
+
+ // Verify IPv6 keepalive ack packet is dropped
+ // src: 2404:0:0:0:0:0:faf2, port: 54321
+ // dst: 2404:0:0:0:0:0:faf1, port: 12345
+ assertDrop(program,
+ ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1));
+ // Verify IPv6 non-keepalive ack packet from the same source address is passed
+ assertPass(program,
+ ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum + 100, seqNum));
+ // Verify IPv6 packet from another address is passed
+ assertPass(program,
+ ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+ anotherDstPort, anotherSeqNum, anotherAckNum));
+
+ // Remove IPv6 keepalive filter
+ apfFilter.removeKeepalivePacketFilter(slot1);
+
+ // Verify multiple filters
+ apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable());
+ apfFilter.addKeepalivePacketFilter(slot2, ipv6TcpKeepalivePacket.toStableParcelable());
+ program = cb.getApfProgram();
+
+ // Verify IPv4 keepalive ack packet is dropped
+ // src: 10.0.0.6, port: 54321
+ // dst: 10.0.0.5, port: 12345
+ assertDrop(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1));
+ // Verify IPv4 non-keepalive ack packet from the same source address is passed
+ assertPass(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum + 100, seqNum));
+ // Verify IPv4 packet from another address is passed
+ assertPass(program,
+ ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+ anotherDstPort, anotherSeqNum, anotherAckNum));
+
+ // Verify IPv6 keepalive ack packet is dropped
+ // src: 2404:0:0:0:0:0:faf2, port: 54321
+ // dst: 2404:0:0:0:0:0:faf1, port: 12345
+ assertDrop(program,
+ ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1));
+ // Verify IPv6 non-keepalive ack packet from the same source address is passed
+ assertPass(program,
+ ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum + 100, seqNum));
+ // Verify IPv6 packet from another address is passed
+ assertPass(program,
+ ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+ anotherDstPort, anotherSeqNum, anotherAckNum));
+
+ // Remove keepalive filters
+ apfFilter.removeKeepalivePacketFilter(slot1);
+ apfFilter.removeKeepalivePacketFilter(slot2);
+ } catch (SocketKeepalive.InvalidPacketException e) {
+ // TODO: support V6 packets
+ }
+
+ program = cb.getApfProgram();
+
+ // Verify IPv4, IPv6 packets are passed
+ assertPass(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1));
+ assertPass(program,
+ ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1));
+ assertPass(program,
+ ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort,
+ dstPort, anotherSeqNum, anotherAckNum));
+ assertPass(program,
+ ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort,
+ dstPort, anotherSeqNum, anotherAckNum));
+
+ apfFilter.shutdown();
+ }
+
+ private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
+ int dport, int seq, int ack) {
+ ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
+ packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
+ put(packet, IPV4_SRC_ADDR_OFFSET, sip);
+ put(packet, IPV4_DEST_ADDR_OFFSET, tip);
+ packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport);
+ packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport);
+ packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq);
+ packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack);
+ return packet.array();
+ }
+
+ private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
+ int dport, int seq, int ack, int dataLength) {
+ final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
+
+ ByteBuffer packet = ByteBuffer.wrap(ipv4Packet(sip, tip, sport, dport, seq, ack));
+ packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
+ // TCP header length 5, reserved 3 bits, NS=0
+ packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50);
+ return packet.array();
+ }
+
+ private static byte[] ipv6Packet(byte[] sip, byte[] tip, int sport,
+ int dport, int seq, int ack) {
+ ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6);
+ put(packet, IPV6_SRC_ADDR_OFFSET, sip);
+ put(packet, IPV6_DEST_ADDR_OFFSET, tip);
+ packet.putShort(IPV6_TCP_SRC_PORT_OFFSET, (short) sport);
+ packet.putShort(IPV6_TCP_DEST_PORT_OFFSET, (short) dport);
+ packet.putInt(IPV6_TCP_SEQ_NUM_OFFSET, seq);
+ packet.putInt(IPV6_TCP_ACK_NUM_OFFSET, ack);
+ return packet.array();
+ }
+
// Verify that the last program pushed to the IpClient.Callback properly filters the
// given packet for the given lifetime.
private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 478ee54..cd6abb2 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -37,16 +37,8 @@
<uses-permission android:name="android.permission.READ_PRINT_SERVICES" />
<uses-permission android:name="android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS" />
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
- android:dataSentOffDevice="no"
- android:dataSharedWithThirdParty="no"
- android:dataUsedForMonetization="no"
- android:dataRetentionTime="unlimited"/>
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
- android:dataSentOffDevice="no"
- android:dataSharedWithThirdParty="no"
- android:dataUsedForMonetization="no"
- android:dataRetentionTime="unlimited"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowClearUserData="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 866b46f..3778a9c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -22,11 +22,6 @@
android:sharedUserId="android.uid.systemui"
coreApp="true">
- <!-- Using OpenGL ES 2.0 -->
- <uses-feature
- android:glEsVersion="0x00020000"
- android:required="true" />
-
<!-- SysUI must be the one to define this permission; its name is
referenced by the core OS. -->
<permission android:name="android.permission.systemui.IDENTITY"
diff --git a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
deleted file mode 100644
index 11d73a9..0000000
--- a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
+++ /dev/null
@@ -1,33 +0,0 @@
-precision mediump float;
-
-uniform sampler2D uTexture;
-uniform float uCenterReveal;
-uniform float uReveal;
-uniform float uAod2Opacity;
-uniform int uAodMode;
-varying vec2 vTextureCoordinates;
-
-vec3 luminosity(vec3 color) {
- float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
- return vec3(lum);
-}
-
-vec4 transform(vec3 diffuse) {
- // TODO: Add well comments here, tracking on b/123615467.
- vec3 lum = luminosity(diffuse);
- diffuse = mix(diffuse, lum, smoothstep(0., uCenterReveal, uReveal));
- float val = mix(uReveal, uCenterReveal, step(uCenterReveal, uReveal));
- diffuse = smoothstep(val, 1.0, diffuse);
- diffuse *= uAod2Opacity * (1. - smoothstep(uCenterReveal, 1., uReveal));
- return vec4(diffuse.r, diffuse.g, diffuse.b, 1.);
-}
-
-void main() {
- vec4 fragColor = texture2D(uTexture, vTextureCoordinates);
- // TODO: Remove the branch logic here, tracking on b/123615467.
- if (uAodMode != 0) {
- gl_FragColor = transform(fragColor.rgb);
- } else {
- gl_FragColor = fragColor;
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl
deleted file mode 100644
index 4393e2b..0000000
--- a/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl
+++ /dev/null
@@ -1,8 +0,0 @@
-attribute vec4 aPosition;
-attribute vec2 aTextureCoordinates;
-varying vec2 vTextureCoordinates;
-
-void main() {
- vTextureCoordinates = aTextureCoordinates;
- gl_Position = aPosition;
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index fece94e..c3f61ee 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.SensorPrivacyManager;
+import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.os.Looper;
import android.util.ArrayMap;
@@ -25,7 +26,6 @@
import android.view.IWindowManager;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.ColorDisplayController;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.Preconditions;
@@ -206,7 +206,7 @@
@Inject Lazy<UserInfoController> mUserInfoController;
@Inject Lazy<KeyguardMonitor> mKeyguardMonitor;
@Inject Lazy<BatteryController> mBatteryController;
- @Inject Lazy<ColorDisplayController> mColorDisplayController;
+ @Inject Lazy<NightDisplayListener> mNightDisplayListener;
@Inject Lazy<ManagedProfileController> mManagedProfileController;
@Inject Lazy<NextAlarmController> mNextAlarmController;
@Inject Lazy<DataSaverController> mDataSaverController;
@@ -330,7 +330,7 @@
mProviders.put(BatteryController.class, mBatteryController::get);
- mProviders.put(ColorDisplayController.class, mColorDisplayController::get);
+ mProviders.put(NightDisplayListener.class, mNightDisplayListener::get);
mProviders.put(ManagedProfileController.class, mManagedProfileController::get);
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index 88e32cb..a517d7c 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
+import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -34,7 +35,6 @@
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
-import com.android.internal.app.ColorDisplayController;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -152,8 +152,8 @@
@Singleton
@Provides
- public ColorDisplayController provideColorDisplayController(Context context) {
- return new ColorDisplayController(context);
+ public NightDisplayListener provideNightDisplayListener(Context context) {
+ return new NightDisplayListener(context);
}
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 5040942..2aecc24 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -19,11 +19,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
import android.app.WallpaperManager;
-import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.RecordingCanvas;
@@ -31,9 +28,7 @@
import android.graphics.RectF;
import android.graphics.Region.Op;
import android.hardware.display.DisplayManager;
-import android.opengl.GLSurfaceView;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.Handler;
import android.os.Trace;
import android.service.wallpaper.WallpaperService;
@@ -44,7 +39,6 @@
import android.view.SurfaceHolder;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -57,17 +51,12 @@
public class ImageWallpaper extends WallpaperService {
private static final String TAG = "ImageWallpaper";
private static final String GL_LOG_TAG = "ImageWallpaperGL";
- // TODO: Testing purpose, need to remove later, b/123616712.
- private static final String SENSOR_EVENT_AWAKE = "systemui.test.event.awake";
- // TODO: Testing purpose, need to remove later, b/123616712.
- private static final String SENSOR_EVENT_SLEEP = "systemui.test.event.sleep";
private static final boolean DEBUG = false;
private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
private static final long DELAY_FORGET_WALLPAPER = 5000;
private WallpaperManager mWallpaperManager;
private DrawableEngine mEngine;
- private GLEngine mGlEngine;
@Override
public void onCreate() {
@@ -84,112 +73,10 @@
@Override
public Engine onCreateEngine() {
- mGlEngine = new GLEngine(this);
- return mGlEngine;
+ mEngine = new DrawableEngine();
+ return mEngine;
}
- class GLEngine extends Engine {
- private GLWallpaperSurfaceView mWallpaperSurfaceView;
-
- GLEngine(Context context) {
- mWallpaperSurfaceView = new GLWallpaperSurfaceView(context);
- mWallpaperSurfaceView.setRenderer(
- new ImageWallpaperRenderer(context, mWallpaperSurfaceView));
- mWallpaperSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
- setOffsetNotificationsEnabled(true);
- }
-
- @Override
- public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
- if (mWallpaperSurfaceView != null) {
- mWallpaperSurfaceView.notifyAmbientModeChanged(inAmbientMode);
- }
- }
-
- @Override
- public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
- float yOffsetStep, int xPixelOffset, int yPixelOffset) {
- if (mWallpaperSurfaceView != null) {
- mWallpaperSurfaceView.notifyOffsetsChanged(xOffset, yOffset);
- }
- }
-
- private class GLWallpaperSurfaceView extends GLSurfaceView implements ImageGLView {
- private SensorEventListener mEventListener;
- private WallpaperStatusListener mWallpaperChangedListener;
-
- // TODO: Testing purpose, need to remove later, b/123616712.
- /**
- * For testing only: adb shell am broadcast -a <INTENT>
- */
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent == null) {
- return;
- }
- switch (intent.getAction()) {
- case SENSOR_EVENT_AWAKE:
- notifySensorEvents(true);
- break;
- case SENSOR_EVENT_SLEEP:
- notifySensorEvents(false);
- break;
- }
- }
- };
-
- GLWallpaperSurfaceView(Context context) {
- super(context);
- setEGLContextClientVersion(2);
- // TODO: Testing purpose, need to remove later, b/123616712.
- if (Build.IS_DEBUGGABLE) {
- IntentFilter filter = new IntentFilter();
- filter.addAction(SENSOR_EVENT_AWAKE);
- filter.addAction(SENSOR_EVENT_SLEEP);
- registerReceiver(mReceiver, filter);
- }
- }
-
- @Override
- public SurfaceHolder getHolder() {
- return getSurfaceHolder();
- }
-
- @Override
- public void setRenderer(Renderer renderer) {
- super.setRenderer(renderer);
- mEventListener = (SensorEventListener) renderer;
- mWallpaperChangedListener = (WallpaperStatusListener) renderer;
- }
-
- private void notifySensorEvents(boolean reach) {
- if (mEventListener != null) {
- mEventListener.onSensorEvent(reach);
- }
- }
-
- private void notifyAmbientModeChanged(boolean inAmbient) {
- if (mWallpaperChangedListener != null) {
- mWallpaperChangedListener.onAmbientModeChanged(inAmbient);
- }
- }
-
- private void notifyOffsetsChanged(float xOffset, float yOffset) {
- if (mWallpaperChangedListener != null) {
- mWallpaperChangedListener.onOffsetsChanged(
- xOffset, yOffset, getHolder().getSurfaceFrame());
- }
- }
-
- @Override
- public void render() {
- requestRender();
- }
- }
- }
-
- // TODO: Remove this engine, tracking on b/123617158.
class DrawableEngine extends Engine {
private final Runnable mUnloadWallpaperCallback = () -> {
unloadWallpaper(false /* forgetSize */);
@@ -677,46 +564,4 @@
}
}
}
-
- /**
- * A listener to trace sensor event.
- */
- public interface SensorEventListener {
-
- /**
- * Called back while sensor event comes.
- * @param reach The status of sensor.
- */
- void onSensorEvent(boolean reach);
- }
-
- /**
- * A listener to trace status of image wallpaper.
- */
- public interface WallpaperStatusListener {
-
- /**
- * Called back while ambient mode changes.
- * @param inAmbientMode true if is in ambient mode, false otherwise.
- */
- void onAmbientModeChanged(boolean inAmbientMode);
-
- /**
- * Called back while wallpaper offsets.
- * @param xOffset The offset portion along x.
- * @param yOffset The offset portion along y.
- */
- void onOffsetsChanged(float xOffset, float yOffset, Rect frame);
- }
-
- /**
- * An abstraction for view of GLRenderer.
- */
- public interface ImageGLView {
-
- /**
- * Ask the view to render.
- */
- void render();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index d6a46a9..6877f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,10 @@
package com.android.systemui.bubbles;
+import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
+import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
+import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
+import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -26,17 +30,16 @@
import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.Notification;
-import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.util.StatsLog;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -427,11 +430,15 @@
@Nullable
private PendingIntent getValidBubbleIntent(NotificationEntry notif) {
Notification notification = notif.notification.getNotification();
+ String packageName = notif.notification.getPackageName();
Notification.BubbleMetadata data = notif.getBubbleMetadata();
- if (data != null && canLaunchInActivityView(data.getIntent())) {
+ if (data != null && canLaunchInActivityView(data.getIntent(),
+ true /* enable logging for bubbles */, packageName)) {
return data.getIntent();
- } else if (shouldUseContentIntent(mContext)
- && canLaunchInActivityView(notification.contentIntent)) {
+ }
+ if (shouldUseContentIntent(mContext)
+ && canLaunchInActivityView(notification.contentIntent,
+ false /* disable logging for notifications */, packageName)) {
Log.d(TAG, "[addBubble " + notif.key
+ "]: No appOverlayIntent, using contentIntent.");
return notification.contentIntent;
@@ -442,16 +449,41 @@
/**
* Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
+ *
+ * @param intent the pending intent of the bubble.
+ * @param enableLogging whether bubble developer error should be logged.
+ * @param packageName the notification package name for this bubble.
+ * @return
*/
- private boolean canLaunchInActivityView(PendingIntent intent) {
+ private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging,
+ String packageName) {
if (intent == null) {
return false;
}
ActivityInfo info =
intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
- return info != null
- && ActivityInfo.isResizeableMode(info.resizeMode)
- && (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
+ if (info == null) {
+ if (enableLogging) {
+ StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
+ }
+ return false;
+ }
+ if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+ if (enableLogging) {
+ StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
+ }
+ return false;
+ }
+ if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
+ if (enableLogging) {
+ StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
+ }
+ return false;
+ }
+ return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 4557b4d..d06feed 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -110,7 +110,8 @@
@Override
public void requestWakeUp() {
PowerManager pm = getSystemService(PowerManager.class);
- pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE");
+ pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
+ "com.android.systemui:NODOZE");
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java
deleted file mode 100644
index d03b00b..0000000
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.glwallpaper;
-
-import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
-import static android.opengl.GLES20.GL_VERTEX_SHADER;
-import static android.opengl.GLES20.glAttachShader;
-import static android.opengl.GLES20.glCompileShader;
-import static android.opengl.GLES20.glCreateProgram;
-import static android.opengl.GLES20.glCreateShader;
-import static android.opengl.GLES20.glGetAttribLocation;
-import static android.opengl.GLES20.glGetUniformLocation;
-import static android.opengl.GLES20.glLinkProgram;
-import static android.opengl.GLES20.glShaderSource;
-import static android.opengl.GLES20.glUseProgram;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-
-/**
- * This class takes charge of linking shader codes and then return a handle for OpenGL ES program.
- */
-class ImageGLProgram {
- private static final String TAG = ImageGLProgram.class.getSimpleName();
-
- private Context mContext;
- private int mProgramHandle;
-
- ImageGLProgram(Context context) {
- mContext = context.getApplicationContext();
- }
-
- private int loadShaderProgram(int vertexId, int fragmentId) {
- final String vertexSrc = getShaderResource(vertexId);
- final String fragmentSrc = getShaderResource(fragmentId);
- final int vertexHandle = getShaderHandle(GL_VERTEX_SHADER, vertexSrc);
- final int fragmentHandle = getShaderHandle(GL_FRAGMENT_SHADER, fragmentSrc);
- return getProgramHandle(vertexHandle, fragmentHandle);
- }
-
- private String getShaderResource(int shaderId) {
- Resources res = mContext.getResources();
- StringBuilder code = new StringBuilder();
-
- try (BufferedReader reader = new BufferedReader(
- new InputStreamReader(res.openRawResource(shaderId)))) {
- String nextLine;
- while ((nextLine = reader.readLine()) != null) {
- code.append(nextLine).append("\n");
- }
- } catch (IOException | Resources.NotFoundException ex) {
- Log.d(TAG, "Can not read the shader source", ex);
- code = null;
- }
-
- return code == null ? "" : code.toString();
- }
-
- private int getShaderHandle(int type, String src) {
- final int shader = glCreateShader(type);
- if (shader == 0) {
- Log.d(TAG, "Create shader failed, type=" + type);
- return 0;
- }
- glShaderSource(shader, src);
- glCompileShader(shader);
- return shader;
- }
-
- private int getProgramHandle(int vertexHandle, int fragmentHandle) {
- final int program = glCreateProgram();
- if (program == 0) {
- Log.d(TAG, "Can not create OpenGL ES program");
- return 0;
- }
-
- glAttachShader(program, vertexHandle);
- glAttachShader(program, fragmentHandle);
- glLinkProgram(program);
- return program;
- }
-
- boolean useGLProgram(int vertexResId, int fragmentResId) {
- mProgramHandle = loadShaderProgram(vertexResId, fragmentResId);
- glUseProgram(mProgramHandle);
- return true;
- }
-
- int getAttributeHandle(String name) {
- return glGetAttribLocation(mProgramHandle, name);
- }
-
- int getUniformHandle(String name) {
- return glGetUniformLocation(mProgramHandle, name);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
deleted file mode 100644
index 4e07872..0000000
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.glwallpaper;
-
-import static android.opengl.GLES20.GL_FLOAT;
-import static android.opengl.GLES20.GL_LINEAR;
-import static android.opengl.GLES20.GL_TEXTURE0;
-import static android.opengl.GLES20.GL_TEXTURE_2D;
-import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
-import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
-import static android.opengl.GLES20.GL_TRIANGLES;
-import static android.opengl.GLES20.glActiveTexture;
-import static android.opengl.GLES20.glBindTexture;
-import static android.opengl.GLES20.glDrawArrays;
-import static android.opengl.GLES20.glEnableVertexAttribArray;
-import static android.opengl.GLES20.glGenTextures;
-import static android.opengl.GLES20.glTexParameteri;
-import static android.opengl.GLES20.glUniform1i;
-import static android.opengl.GLES20.glVertexAttribPointer;
-
-import android.graphics.Bitmap;
-import android.opengl.GLUtils;
-import android.util.Log;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-
-/**
- * This class takes charge of the geometry data like vertices and texture coordinates.
- * It delivers these data to opengl runtime and triggers draw calls if necessary.
- */
-class ImageGLWallpaper {
- private static final String TAG = ImageGLWallpaper.class.getSimpleName();
-
- static final String A_POSITION = "aPosition";
- static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
- static final String U_CENTER_REVEAL = "uCenterReveal";
- static final String U_REVEAL = "uReveal";
- static final String U_AOD2OPACITY = "uAod2Opacity";
- static final String U_TEXTURE = "uTexture";
- static final String U_AOD_MODE = "uAodMode";
-
- private static final int HANDLE_UNDEFINED = -1;
- private static final int POSITION_COMPONENT_COUNT = 2;
- private static final int TEXTURE_COMPONENT_COUNT = 2;
- private static final int BYTES_PER_FLOAT = 4;
-
- // Vertices to define the square with 2 triangles.
- private static final float[] VERTICES = {
- -1.0f, -1.0f,
- +1.0f, -1.0f,
- +1.0f, +1.0f,
- +1.0f, +1.0f,
- -1.0f, +1.0f,
- -1.0f, -1.0f
- };
-
- // Texture coordinates that maps to vertices.
- private static final float[] TEXTURES = {
- 0f, 1f,
- 1f, 1f,
- 1f, 0f,
- 1f, 0f,
- 0f, 0f,
- 0f, 1f
- };
-
- private final FloatBuffer mVertexBuffer;
- private final FloatBuffer mTextureBuffer;
- private final ImageGLProgram mProgram;
-
- private int mAttrPosition;
- private int mAttrTextureCoordinates;
- private int mUniAod2Opacity;
- private int mUniAodMode;
- private int mUniCenterReveal;
- private int mUniReveal;
- private int mUniTexture;
- private int mTextureId;
-
- ImageGLWallpaper(ImageGLProgram program) {
- mProgram = program;
-
- // Create an float array in opengles runtime (native) and put vertex data.
- mVertexBuffer = ByteBuffer.allocateDirect(VERTICES.length * BYTES_PER_FLOAT)
- .order(ByteOrder.nativeOrder())
- .asFloatBuffer();
- mVertexBuffer.put(VERTICES);
- mVertexBuffer.position(0);
-
- // Create an float array in opengles runtime (native) and put texture data.
- mTextureBuffer = ByteBuffer.allocateDirect(TEXTURES.length * BYTES_PER_FLOAT)
- .order(ByteOrder.nativeOrder())
- .asFloatBuffer();
- mTextureBuffer.put(TEXTURES);
- mTextureBuffer.position(0);
- }
-
- void setup() {
- setupAttributes();
- setupUniforms();
- }
-
- private void setupAttributes() {
- mAttrPosition = mProgram.getAttributeHandle(A_POSITION);
- mVertexBuffer.position(0);
- glVertexAttribPointer(mAttrPosition, POSITION_COMPONENT_COUNT, GL_FLOAT,
- false, 0, mVertexBuffer);
- glEnableVertexAttribArray(mAttrPosition);
-
- mAttrTextureCoordinates = mProgram.getAttributeHandle(A_TEXTURE_COORDINATES);
- mTextureBuffer.position(0);
- glVertexAttribPointer(mAttrTextureCoordinates, TEXTURE_COMPONENT_COUNT, GL_FLOAT,
- false, 0, mTextureBuffer);
- glEnableVertexAttribArray(mAttrTextureCoordinates);
- }
-
- private void setupUniforms() {
- mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY);
- mUniAodMode = mProgram.getUniformHandle(U_AOD_MODE);
- mUniCenterReveal = mProgram.getUniformHandle(U_CENTER_REVEAL);
- mUniReveal = mProgram.getUniformHandle(U_REVEAL);
- mUniTexture = mProgram.getUniformHandle(U_TEXTURE);
- }
-
- int getHandle(String name) {
- switch (name) {
- case A_POSITION:
- return mAttrPosition;
- case A_TEXTURE_COORDINATES:
- return mAttrTextureCoordinates;
- case U_AOD2OPACITY:
- return mUniAod2Opacity;
- case U_AOD_MODE:
- return mUniAodMode;
- case U_CENTER_REVEAL:
- return mUniCenterReveal;
- case U_REVEAL:
- return mUniReveal;
- case U_TEXTURE:
- return mUniTexture;
- default:
- return HANDLE_UNDEFINED;
- }
- }
-
- void draw() {
- glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2);
- }
-
- void setupTexture(Bitmap bitmap) {
- final int[] tids = new int[1];
-
- if (bitmap == null) {
- Log.w(TAG, "setupTexture: invalid bitmap");
- return;
- }
-
- // Generate one texture object and store the id in tids[0].
- glGenTextures(1, tids, 0);
- if (tids[0] == 0) {
- Log.w(TAG, "setupTexture: glGenTextures() failed");
- return;
- }
-
- // Bind a named texture to a texturing target.
- glBindTexture(GL_TEXTURE_2D, tids[0]);
- // Load the bitmap data and copy it over into the texture object that is currently bound.
- GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
- // Use bilinear texture filtering when minification.
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- // Use bilinear texture filtering when magnification.
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- mTextureId = tids[0];
- }
-
- void useTexture() {
- // Set the active texture unit to texture unit 0.
- glActiveTexture(GL_TEXTURE0);
- // Bind the texture to this unit.
- glBindTexture(GL_TEXTURE_2D, mTextureId);
- // Let the texture sampler in fragment shader to read form this texture unit.
- glUniform1i(mUniTexture, 0);
- }
-
- void adjustTextureCoordinates(Bitmap bitmap, int surfaceWidth, int surfaceHeight,
- float xOffset, float yOffset) {
- if (bitmap == null) {
- Log.d(TAG, "adjustTextureCoordinates: invalid bitmap");
- return;
- }
-
- float ratioW = 1f;
- float ratioH = 1f;
- int bitmapWidth = bitmap.getWidth();
- int bitmapHeight = bitmap.getHeight();
-
- boolean adjustWidth = bitmapWidth > surfaceWidth;
- if (adjustWidth) {
- ratioW = (float) surfaceWidth / bitmapWidth;
- float referenceX = xOffset + ratioW > 1f ? 1f - ratioW : xOffset;
- for (int i = 0; i < TEXTURES.length; i += 2) {
- if (i == 2 || i == 4 || i == 6) {
- TEXTURES[i] = Math.min(1f, referenceX + ratioW);
- } else {
- TEXTURES[i] = referenceX;
- }
- }
- }
-
- boolean adjustHeight = bitmapHeight > surfaceHeight;
- if (adjustHeight) {
- ratioH = (float) surfaceHeight / bitmapHeight;
- float referenceY = yOffset + ratioH > 1f ? 1f - ratioH : yOffset;
- for (int i = 1; i < TEXTURES.length; i += 2) {
- if (i == 1 || i == 3 || i == 11) {
- TEXTURES[i] = Math.min(1f, referenceY + ratioH);
- } else {
- TEXTURES[i] = referenceY;
- }
- }
- }
-
- if (adjustWidth || adjustHeight) {
- mTextureBuffer.put(TEXTURES);
- mTextureBuffer.position(0);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
deleted file mode 100644
index 477e7d7e..0000000
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.glwallpaper;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Handler.Callback;
-import android.os.Message;
-import android.util.Log;
-
-/**
- * A helper class that computes histogram and percentile 85 from a bitmap.
- * Percentile 85 will be computed each time the user picks a new image wallpaper.
- */
-class ImageProcessHelper {
- private static final String TAG = ImageProcessHelper.class.getSimpleName();
- private static final float DEFAULT_PER85 = 0.8f;
- private static final int MSG_UPDATE_PER85 = 1;
-
- /**
- * This color matrix will be applied to each pixel to get luminance from rgb by below formula:
- * Luminance = .2126f * r + .7152f * g + .0722f * b.
- */
- private static final float[] LUMINOSITY_MATRIX = new float[] {
- .2126f, .0000f, .0000f, .0000f, .0000f,
- .0000f, .7152f, .0000f, .0000f, .0000f,
- .0000f, .0000f, .0722f, .0000f, .0000f,
- .0000f, .0000f, .0000f, 1.000f, .0000f
- };
-
- private final Handler mHandler = new Handler(new Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_PER85:
- mPer85 = (float) msg.obj;
- return true;
- default:
- return false;
- }
- }
- });
-
- private float mPer85 = DEFAULT_PER85;
-
- void startComputingPercentile85(Bitmap bitmap) {
- new Per85ComputeTask(mHandler).execute(bitmap);
- }
-
- float getPercentile85() {
- return mPer85;
- }
-
- private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> {
- private Handler mUpdateHandler;
-
- Per85ComputeTask(Handler handler) {
- super(handler);
- mUpdateHandler = handler;
- }
-
- @Override
- protected Float doInBackground(Bitmap... bitmaps) {
- Bitmap bitmap = bitmaps[0];
- if (bitmap != null) {
- int[] histogram = processHistogram(bitmap);
- return computePercentile85(bitmap, histogram);
- }
- Log.e(TAG, "Per85ComputeTask: Can't get bitmap");
- return DEFAULT_PER85;
- }
-
- @Override
- protected void onPostExecute(Float result) {
- Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result);
- mUpdateHandler.sendMessage(msg);
- }
-
- private int[] processHistogram(Bitmap bitmap) {
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
-
- Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig());
- Canvas canvas = new Canvas(target);
- ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
- Paint paint = new Paint();
- paint.setColorFilter(new ColorMatrixColorFilter(cm));
- canvas.drawBitmap(bitmap, new Matrix(), paint);
-
- // TODO: Fine tune the performance here, tracking on b/123615079.
- int[] histogram = new int[256];
- for (int row = 0; row < height; row++) {
- for (int col = 0; col < width; col++) {
- int pixel = target.getPixel(col, row);
- int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
- histogram[y]++;
- }
- }
-
- return histogram;
- }
-
- private float computePercentile85(Bitmap bitmap, int[] histogram) {
- float per85 = DEFAULT_PER85;
- int pixelCount = bitmap.getWidth() * bitmap.getHeight();
- float[] acc = new float[256];
- for (int i = 0; i < acc.length; i++) {
- acc[i] = (float) histogram[i] / pixelCount;
- float prev = i == 0 ? 0f : acc[i - 1];
- float next = acc[i];
- float idx = (float) (i + 1) / 255;
- float sum = prev + next;
- if (prev < 0.85f && sum >= 0.85f) {
- per85 = idx;
- }
- if (i > 0) {
- acc[i] += acc[i - 1];
- }
- }
- return per85;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
deleted file mode 100644
index 787972c..0000000
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.glwallpaper;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-
-import com.android.systemui.Interpolators;
-
-/**
- * Use ValueAnimator and appropriate interpolator to control the progress of reveal transition.
- * The transition will happen while getting awake and quit events.
- */
-class ImageRevealHelper {
- private static final String TAG = ImageRevealHelper.class.getSimpleName();
- private static final float MAX_REVEAL = 0f;
- private static final float MIN_REVEAL = 1f;
- private static final int REVEAL_DURATION = 1000;
-
- private final ValueAnimator mAnimator;
- private final RevealStateListener mRevealListener;
- private float mReveal = MIN_REVEAL;
- private boolean mAwake = false;
-
- ImageRevealHelper(RevealStateListener listener) {
- mRevealListener = listener;
- mAnimator = ValueAnimator.ofFloat();
- mAnimator.setDuration(REVEAL_DURATION);
- mAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mAnimator.addUpdateListener(animator -> {
- mReveal = (float) animator.getAnimatedValue();
- if (mRevealListener != null) {
- mRevealListener.onRevealStateChanged();
- }
- });
- mAnimator.addListener(new AnimatorListenerAdapter() {
- private boolean mIsCanceled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mIsCanceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mIsCanceled) {
- mAwake = !mAwake;
- }
- mIsCanceled = false;
- }
- });
- }
-
- private void animate() {
- mAnimator.cancel();
- mAnimator.setFloatValues(mReveal, !mAwake ? MIN_REVEAL : MAX_REVEAL);
- mAnimator.start();
- }
-
- public float getReveal() {
- return mReveal;
- }
-
- public boolean isAwake() {
- return mAwake;
- }
-
- void updateAwake(boolean awake) {
- mAwake = awake;
- animate();
- }
-
- void sleep() {
- mReveal = MIN_REVEAL;
- mAwake = false;
- }
-
- /**
- * A listener to trace value changes of reveal.
- */
- public interface RevealStateListener {
-
- /**
- * Called back while reveal status changes.
- */
- void onRevealStateChanged();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
deleted file mode 100644
index 8916b28..0000000
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.glwallpaper;
-
-import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
-import static android.opengl.GLES20.glClear;
-import static android.opengl.GLES20.glClearColor;
-import static android.opengl.GLES20.glUniform1f;
-import static android.opengl.GLES20.glUniform1i;
-import static android.opengl.GLES20.glViewport;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.opengl.GLSurfaceView;
-import android.util.Log;
-
-import com.android.systemui.ImageWallpaper;
-import com.android.systemui.ImageWallpaper.ImageGLView;
-import com.android.systemui.R;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-/**
- * A GL renderer for image wallpaper.
- */
-public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,
- ImageWallpaper.SensorEventListener, ImageWallpaper.WallpaperStatusListener,
- ImageRevealHelper.RevealStateListener {
- private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
-
- private final WallpaperManager mWallpaperManager;
- private final ImageGLProgram mProgram;
- private final ImageGLWallpaper mWallpaper;
- private final ImageProcessHelper mImageProcessHelper;
- private final ImageRevealHelper mImageRevealHelper;
- private final ImageGLView mGLView;
- private boolean mIsInAmbientMode;
- private float mXOffset = 0f;
- private float mYOffset = 0f;
-
- public ImageWallpaperRenderer(Context context, ImageGLView glView) {
- mWallpaperManager = context.getSystemService(WallpaperManager.class);
- if (mWallpaperManager == null) {
- Log.w(TAG, "WallpaperManager not available");
- }
-
- mProgram = new ImageGLProgram(context);
- mWallpaper = new ImageGLWallpaper(mProgram);
- mImageProcessHelper = new ImageProcessHelper();
- mImageRevealHelper = new ImageRevealHelper(this);
- mGLView = glView;
-
- if (mWallpaperManager != null) {
- // Compute per85 as transition threshold, this is an async work.
- mImageProcessHelper.startComputingPercentile85(mWallpaperManager.getBitmap());
- }
- }
-
- @Override
- public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- glClearColor(0f, 0f, 0f, 1.0f);
- mProgram.useGLProgram(
- R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader);
- mWallpaper.setup();
- mWallpaper.setupTexture(mWallpaperManager.getBitmap());
- }
-
- @Override
- public void onSurfaceChanged(GL10 gl, int width, int height) {
- glViewport(0, 0, width, height);
- mWallpaper.adjustTextureCoordinates(mWallpaperManager.getBitmap(),
- width, height, mXOffset, mYOffset);
- }
-
- @Override
- public void onDrawFrame(GL10 gl) {
- float threshold = mImageProcessHelper.getPercentile85();
- float reveal = mImageRevealHelper.getReveal();
-
- glClear(GL_COLOR_BUFFER_BIT);
-
- glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), .25f);
- glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_CENTER_REVEAL), threshold);
- glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
- glUniform1i(mWallpaper.getHandle(ImageGLWallpaper.U_AOD_MODE), mIsInAmbientMode ? 1 : 0);
-
- mWallpaper.useTexture();
- mWallpaper.draw();
- }
-
- @Override
- public void onSensorEvent(boolean awake) {
- mImageRevealHelper.updateAwake(awake);
- }
-
- @Override
- public void onAmbientModeChanged(boolean inAmbientMode) {
- mIsInAmbientMode = inAmbientMode;
- if (inAmbientMode) {
- mImageRevealHelper.sleep();
- }
- requestRender();
- }
-
- @Override
- public void onOffsetsChanged(float xOffset, float yOffset, Rect frame) {
- if (frame == null || mWallpaperManager == null
- || (xOffset == mXOffset && yOffset == mYOffset)) {
- return;
- }
-
- Bitmap bitmap = mWallpaperManager.getBitmap();
- if (bitmap == null) {
- return;
- }
-
- int width = frame.width();
- int height = frame.height();
- mXOffset = xOffset;
- mYOffset = yOffset;
-
- mWallpaper.adjustTextureCoordinates(bitmap, width, height, mXOffset, mYOffset);
- requestRender();
- }
-
- @Override
- public void onRevealStateChanged() {
- requestRender();
- }
-
- private void requestRender() {
- if (mGLView != null) {
- mGLView.render();
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 4527f73..66cfadf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -884,6 +884,7 @@
// Just to make sure, make sure the device is awake.
mContext.getSystemService(PowerManager.class).wakeUp(SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_CAMERA_LAUNCH,
"com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK");
mPendingLock = false;
mPendingReset = false;
@@ -1854,8 +1855,9 @@
// It's possible that the device was unlocked in a dream state. It's time to wake up.
if (mAodShowing) {
- PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:BOUNCER_DOZING");
+ PowerManager pm = mContext.getSystemService(PowerManager.class);
+ pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
+ "com.android.systemui:BOUNCER_DOZING");
}
synchronized (KeyguardViewMediator.this) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index fa1426e..cff7fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -69,7 +69,7 @@
setPositiveButton(R.string.ongoing_privacy_dialog_ok, null)
setNeutralButton(R.string.ongoing_privacy_dialog_open_settings,
object : DialogInterface.OnClickListener {
- val intent = Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS).putExtra(
+ val intent = Intent(Settings.ACTION_PRIVACY_SETTINGS).putExtra(
Intent.EXTRA_DURATION_MILLIS, TimeUnit.MINUTES.toMillis(1))
@Suppress("DEPRECATION")
@@ -167,7 +167,7 @@
// Check if package exists
context.packageManager.getPackageInfo(app.packageName, 0)
item.setOnClickListener(object : View.OnClickListener {
- val intent = Intent(Intent.ACTION_REVIEW_APP_PERMISSION_USAGE)
+ val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
.putExtra(Intent.EXTRA_PACKAGE_NAME, app.packageName)
.putExtra(Intent.EXTRA_USER, UserHandle.getUserHandleForUid(app.uid))
override fun onClick(v: View?) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index de78d33..effa935 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -22,6 +22,7 @@
import android.app.ActivityManager;
import android.content.Intent;
import android.hardware.display.ColorDisplayManager;
+import android.hardware.display.NightDisplayListener;
import android.metrics.LogMaker;
import android.provider.Settings;
import android.service.quicksettings.Tile;
@@ -31,7 +32,6 @@
import androidx.annotation.StringRes;
-import com.android.internal.app.ColorDisplayController;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -46,8 +46,9 @@
import javax.inject.Inject;
-public class NightDisplayTile extends QSTileImpl<BooleanState>
- implements ColorDisplayController.Callback {
+/** Quick settings tile: Night display **/
+public class NightDisplayTile extends QSTileImpl<BooleanState> implements
+ NightDisplayListener.Callback {
/**
* Pattern for {@link java.time.format.DateTimeFormatter} used to approximate the time to the
@@ -57,13 +58,15 @@
private static final String PATTERN_HOUR_MINUTE = "h:mm a";
private static final String PATTERN_HOUR_NINUTE_24 = "HH:mm";
- private ColorDisplayController mController;
+ private final ColorDisplayManager mManager;
+ private NightDisplayListener mListener;
private boolean mIsListening;
@Inject
public NightDisplayTile(QSHost host) {
super(host);
- mController = new ColorDisplayController(mContext, ActivityManager.getCurrentUser());
+ mManager = mContext.getSystemService(ColorDisplayManager.class);
+ mListener = new NightDisplayListener(mContext, ActivityManager.getCurrentUser());
}
@Override
@@ -81,27 +84,27 @@
// Enroll in forced auto mode if eligible.
if ("1".equals(Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE))
- && mController.getAutoModeRaw() == -1) {
- mController.setAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
+ && mManager.getNightDisplayAutoModeRaw() == -1) {
+ mManager.setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
Log.i("NightDisplayTile", "Enrolled in forced night display auto mode");
}
// Change current activation state.
final boolean activated = !mState.value;
- mController.setActivated(activated);
+ mManager.setNightDisplayActivated(activated);
}
@Override
protected void handleUserSwitch(int newUserId) {
// Stop listening to the old controller.
if (mIsListening) {
- mController.setListener(null);
+ mListener.setCallback(null);
}
// Make a new controller for the new user.
- mController = new ColorDisplayController(mContext, newUserId);
+ mListener = new NightDisplayListener(mContext, newUserId);
if (mIsListening) {
- mController.setListener(this);
+ mListener.setCallback(this);
}
super.handleUserSwitch(newUserId);
@@ -109,7 +112,7 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.value = mController.isActivated();
+ state.value = mManager.isNightDisplayActivated();
state.label = mContext.getString(R.string.quick_settings_night_display_label);
state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_night_display_on);
state.expandedAccessibilityClassName = Switch.class.getName();
@@ -121,12 +124,12 @@
}
/**
- * Returns a {@link String} for the secondary label that reflects when the light will be turned
- * on or off based on the current auto mode and night light activated status.
+ * Returns a String for the secondary label that reflects when the light will be turned on or
+ * off based on the current auto mode and night light activated status.
*/
@Nullable
private String getSecondaryLabel(boolean isNightLightActivated) {
- switch(mController.getAutoMode()) {
+ switch (mManager.getNightDisplayAutoMode()) {
case ColorDisplayManager.AUTO_MODE_TWILIGHT:
// Auto mode related to sunrise & sunset. If the light is on, it's guaranteed to be
// turned off at sunrise. If it's off, it's guaranteed to be turned on at sunset.
@@ -143,10 +146,10 @@
final DateTimeFormatter toggleTimeFormat;
if (isNightLightActivated) {
- toggleTime = mController.getCustomEndTime();
+ toggleTime = mManager.getNightDisplayCustomEndTime();
toggleTimeStringRes = R.string.quick_settings_secondary_label_until;
} else {
- toggleTime = mController.getCustomStartTime();
+ toggleTime = mManager.getNightDisplayCustomStartTime();
toggleTimeStringRes = R.string.quick_settings_night_secondary_label_on_at;
}
@@ -175,7 +178,8 @@
@Override
public LogMaker populate(LogMaker logMaker) {
- return super.populate(logMaker).addTaggedData(FIELD_QS_MODE, mController.getAutoModeRaw());
+ return super.populate(logMaker)
+ .addTaggedData(FIELD_QS_MODE, mManager.getNightDisplayAutoModeRaw());
}
@Override
@@ -187,10 +191,10 @@
protected void handleSetListening(boolean listening) {
mIsListening = listening;
if (listening) {
- mController.setListener(this);
+ mListener.setCallback(this);
refreshState();
} else {
- mController.setListener(null);
+ mListener.setCallback(null);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 491f310..b820dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -132,7 +132,8 @@
@Override
public boolean onClickHandler(
View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
- mShadeController.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view);
+ mShadeController.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view,
+ "NOTIFICATION_CLICK");
if (handleRemoteInput(view, pendingIntent)) {
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 49f1a8d..b788f53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -52,7 +52,7 @@
return;
}
- mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v);
+ mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK");
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
final StatusBarNotification sbn = row.getStatusBarNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index b6948fc..c7b2fab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -537,7 +537,7 @@
mChosenImportance = IMPORTANCE_LOW;
confirmationText.setText(R.string.notification_channel_silenced);
} else {
- mChosenImportance = IMPORTANCE_HIGH;
+ mChosenImportance = IMPORTANCE_DEFAULT;
confirmationText.setText(R.string.notification_channel_unsilenced);
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index b96c55b..c9be2c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -16,11 +16,11 @@
import android.content.Context;
import android.hardware.display.ColorDisplayManager;
+import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.provider.Settings.Secure;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.ColorDisplayController;
import com.android.systemui.Dependency;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
@@ -50,7 +50,7 @@
private final HotspotController mHotspotController;
private final DataSaverController mDataSaverController;
private final ManagedProfileController mManagedProfileController;
- private final ColorDisplayController mColorDisplayController;
+ private final NightDisplayListener mNightDisplayListener;
@Inject
public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
@@ -58,7 +58,7 @@
HotspotController hotspotController,
DataSaverController dataSaverController,
ManagedProfileController managedProfileController,
- ColorDisplayController colorDisplayController) {
+ NightDisplayListener nightDisplayListener) {
mAutoTracker = autoAddTracker;
mContext = context;
mHost = host;
@@ -66,7 +66,7 @@
mHotspotController = hotspotController;
mDataSaverController = dataSaverController;
mManagedProfileController = managedProfileController;
- mColorDisplayController = colorDisplayController;
+ mNightDisplayListener = nightDisplayListener;
if (!mAutoTracker.isAdded(HOTSPOT)) {
hotspotController.addCallback(mHotspotCallback);
}
@@ -93,7 +93,7 @@
}
if (!mAutoTracker.isAdded(NIGHT)
&& ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- colorDisplayController.setListener(mColorDisplayCallback);
+ nightDisplayListener.setCallback(mNightDisplayCallback);
}
}
@@ -106,7 +106,7 @@
mDataSaverController.removeCallback(mDataSaverListener);
mManagedProfileController.removeCallback(mProfileCallback);
if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- mColorDisplayController.setListener(null);
+ mNightDisplayListener.setCallback(null);
}
}
@@ -157,8 +157,8 @@
};
@VisibleForTesting
- final ColorDisplayController.Callback mColorDisplayCallback =
- new ColorDisplayController.Callback() {
+ final NightDisplayListener.Callback mNightDisplayCallback =
+ new NightDisplayListener.Callback() {
@Override
public void onActivated(boolean activated) {
if (activated) {
@@ -178,7 +178,7 @@
if (mAutoTracker.isAdded(NIGHT)) return;
mHost.addTile(NIGHT);
mAutoTracker.setTileAdded(NIGHT);
- mHandler.post(() -> mColorDisplayController.setListener(null));
+ mHandler.post(() -> mNightDisplayListener.setCallback(null));
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 304d2ee..2162ea7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -232,7 +232,8 @@
if (DEBUG_BIO_WAKELOCK) {
Log.i(TAG, "bio wakelock: Authenticated, waking up...");
}
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:BIOMETRIC");
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
+ "android.policy:BIOMETRIC");
}
if (delayWakeUp) {
mKeyguardViewMediator.onWakeAndUnlocking();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index f926218..234a968 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -14,6 +14,7 @@
package com.android.systemui.statusbar.phone;
+import android.annotation.NonNull;
import android.view.View;
import com.android.systemui.statusbar.StatusBarState;
@@ -96,8 +97,9 @@
*
* @param time when to wake up
* @param view the view requesting the wakeup
+ * @param why the reason for the wake up
*/
- void wakeUpIfDozing(long time, View view);
+ void wakeUpIfDozing(long time, View view, @NonNull String why);
/**
* If secure with redaction: Show bouncer, go to unlocked shade.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3532af5..37a0610 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -482,7 +482,8 @@
updateAodMaskVisibility(deviceSupportsAodWallpaper && aodImageWallpaperEnabled);
// If WallpaperInfo is null, it must be ImageWallpaper.
final boolean supportsAmbientMode = deviceSupportsAodWallpaper
- && (info == null || info.supportsAmbientMode());
+ && (info == null && aodImageWallpaperEnabled
+ || info != null && info.supportsAmbientMode());
mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -551,7 +552,7 @@
private final View.OnClickListener mGoToLockedShadeListener = v -> {
if (mState == StatusBarState.KEYGUARD) {
- wakeUpIfDozing(SystemClock.uptimeMillis(), v);
+ wakeUpIfDozing(SystemClock.uptimeMillis(), v, "SHADE_CLICK");
goToLockedShade(null);
}
};
@@ -1090,10 +1091,10 @@
}
@Override
- public void wakeUpIfDozing(long time, View where) {
+ public void wakeUpIfDozing(long time, View where, String why) {
if (mDozing) {
- PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- pm.wakeUp(time, "com.android.systemui:NODOZE");
+ PowerManager pm = mContext.getSystemService(PowerManager.class);
+ pm.wakeUp(time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
mWakeUpComingFromTouch = true;
where.getLocationInWindow(mTmpInt2);
mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
@@ -3729,7 +3730,8 @@
}
if (!mDeviceInteractive) {
PowerManager pm = mContext.getSystemService(PowerManager.class);
- pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE");
+ pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH,
+ "com.android.systemui:CAMERA_GESTURE");
mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
}
vibrateForCameraGesture();
@@ -3890,7 +3892,8 @@
public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
mScrimController.setPulseReason(reason);
if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) {
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE");
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
+ "com.android.systemui:LONG_PRESS");
startAssist(new Bundle());
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 8b25c34..c39e172 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -112,7 +112,7 @@
mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
mFalsingManager = FalsingManager.getInstance(context);
mDoubleTapHelper = new DoubleTapHelper(this, active -> {}, () -> {
- mService.wakeUpIfDozing(SystemClock.uptimeMillis(), this);
+ mService.wakeUpIfDozing(SystemClock.uptimeMillis(), this, "DOUBLE_TAP");
return true;
}, null, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
index dd1d0ca..52cabe2 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
@@ -156,8 +156,6 @@
private boolean checkIfNeedMask() {
// We need mask for ImageWallpaper / LockScreen Wallpaper (Music album art).
- // Because of conflicting with another wallpaper feature,
- // we only support LockScreen wallpaper currently.
return mWallpaperManager.getWallpaperInfo() == null || ScrimState.AOD.hasBackdrop();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 2a64445..105bd9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -18,7 +18,6 @@
import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
@@ -1067,7 +1066,7 @@
anyString(), eq(TEST_UID), updated.capture());
assertTrue((updated.getValue().getUserLockedFields()
& USER_LOCKED_IMPORTANCE) != 0);
- assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance());
+ assertEquals(IMPORTANCE_DEFAULT, updated.getValue().getImportance());
}
@Test
@@ -1111,7 +1110,7 @@
anyString(), eq(TEST_UID), updated.capture());
assertTrue((updated.getValue().getUserLockedFields()
& USER_LOCKED_IMPORTANCE) != 0);
- assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance());
+ assertEquals(IMPORTANCE_DEFAULT, updated.getValue().getImportance());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 1ded835..f3740c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -21,13 +21,13 @@
import static org.mockito.Mockito.verify;
import android.hardware.display.ColorDisplayManager;
+import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.internal.app.ColorDisplayController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
@@ -58,7 +58,7 @@
mock(HotspotController.class),
mock(DataSaverController.class),
mock(ManagedProfileController.class),
- mock(ColorDisplayController.class));
+ mock(NightDisplayListener.class));
}
@Test
@@ -66,7 +66,7 @@
if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
return;
}
- mAutoTileManager.mColorDisplayCallback.onActivated(true);
+ mAutoTileManager.mNightDisplayCallback.onActivated(true);
verify(mQsTileHost).addTile("night");
}
@@ -75,7 +75,7 @@
if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
return;
}
- mAutoTileManager.mColorDisplayCallback.onActivated(false);
+ mAutoTileManager.mNightDisplayCallback.onActivated(false);
verify(mQsTileHost, never()).addTile("night");
}
@@ -84,7 +84,7 @@
if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
return;
}
- mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+ mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
ColorDisplayManager.AUTO_MODE_TWILIGHT);
verify(mQsTileHost).addTile("night");
}
@@ -94,7 +94,7 @@
if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
return;
}
- mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+ mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
verify(mQsTileHost).addTile("night");
}
@@ -104,7 +104,7 @@
if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
return;
}
- mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+ mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
ColorDisplayManager.AUTO_MODE_DISABLED);
verify(mQsTileHost, never()).addTile("night");
}
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index b8a57ae..1294dbb 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -16,17 +16,13 @@
include $(CLEAR_VARS)
LOCAL_MODULE := frameworks-base-overlays
-
LOCAL_REQUIRED_MODULES := \
- ExperimentNavigationBarFloatingOverlay \
- ExperimentNavigationBarDefaultOverlay \
- ExperimentNavigationBarSlimOverlay32 \
- ExperimentNavigationBarSlimOverlay40 \
- ExperimentNavigationBarLargeOverlay56 \
- ExperimentNavigationBarLargeOverlay64 \
AccentColorBlackOverlay \
AccentColorGreenOverlay \
AccentColorPurpleOverlay \
+ DisplayCutoutEmulationCornerOverlay \
+ DisplayCutoutEmulationDoubleOverlay \
+ DisplayCutoutEmulationTallOverlay \
FontNotoSerifSourceOverlay \
IconPackCircularAndroidOverlay \
IconPackCircularSettingsOverlay \
@@ -42,7 +38,17 @@
IconShapeSquircleOverlay \
IconShapeTeardropOverlay
+include $(BUILD_PHONY_PACKAGE)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := frameworks-base-overlays-debug
+LOCAL_REQUIRED_MODULES := \
+ ExperimentNavigationBarFloatingOverlay \
+ ExperimentNavigationBarDefaultOverlay \
+ ExperimentNavigationBarSlimOverlay32 \
+ ExperimentNavigationBarSlimOverlay40 \
+ ExperimentNavigationBarLargeOverlay56 \
+ ExperimentNavigationBarLargeOverlay64
include $(BUILD_PHONY_PACKAGE)
-
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index eb8710d..3c91069 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6949,6 +6949,32 @@
// OS: Q
NOTIFICATION_SMART_REPLY_MODIFIED_BEFORE_SENDING = 1648;
+ // CATEGORY: ACTION_ACTIVITY_CHOOSER_SHOWN
+ // Field to add the mimetype for a ChooserActivity
+ // OS:Q
+ FIELD_SHARESHEET_MIMETYPE = 1649;
+
+ // CATEGORY: ACTION_ACTIVITY_CHOOSER_SHOWN
+ // Sharesheet direct targets are ready to show.
+ // OS:Q
+ ACTION_ACTIVITY_CHOOSER_SHOWN_DIRECT_TARGET = 1650;
+
+ // CATEGORY: ACTION_SHARESHEET_SCROLL
+ // Sharesheet are either expanded, scrolling through them or compacted again.
+ // OS:Q
+ // Subtype 1 means collapsed, 0 expanded
+ ACTION_SHARESHEET_COLLAPSED_CHANGED = 1651;
+
+ // ACTION: Share with screenshot extra
+ // OS: Q
+ ACTION_SHARE_WITH_PREVIEW = 1652;
+
+ // CATEGORY: ACTION_ACTIVITY_CHOOSER_SHOWN
+ // OS:Q
+ // The time elapsed from triggering the share to displaying the app targets
+ // formerly: histogram system_cost_for_smart_sharing
+ FIELD_TIME_TO_APP_TARGETS = 1653;
+
// ---- 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/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 8886ee2..1bce11ee 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -180,7 +180,7 @@
mAugmentedAutofillResolver = new FrameworkResourcesServiceNameResolver(master.getContext(),
com.android.internal.R.string.config_defaultAugmentedAutofillService);
mAugmentedAutofillResolver.setOnTemporaryServiceNameChangedCallback(
- (u, s) -> updateRemoteAugmentedAutofillService());
+ (u, s) -> updateRemoteAugmentedAutofillService(s));
updateLocked(disabled);
}
@@ -1048,8 +1048,12 @@
componentName, mUserId, new RemoteAugmentedAutofillServiceCallbacks() {
@Override
public void onServiceDied(@NonNull RemoteAugmentedAutofillService service) {
- // TODO(b/123100811): properly implement
Slog.w(TAG, "remote augmented autofill service died");
+ final RemoteAugmentedAutofillService remoteService =
+ mRemoteAugmentedAutofillService;
+ if (remoteService != null) {
+ remoteService.destroy();
+ }
}
}, mMaster.isInstantServiceAllowed(), mMaster.verbose);
}
@@ -1060,8 +1064,7 @@
/**
* Called when the {@link #mAugmentedAutofillResolver} changed (among other places).
*/
- private void updateRemoteAugmentedAutofillService() {
- final String serviceName = mAugmentedAutofillResolver.getServiceName(mUserId);
+ private void updateRemoteAugmentedAutofillService(@Nullable String serviceName) {
if (serviceName == null) {
if (sVerbose) Slog.v(TAG, "updateRemoteAugmentedAutofillService(): time's up!");
if (mRemoteAugmentedAutofillService != null) {
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/ByteRange.java b/services/backup/java/com/android/server/backup/encryption/chunking/ByteRange.java
new file mode 100644
index 0000000..004d9e3
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/ByteRange.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import com.android.internal.util.Preconditions;
+
+/** Representation of a range of bytes to be downloaded. */
+final class ByteRange {
+ private final long mStart;
+ private final long mEnd;
+
+ /** Creates a range of bytes which includes {@code mStart} and {@code mEnd}. */
+ ByteRange(long start, long end) {
+ Preconditions.checkArgument(start >= 0);
+ Preconditions.checkArgument(end >= start);
+ mStart = start;
+ mEnd = end;
+ }
+
+ /** Returns the start of the {@code ByteRange}. The start is included in the range. */
+ long getStart() {
+ return mStart;
+ }
+
+ /** Returns the end of the {@code ByteRange}. The end is included in the range. */
+ long getEnd() {
+ return mEnd;
+ }
+
+ /** Returns the number of bytes included in the {@code ByteRange}. */
+ int getLength() {
+ return (int) (mEnd - mStart + 1);
+ }
+
+ /** Creates a new {@link ByteRange} from {@code mStart} to {@code mEnd + length}. */
+ ByteRange extend(long length) {
+ Preconditions.checkArgument(length > 0);
+ return new ByteRange(mStart, mEnd + length);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof ByteRange)) {
+ return false;
+ }
+
+ ByteRange byteRange = (ByteRange) o;
+ return (mEnd == byteRange.mEnd && mStart == byteRange.mStart);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (int) (mStart ^ (mStart >>> 32));
+ result = 31 * result + (int) (mEnd ^ (mEnd >>> 32));
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("ByteRange{mStart=%d, mEnd=%d}", mStart, mEnd);
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java b/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java
new file mode 100644
index 0000000..69fb5cb
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Writes backup data to a diff script, using a {@link SingleStreamDiffScriptWriter}. */
+public class DiffScriptBackupWriter implements BackupWriter {
+ /**
+ * The maximum size of a chunk in the diff script. The diff script writer {@code mWriter} will
+ * buffer this many bytes in memory.
+ */
+ private static final int ENCRYPTION_DIFF_SCRIPT_MAX_CHUNK_SIZE_BYTES = 1024 * 1024;
+
+ private final SingleStreamDiffScriptWriter mWriter;
+ private long mBytesWritten;
+
+ /**
+ * Constructs a new writer which writes the diff script to the given output stream, using the
+ * maximum new chunk size {@code ENCRYPTION_DIFF_SCRIPT_MAX_CHUNK_SIZE_BYTES}.
+ */
+ public static DiffScriptBackupWriter newInstance(OutputStream outputStream) {
+ SingleStreamDiffScriptWriter writer =
+ new SingleStreamDiffScriptWriter(
+ outputStream, ENCRYPTION_DIFF_SCRIPT_MAX_CHUNK_SIZE_BYTES);
+ return new DiffScriptBackupWriter(writer);
+ }
+
+ @VisibleForTesting
+ DiffScriptBackupWriter(SingleStreamDiffScriptWriter writer) {
+ mWriter = writer;
+ }
+
+ @Override
+ public void writeBytes(byte[] bytes) throws IOException {
+ for (byte b : bytes) {
+ mWriter.writeByte(b);
+ }
+
+ mBytesWritten += bytes.length;
+ }
+
+ @Override
+ public void writeChunk(long start, int length) throws IOException {
+ mWriter.writeChunk(start, length);
+ mBytesWritten += length;
+ }
+
+ @Override
+ public long getBytesWritten() {
+ return mBytesWritten;
+ }
+
+ @Override
+ public void flush() throws IOException {
+ mWriter.flush();
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptWriter.java b/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptWriter.java
new file mode 100644
index 0000000..49d1571
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptWriter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Writer that formats a Diff Script and writes it to an output source. */
+interface DiffScriptWriter {
+ /** Adds a new byte to the diff script. */
+ void writeByte(byte b) throws IOException;
+
+ /** Adds a known chunk to the diff script. */
+ void writeChunk(long chunkStart, int chunkLength) throws IOException;
+
+ /** Indicates that no more bytes or chunks will be added to the diff script. */
+ void flush() throws IOException;
+
+ interface Factory {
+ DiffScriptWriter create(OutputStream outputStream);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsPart.java b/services/backup/java/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
similarity index 60%
rename from telephony/java/android/telephony/ims/RcsPart.java
rename to services/backup/java/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
index da50173..4aea601 100644
--- a/telephony/java/android/telephony/ims/RcsPart.java
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.telephony.ims;
-import android.os.Parcelable;
+package com.android.server.backup.encryption.chunking;
-/**
- * A part of a composite {@link RcsMessage}.
- * @hide - TODO(sahinc) make this public
- */
-public abstract class RcsPart implements Parcelable {
+import java.io.OutputStream;
+
+/** An interface that wraps one {@link OutputStream} with another for filtration purposes. */
+public interface OutputStreamWrapper {
+ /** Wraps a given {@link OutputStream}. */
+ OutputStream wrap(OutputStream outputStream);
}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java b/services/backup/java/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java
new file mode 100644
index 0000000..0e4bd58
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import android.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.util.Locale;
+
+/**
+ * A {@link DiffScriptWriter} that writes an entire diff script to a single {@link OutputStream}.
+ */
+public class SingleStreamDiffScriptWriter implements DiffScriptWriter {
+ static final byte LINE_SEPARATOR = 0xA;
+ private static final Charset UTF_8 = Charset.forName("UTF-8");
+
+ private final int mMaxNewByteChunkSize;
+ private final OutputStream mOutputStream;
+ private final byte[] mByteBuffer;
+ private int mBufferSize = 0;
+ // Each chunk could be written immediately to the output stream. However,
+ // it is possible that chunks may overlap. We therefore cache the most recent
+ // reusable chunk and try to merge it with future chunks.
+ private ByteRange mReusableChunk;
+
+ public SingleStreamDiffScriptWriter(OutputStream outputStream, int maxNewByteChunkSize) {
+ mOutputStream = outputStream;
+ mMaxNewByteChunkSize = maxNewByteChunkSize;
+ mByteBuffer = new byte[maxNewByteChunkSize];
+ }
+
+ @Override
+ public void writeByte(byte b) throws IOException {
+ if (mReusableChunk != null) {
+ writeReusableChunk();
+ }
+ mByteBuffer[mBufferSize++] = b;
+ if (mBufferSize == mMaxNewByteChunkSize) {
+ writeByteBuffer();
+ }
+ }
+
+ @Override
+ public void writeChunk(long chunkStart, int chunkLength) throws IOException {
+ Preconditions.checkArgument(chunkStart >= 0);
+ Preconditions.checkArgument(chunkLength > 0);
+ if (mBufferSize != 0) {
+ writeByteBuffer();
+ }
+
+ if (mReusableChunk != null && mReusableChunk.getEnd() + 1 == chunkStart) {
+ // The new chunk overlaps the old, so combine them into a single byte range.
+ mReusableChunk = mReusableChunk.extend(chunkLength);
+ } else {
+ writeReusableChunk();
+ mReusableChunk = new ByteRange(chunkStart, chunkStart + chunkLength - 1);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ Preconditions.checkState(!(mBufferSize != 0 && mReusableChunk != null));
+ if (mBufferSize != 0) {
+ writeByteBuffer();
+ }
+ if (mReusableChunk != null) {
+ writeReusableChunk();
+ }
+ mOutputStream.flush();
+ }
+
+ private void writeByteBuffer() throws IOException {
+ mOutputStream.write(Integer.toString(mBufferSize).getBytes(UTF_8));
+ mOutputStream.write(LINE_SEPARATOR);
+ mOutputStream.write(mByteBuffer, 0, mBufferSize);
+ mOutputStream.write(LINE_SEPARATOR);
+ mBufferSize = 0;
+ }
+
+ private void writeReusableChunk() throws IOException {
+ if (mReusableChunk != null) {
+ mOutputStream.write(
+ String.format(
+ Locale.US,
+ "%d-%d",
+ mReusableChunk.getStart(),
+ mReusableChunk.getEnd())
+ .getBytes(UTF_8));
+ mOutputStream.write(LINE_SEPARATOR);
+ mReusableChunk = null;
+ }
+ }
+
+ /** A factory that creates {@link SingleStreamDiffScriptWriter}s. */
+ public static class Factory implements DiffScriptWriter.Factory {
+ private final int mMaxNewByteChunkSize;
+ private final OutputStreamWrapper mOutputStreamWrapper;
+
+ public Factory(int maxNewByteChunkSize, @Nullable OutputStreamWrapper outputStreamWrapper) {
+ mMaxNewByteChunkSize = maxNewByteChunkSize;
+ mOutputStreamWrapper = outputStreamWrapper;
+ }
+
+ @Override
+ public SingleStreamDiffScriptWriter create(OutputStream outputStream) {
+ if (mOutputStreamWrapper != null) {
+ outputStream = mOutputStreamWrapper.wrap(outputStream);
+ }
+ return new SingleStreamDiffScriptWriter(outputStream, mMaxNewByteChunkSize);
+ }
+ }
+}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
index 2f78276..39d5c9d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
@@ -77,6 +77,15 @@
pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
pw.println(" To reset, call with just the USER_ID argument.");
pw.println("");
+ pw.println("");
+ pw.println(" set default-service-enabled USER_ID [true|false]");
+ pw.println(" Enable / disable the default service for the user.");
+ pw.println("");
+ pw.println("");
+ pw.println(" get default-service-enabled USER_ID");
+ pw.println(" Checks whether the default service is enabled for the user.");
+ pw.println("");
+ pw.println("");
pw.println(" list sessions [--user USER_ID]");
pw.println(" Lists all pending sessions.");
pw.println("");
@@ -91,6 +100,8 @@
switch(what) {
case "bind-instant-service-allowed":
return getBindInstantService(pw);
+ case "default-service-enabled":
+ return getDefaultServiceEnabled(pw);
default:
pw.println("Invalid set: " + what);
return -1;
@@ -105,6 +116,8 @@
return setBindInstantService(pw);
case "temporary-service":
return setTemporaryService(pw);
+ case "default-service-enabled":
+ return setDefaultServiceEnabled();
default:
pw.println("Invalid set: " + what);
return -1;
@@ -149,6 +162,20 @@
return 0;
}
+ private int setDefaultServiceEnabled() {
+ final int userId = getNextIntArgRequired();
+ final boolean enabled = Boolean.parseBoolean(getNextArg());
+ mService.setDefaultServiceEnabled(userId, enabled);
+ return 0;
+ }
+
+ private int getDefaultServiceEnabled(PrintWriter pw) {
+ final int userId = getNextIntArgRequired();
+ final boolean enabled = mService.isDefaultServiceEnabled(userId);
+ pw.println(enabled);
+ return 0;
+ }
+
private int requestDestroy(PrintWriter pw) {
if (!isNextArgSessions(pw)) {
return -1;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 80b3d67..fe4411c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1833,14 +1833,20 @@
"ConnectivityService");
}
- private void enforceAnyPermissionOf(String... permissions) {
+ private boolean checkAnyPermissionOf(String... permissions) {
for (String permission : permissions) {
if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
- return;
+ return true;
}
}
- throw new SecurityException(
- "Requires one of the following permissions: " + String.join(", ", permissions) + ".");
+ return false;
+ }
+
+ private void enforceAnyPermissionOf(String... permissions) {
+ if (!checkAnyPermissionOf(permissions)) {
+ throw new SecurityException("Requires one of the following permissions: "
+ + String.join(", ", permissions) + ".");
+ }
}
private void enforceInternetPermission() {
@@ -1860,19 +1866,22 @@
}
private void enforceSettingsPermission() {
- mContext.enforceCallingOrSelfPermission(
+ enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
- "ConnectivityService");
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private boolean checkSettingsPermission() {
- return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.NETWORK_SETTINGS);
+ return checkAnyPermissionOf(
+ android.Manifest.permission.NETWORK_SETTINGS,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private boolean checkSettingsPermission(int pid, int uid) {
return PERMISSION_GRANTED == mContext.checkPermission(
- android.Manifest.permission.NETWORK_SETTINGS, pid, uid);
+ android.Manifest.permission.NETWORK_SETTINGS, pid, uid)
+ || PERMISSION_GRANTED == mContext.checkPermission(
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid);
}
private void enforceTetherAccessPermission() {
@@ -1882,9 +1891,9 @@
}
private void enforceConnectivityInternalPermission() {
- mContext.enforceCallingOrSelfPermission(
+ enforceAnyPermissionOf(
android.Manifest.permission.CONNECTIVITY_INTERNAL,
- "ConnectivityService");
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private void enforceControlAlwaysOnVpnPermission() {
@@ -1895,20 +1904,16 @@
private void enforceNetworkStackSettingsOrSetup() {
enforceAnyPermissionOf(
- android.Manifest.permission.NETWORK_SETTINGS,
- android.Manifest.permission.NETWORK_SETUP_WIZARD,
- android.Manifest.permission.NETWORK_STACK);
- }
-
- private void enforceNetworkStackPermission() {
- mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
android.Manifest.permission.NETWORK_STACK,
- "ConnectivityService");
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private boolean checkNetworkStackPermission() {
- return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.NETWORK_STACK);
+ return checkAnyPermissionOf(
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private void enforceConnectivityRestrictedNetworksPermission() {
@@ -3240,6 +3245,25 @@
});
}
+ /**
+ * NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this
+ * endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself.
+ * @param appExtras Bundle to use as intent extras for the captive portal application.
+ * Must be treated as opaque to avoid preventing the captive portal app to
+ * update its arguments.
+ */
+ @Override
+ public void startCaptivePortalAppInternal(Bundle appExtras) {
+ mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+
+ final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+ appIntent.putExtras(appExtras);
+ appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ Binder.withCleanCallingIdentity(() ->
+ mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
+ }
+
public boolean avoidBadWifi() {
return mMultinetworkPolicyTracker.getAvoidBadWifi();
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 5278bbb..4834ce0 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -792,7 +792,7 @@
String[] testProviderStrings = resources.getStringArray(
com.android.internal.R.array.config_testLocationProviders);
for (String testProviderString : testProviderStrings) {
- String fragments[] = testProviderString.split(",");
+ String[] fragments = testProviderString.split(",");
String name = fragments[0].trim();
ProviderProperties properties = new ProviderProperties(
Boolean.parseBoolean(fragments[1]) /* requiresNetwork */,
@@ -816,12 +816,6 @@
return;
}
- // this call has the side effect of forcing a write to the LOCATION_MODE setting in an OS
- // upgrade case, and ensures that if anyone checks the LOCATION_MODE setting directly, they
- // will see it in an appropriate state (at least after that user becomes foreground for the
- // first time...)
- isLocationEnabledForUser(userId);
-
// let providers know the current user is on the way out before changing the user
for (LocationProvider p : mProviders) {
p.onUserChangingLocked();
@@ -934,17 +928,22 @@
@GuardedBy("mLock")
public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(mName + " provider:");
+ pw.print(" " + mName + " provider");
if (isMock()) {
- pw.println(" mock=true");
+ pw.print(" [mock]");
}
- pw.println(" attached=" + (mProvider != null));
- if (mIsManagedBySettings) {
- pw.println(" allowed=" + mAllowed);
+ pw.println(":");
+
+ pw.println(" useable=" + mUseable);
+ if (!mUseable) {
+ pw.println(" attached=" + (mProvider != null));
+ if (mIsManagedBySettings) {
+ pw.println(" allowed=" + mAllowed);
+ }
+ pw.println(" enabled=" + mEnabled);
}
- pw.println(" enabled=" + mEnabled);
- pw.println(" useable=" + mUseable);
- pw.println(" properties=" + mProperties);
+
+ pw.println(" properties=" + mProperties);
if (mProvider != null) {
long identity = Binder.clearCallingIdentity();
@@ -1397,14 +1396,10 @@
public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
if (mListener != null) {
try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- mListener.onStatusChanged(provider, status, extras);
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ mListener.onStatusChanged(provider, status, extras);
+ // call this after broadcasting so we do not increment
+ // if we throw an exception.
+ incrementPendingBroadcastsLocked();
} catch (RemoteException e) {
return false;
}
@@ -1413,16 +1408,12 @@
statusChanged.putExtras(new Bundle(extras));
statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, statusChanged, this, mHandler,
- getResolutionPermission(mAllowedResolutionLevel),
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ mPendingIntent.send(mContext, 0, statusChanged, this, mHandler,
+ getResolutionPermission(mAllowedResolutionLevel),
+ PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+ // call this after broadcasting so we do not increment
+ // if we throw an exception.
+ incrementPendingBroadcastsLocked();
} catch (PendingIntent.CanceledException e) {
return false;
}
@@ -1433,14 +1424,10 @@
public boolean callLocationChangedLocked(Location location) {
if (mListener != null) {
try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- mListener.onLocationChanged(new Location(location));
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ mListener.onLocationChanged(new Location(location));
+ // call this after broadcasting so we do not increment
+ // if we throw an exception.
+ incrementPendingBroadcastsLocked();
} catch (RemoteException e) {
return false;
}
@@ -1449,16 +1436,12 @@
locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED,
new Location(location));
try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
- getResolutionPermission(mAllowedResolutionLevel),
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
+ getResolutionPermission(mAllowedResolutionLevel),
+ PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+ // call this after broadcasting so we do not increment
+ // if we throw an exception.
+ incrementPendingBroadcastsLocked();
} catch (PendingIntent.CanceledException e) {
return false;
}
@@ -1473,18 +1456,14 @@
if (mListener != null) {
try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- if (enabled) {
- mListener.onProviderEnabled(provider);
- } else {
- mListener.onProviderDisabled(provider);
- }
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
+ if (enabled) {
+ mListener.onProviderEnabled(provider);
+ } else {
+ mListener.onProviderDisabled(provider);
}
+ // call this after broadcasting so we do not increment
+ // if we throw an exception.
+ incrementPendingBroadcastsLocked();
} catch (RemoteException e) {
return false;
}
@@ -1492,16 +1471,12 @@
Intent providerIntent = new Intent();
providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcastsLocked()
- // is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
- getResolutionPermission(mAllowedResolutionLevel),
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
+ getResolutionPermission(mAllowedResolutionLevel),
+ PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+ // call this after broadcasting so we do not increment
+ // if we throw an exception.
+ incrementPendingBroadcastsLocked();
} catch (PendingIntent.CanceledException e) {
return false;
}
@@ -1515,8 +1490,6 @@
synchronized (mLock) {
removeUpdatesLocked(this);
- }
- synchronized (this) {
clearPendingBroadcastsLocked();
}
}
@@ -1524,7 +1497,7 @@
@Override
public void onSendFinished(PendingIntent pendingIntent, Intent intent,
int resultCode, String resultData, Bundle resultExtras) {
- synchronized (this) {
+ synchronized (mLock) {
decrementPendingBroadcastsLocked();
}
}
@@ -1533,13 +1506,25 @@
// containing the sending of the broadcaset
private void incrementPendingBroadcastsLocked() {
mPendingBroadcasts++;
- mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
+ // so wakelock calls will succeed
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
private void decrementPendingBroadcastsLocked() {
if (--mPendingBroadcasts == 0) {
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
+ // so wakelock calls will succeed
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
}
@@ -1547,8 +1532,14 @@
public void clearPendingBroadcastsLocked() {
if (mPendingBroadcasts > 0) {
mPendingBroadcasts = 0;
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
+ // so wakelock calls will succeed
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
}
@@ -1561,18 +1552,9 @@
//LocationListener was removed when it had a pending broadcast and should
//not be added back.
synchronized (mLock) {
- IBinder binder = listener.asBinder();
- Receiver receiver = mReceivers.get(binder);
+ Receiver receiver = mReceivers.get(listener.asBinder());
if (receiver != null) {
- synchronized (receiver) {
- // so wakelock calls will succeed
- long identity = Binder.clearCallingIdentity();
- try {
- receiver.decrementPendingBroadcastsLocked();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ receiver.decrementPendingBroadcastsLocked();
}
}
}
@@ -2072,6 +2054,7 @@
if (!provider.isUseableLocked()) {
if (isSettingsExemptLocked(record)) {
providerRequest.forceLocation = true;
+ providerRequest.lowPowerMode = false;
} else {
continue;
}
@@ -2080,7 +2063,9 @@
LocationRequest locationRequest = record.mRealRequest;
long interval = locationRequest.getInterval();
- if (!isThrottlingExemptLocked(record.mReceiver.mCallerIdentity)) {
+ // if we're forcing location, don't apply any throttling
+ if (!providerRequest.forceLocation && !isThrottlingExemptLocked(
+ record.mReceiver.mCallerIdentity)) {
if (!record.mIsForegroundUid) {
interval = Math.max(interval, backgroundThrottleInterval);
}
@@ -2174,11 +2159,8 @@
return true;
}
- if (isProviderPackage(callerIdentity.mPackageName)) {
- return true;
- }
+ return isProviderPackage(callerIdentity.mPackageName);
- return false;
}
@GuardedBy("mLock")
@@ -2192,11 +2174,8 @@
return true;
}
- if (isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName)) {
- return true;
- }
+ return isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName);
- return false;
}
private class UpdateRecord {
@@ -2504,9 +2483,7 @@
if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
- synchronized (receiver) {
- receiver.clearPendingBroadcastsLocked();
- }
+ receiver.clearPendingBroadcastsLocked();
}
receiver.updateMonitoring(false);
@@ -2682,7 +2659,6 @@
// geo-fence manager uses the public location API, need to clear identity
int uid = Binder.getCallingUid();
- // TODO: http://b/23822629
if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) {
// temporary measure until geofences work for secondary users
Log.w(TAG, "proximity alerts are currently available only to the primary user");
@@ -2723,8 +2699,6 @@
return false;
}
- // TODO(b/120449926): The GNSS status listeners should be handled similar to the GNSS
- // measurements listeners.
return mGnssStatusProvider.addListener(callback, new CallerIdentity(Binder.getCallingUid(),
Binder.getCallingPid(), packageName));
}
@@ -2744,7 +2718,6 @@
synchronized (mLock) {
CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
Binder.getCallingPid(), packageName);
- // TODO(b/120481270): Register for client death notification and update map.
mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
try {
@@ -2768,9 +2741,9 @@
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to inject GNSS measurement corrections.");
if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
- mGnssMeasurementsProvider.injectGnssMeasurementCorrections(measurementCorrections);
- } else {
Slog.e(TAG, "Can not inject GNSS corrections due to no permission.");
+ } else {
+ mGnssMeasurementsProvider.injectGnssMeasurementCorrections(measurementCorrections);
}
}
@@ -2809,7 +2782,6 @@
CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
Binder.getCallingPid(), packageName);
- // TODO(b/120481270): Register for client death notification and update map.
mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
try {
@@ -3382,7 +3354,9 @@
return;
}
pw.println("Current Location Manager state:");
- pw.println(" Location Mode: " + isLocationEnabled());
+ pw.println(" Current user: " + mCurrentUserId + " " + Arrays.toString(
+ mCurrentUserProfiles));
+ pw.println(" Location mode: " + isLocationEnabled());
pw.println(" Location Listeners:");
for (Receiver receiver : mReceivers.values()) {
pw.println(" " + receiver);
@@ -3406,14 +3380,6 @@
+ callerIdentity.mPackageName + ": "
+ isThrottlingExemptLocked(callerIdentity));
}
- pw.println(" Overlay Provider Packages:");
- for (LocationProvider provider : mProviders) {
- if (provider.mProvider instanceof LocationProviderProxy) {
- pw.println(" " + provider.getName() + ": "
- + ((LocationProviderProxy) provider.mProvider)
- .getProviderPackages());
- }
- }
pw.println(" Historical Records by Provider:");
for (Map.Entry<PackageProviderKey, PackageStatistics> entry
: mRequestStatistics.statistics.entrySet()) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2f1f91e..026430b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -333,7 +333,8 @@
}
r.delayed = false;
try {
- startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true);
+ startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true,
+ false);
} catch (TransactionTooLargeException e) {
// Ignore, nobody upstack cares.
}
@@ -643,7 +644,8 @@
SERVICE_BG_ACTIVITY_START_TIMEOUT_MS);
}
}
- ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
+ ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting,
+ allowBackgroundActivityStarts);
return cmp;
}
@@ -702,7 +704,8 @@
}
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
- boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
+ boolean callerFg, boolean addToStarting, boolean allowBackgroundActivityStarts)
+ throws TransactionTooLargeException {
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
@@ -713,7 +716,8 @@
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
}
- String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
+ String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false,
+ allowBackgroundActivityStarts);
if (error != null) {
return new ComponentName("!!", error);
}
@@ -1645,7 +1649,7 @@
try {
bringUpServiceLocked(serviceRecord,
serviceIntent.getFlags(),
- callerFg, false, false);
+ callerFg, false, false, false);
} catch (RemoteException e) {
/* ignore - local call */
}
@@ -1748,7 +1752,7 @@
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
- permissionsReviewRequired) != null) {
+ permissionsReviewRequired, false) != null) {
return 0;
}
}
@@ -2418,7 +2422,8 @@
return;
}
try {
- bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false);
+ bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false,
+ false);
} catch (TransactionTooLargeException e) {
// Ignore, it's been logged and nothing upstack cares.
}
@@ -2463,8 +2468,8 @@
}
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
- boolean whileRestarting, boolean permissionsReviewRequired)
- throws TransactionTooLargeException {
+ boolean whileRestarting, boolean permissionsReviewRequired,
+ boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
//Slog.i(TAG, "Bring up service:");
//r.dump(" ");
@@ -2575,6 +2580,13 @@
}
}
+ if (app != null && allowBackgroundActivityStarts) {
+ app.addAllowBackgroundActivityStartsToken(r);
+ // schedule removal of the whitelisting token after the timeout
+ removeAllowBackgroundActivityStartsServiceToken(app, r,
+ SERVICE_BG_ACTIVITY_START_TIMEOUT_MS);
+ }
+
if (r.fgRequired) {
if (DEBUG_FOREGROUND_SERVICE) {
Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d199424..5932f99 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14997,7 +14997,7 @@
oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
oldRecord.intent,
Activity.RESULT_CANCELED, null, null,
- false, false, oldRecord.userId);
+ false, false, oldRecord.userId, oldRecord);
} catch (RemoteException e) {
Slog.w(TAG, "Failure ["
+ queue.mQueueName + "] sending broadcast result of "
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index 1118014..502f078 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -170,6 +170,9 @@
updateCompactionThrottles();
updateStatsdSampleRate();
}
+ Process.setThreadGroupAndCpuset(mCompactionThread.getThreadId(),
+ Process.THREAD_GROUP_SYSTEM);
+
}
/**
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a11ebfd..f0b137a 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -74,6 +74,9 @@
static final int MAX_BROADCAST_SUMMARY_HISTORY
= ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
+ // For how long after a whitelisted receiver's start its process can start a background activity
+ private static final int RECEIVER_BG_ACTIVITY_START_TIMEOUT_MS = 10_000;
+
final ActivityManagerService mService;
/**
@@ -551,13 +554,23 @@
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
+ boolean ordered, boolean sticky, int sendingUser, BroadcastRecord br)
+ throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
if (app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
try {
+ if (br.allowBackgroundActivityStarts) {
+ app.addAllowBackgroundActivityStartsToken(br);
+ // schedule removal of the whitelisting token after the timeout
+ mHandler.postDelayed(() -> {
+ if (app != null) {
+ app.removeAllowBackgroundActivityStartsToken(br);
+ }
+ }, RECEIVER_BG_ACTIVITY_START_TIMEOUT_MS);
+ }
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
// TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
@@ -783,7 +796,7 @@
} else {
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
- r.resultExtras, r.ordered, r.initialSticky, r.userId);
+ r.resultExtras, r.ordered, r.initialSticky, r.userId, r);
}
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
@@ -1082,7 +1095,7 @@
}
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false, r.userId);
+ r.resultData, r.resultExtras, false, false, r.userId, r);
// Set this to null so that the reference
// (local and remote) isn't kept in the mBroadcastHistory.
r.resultTo = null;
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 03c5c93..7035698 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1709,8 +1709,9 @@
(app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
app.curAdj == ProcessList.HOME_APP_ADJ)) {
mAppCompact.compactAppSome(app);
- } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ &&
- app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+ } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
+ && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
+ && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
mAppCompact.compactAppFull(app);
}
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 0b27a8a..af56352 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -384,6 +384,9 @@
final boolean allowTrampoline = uid != callingUid
&& controller.mAtmInternal.isUidForeground(callingUid);
+ // note: we on purpose don't pass in the information about the PendingIntent's creator,
+ // like pid or ProcessRecord, to the ActivityTaskManagerInternal calls below, because
+ // it's not unusual for the creator's process to not be alive at this time
switch (key.type) {
case ActivityManager.INTENT_SENDER_ACTIVITY:
try {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index a3bae52..0dc73d9 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -381,11 +381,11 @@
//---------------------------------------------------------------------
// Message handling on behalf of helper classes
- /*package*/ void broadcastScoConnectionState(int state) {
+ /*package*/ void postBroadcastScoConnectionState(int state) {
sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
}
- /*package*/ void broadcastBecomingNoisy() {
+ /*package*/ void postBroadcastBecomingNoisy() {
sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
}
@@ -415,6 +415,22 @@
delay);
}
+ /*package*/ void postDisconnectA2dp() {
+ sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
+ }
+
+ /*package*/ void postDisconnectA2dpSink() {
+ sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
+ }
+
+ /*package*/ void postDisconnectHearingAid() {
+ sendMsgNoDelay(MSG_DISCONNECT_HEARING_AID, SENDMSG_QUEUE);
+ }
+
+ /*package*/ void postDisconnectHeadset() {
+ sendMsgNoDelay(MSG_DISCONNECT_HEADSET, SENDMSG_QUEUE);
+ }
+
//---------------------------------------------------------------------
// Method forwarding between the helper classes (BtHelper, AudioDeviceInventory)
// only call from a "handle"* method or "on"* method
@@ -444,23 +460,6 @@
}
}
- /*package*/ void handleDisconnectA2dp() {
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectA2dp();
- }
- }
- /*package*/ void handleDisconnectA2dpSink() {
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectA2dpSink();
- }
- }
-
- /*package*/ void handleDisconnectHearingAid() {
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectHearingAid();
- }
- }
-
/*package*/ void handleSetA2dpSinkConnectionState(@BluetoothProfile.BtProfileState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
final int intState = (state == BluetoothA2dp.STATE_CONNECTED)
@@ -482,7 +481,7 @@
state, btDeviceInfo, delay);
}
- /*package*/ void handleSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state,
+ /*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
@@ -710,6 +709,26 @@
(BtHelper.BluetoothA2dpDeviceInfo) msg.obj);
}
break;
+ case MSG_DISCONNECT_A2DP:
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.disconnectA2dp();
+ }
+ break;
+ case MSG_DISCONNECT_A2DP_SINK:
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.disconnectA2dpSink();
+ }
+ break;
+ case MSG_DISCONNECT_HEARING_AID:
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.disconnectHearingAid();
+ }
+ break;
+ case MSG_DISCONNECT_HEADSET:
+ synchronized (mDeviceStateLock) {
+ mBtHelper.disconnectHeadset();
+ }
+ break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -745,6 +764,10 @@
private static final int MSG_I_DISCONNECT_BT_SCO = 16;
private static final int MSG_TOGGLE_HDMI = 17;
private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18;
+ private static final int MSG_DISCONNECT_A2DP = 19;
+ private static final int MSG_DISCONNECT_A2DP_SINK = 20;
+ private static final int MSG_DISCONNECT_HEARING_AID = 21;
+ private static final int MSG_DISCONNECT_HEADSET = 22;
private static boolean isMessageHandledUnderWakelock(int msgId) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 11fdc8f..37f0496 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -859,7 +859,7 @@
// also checks whether media routing if affected by a dynamic policy
if (((device == musicDevice) || mDeviceBroker.isInCommunication())
&& (device == devices) && !mDeviceBroker.hasMediaDynamicPolicy()) {
- mDeviceBroker.broadcastBecomingNoisy();
+ mDeviceBroker.postBroadcastBecomingNoisy();
delay = 1000;
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index de63d0e..a6643d4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5120,7 +5120,7 @@
if (mUserSwitchedReceived) {
// attempt to stop music playback for background user except on first user
// switch (i.e. first boot)
- mDeviceBroker.broadcastBecomingNoisy();
+ mDeviceBroker.postBroadcastBecomingNoisy();
}
mUserSwitchedReceived = true;
// the current audio focus owner is no longer valid
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index b63af8a..e918997 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -374,10 +374,10 @@
}
/*package*/ synchronized void disconnectAllBluetoothProfiles() {
- mDeviceBroker.handleDisconnectA2dp();
- mDeviceBroker.handleDisconnectA2dpSink();
- disconnectHeadset();
- mDeviceBroker.handleDisconnectHearingAid();
+ mDeviceBroker.postDisconnectA2dp();
+ mDeviceBroker.postDisconnectA2dpSink();
+ mDeviceBroker.postDisconnectHeadset();
+ mDeviceBroker.postDisconnectHearingAid();
}
/*package*/ synchronized void resetBluetoothSco() {
@@ -388,9 +388,14 @@
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
+ /*package*/ synchronized void disconnectHeadset() {
+ setBtScoActiveDevice(null);
+ mBluetoothHeadset = null;
+ }
+
//----------------------------------------------------------------------
private void broadcastScoConnectionState(int state) {
- mDeviceBroker.broadcastScoConnectionState(state);
+ mDeviceBroker.postBroadcastScoConnectionState(state);
}
private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
@@ -487,7 +492,7 @@
btDevice = deviceList.get(0);
final @BluetoothProfile.BtProfileState int state =
proxy.getConnectionState(btDevice);
- mDeviceBroker.handleSetA2dpSourceConnectionState(
+ mDeviceBroker.postSetA2dpSourceConnectionState(
state, new BluetoothA2dpDeviceInfo(btDevice));
}
break;
@@ -558,19 +563,19 @@
switch (profile) {
case BluetoothProfile.A2DP:
- mDeviceBroker.handleDisconnectA2dp();
+ mDeviceBroker.postDisconnectA2dp();
break;
case BluetoothProfile.A2DP_SINK:
- mDeviceBroker.handleDisconnectA2dpSink();
+ mDeviceBroker.postDisconnectA2dpSink();
break;
case BluetoothProfile.HEADSET:
- disconnectHeadset();
+ mDeviceBroker.postDisconnectHeadset();
break;
case BluetoothProfile.HEARING_AID:
- mDeviceBroker.handleDisconnectHearingAid();
+ mDeviceBroker.postDisconnectHearingAid();
break;
default:
@@ -579,11 +584,6 @@
}
};
- private void disconnectHeadset() {
- setBtScoActiveDevice(null);
- mBluetoothHeadset = null;
- }
-
//----------------------------------------------------------------------
private class ScoClient implements IBinder.DeathRecipient {
private IBinder mCb; // To be notified of client's death
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 90342ee..017503a 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -168,6 +168,7 @@
String opPackageName, int cookie, int callingUid, int callingPid,
int callingUserId) {
checkPermission(USE_BIOMETRIC_INTERNAL);
+ updateActiveGroup(groupId, opPackageName);
final boolean restricted = true; // BiometricPrompt is always restricted
final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 62947c7..9e0a1c0c 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -192,6 +192,7 @@
IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
int cookie, int callingUid, int callingPid, int callingUserId) {
checkPermission(MANAGE_BIOMETRIC);
+ updateActiveGroup(groupId, opPackageName);
final boolean restricted = true; // BiometricPrompt is always restricted
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 727cf0e..126beef 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -34,6 +34,7 @@
import android.hardware.SensorManager;
import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.ColorDisplayManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayedContentSample;
@@ -57,7 +58,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.ColorDisplayController;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.RingBuffer;
@@ -382,9 +382,8 @@
return;
}
- builder.setNightMode(mInjector.isNightModeActive(mContext, UserHandle.USER_CURRENT));
- builder.setColorTemperature(mInjector.getColorTemperature(mContext,
- UserHandle.USER_CURRENT));
+ builder.setNightMode(mInjector.isNightDisplayActivated(mContext));
+ builder.setColorTemperature(mInjector.getNightDisplayColorTemperature(mContext));
if (mColorSamplingEnabled) {
DisplayedContentSample sample = mInjector.sampleColor(mNoFramesToSample);
@@ -1096,12 +1095,13 @@
return context.getSystemService(PowerManager.class).isInteractive();
}
- public int getColorTemperature(Context context, int userId) {
- return new ColorDisplayController(context, userId).getColorTemperature();
+ public int getNightDisplayColorTemperature(Context context) {
+ return context.getSystemService(ColorDisplayManager.class)
+ .getNightDisplayColorTemperature();
}
- public boolean isNightModeActive(Context context, int userId) {
- return new ColorDisplayController(context, userId).isActivated();
+ public boolean isNightDisplayActivated(Context context) {
+ return context.getSystemService(ColorDisplayManager.class).isNightDisplayActivated();
}
public DisplayedContentSample sampleColor(int noFramesToSample) {
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index b3a1a06..9cb6eee 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -72,7 +72,6 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.ColorDisplayController;
import com.android.internal.util.DumpUtils;
import com.android.server.DisplayThread;
import com.android.server.SystemService;
@@ -115,6 +114,7 @@
private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 0;
private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 1;
private static final int MSG_APPLY_GLOBAL_SATURATION = 2;
+ private static final int MSG_APPLY_DISPLAY_WHITE_BALANCE = 3;
/**
* Return value if a setting has not been set.
@@ -323,8 +323,7 @@
}
private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(
- SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
+ final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
if (displayToken == null) {
return null;
}
@@ -448,7 +447,6 @@
private ContentObserver mUserSetupObserver;
private boolean mBootCompleted;
- private ColorDisplayController mNightDisplayController;
private ContentObserver mContentObserver;
private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
@@ -547,8 +545,6 @@
private void setUp() {
Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
- mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
-
// Listen for external changes to any of the settings.
if (mContentObserver == null) {
mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
@@ -586,7 +582,7 @@
getNightDisplayCustomEndTimeInternal().getLocalTime());
break;
case System.DISPLAY_COLOR_MODE:
- onDisplayColorModeChanged(mNightDisplayController.getColorMode());
+ onDisplayColorModeChanged(getColorModeInternal());
break;
case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
onAccessibilityInversionChanged();
@@ -634,7 +630,7 @@
// Set the color mode, if valid, and immediately apply the updated tint matrix based on the
// existing activated state. This ensures consistency of tint across the color mode change.
- onDisplayColorModeChanged(mNightDisplayController.getColorMode());
+ onDisplayColorModeChanged(getColorModeInternal());
if (mNightDisplayTintController.isAvailable(getContext())) {
// Reset the activated state.
@@ -667,10 +663,6 @@
getContext().getContentResolver().unregisterContentObserver(mContentObserver);
- if (mNightDisplayController != null) {
- mNightDisplayController = null;
- }
-
if (mNightDisplayTintController.isAvailable(getContext())) {
if (mNightDisplayAutoMode != null) {
mNightDisplayAutoMode.onStop();
@@ -740,7 +732,7 @@
}
private void onAccessibilityActivated() {
- onDisplayColorModeChanged(mNightDisplayController.getColorMode());
+ onDisplayColorModeChanged(getColorModeInternal());
}
/**
@@ -871,7 +863,7 @@
// If disabled, clear the tint. If enabled, do nothing more here and let the next
// temperature update set the correct tint.
if (!activated) {
- applyTint(mDisplayWhiteBalanceTintController, false);
+ mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
}
}
@@ -1003,8 +995,7 @@
mCurrentUser);
}
- private @ColorMode
- int getColorModeInternal() {
+ private @ColorMode int getColorModeInternal() {
final ContentResolver cr = getContext().getContentResolver();
if (Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
0, mCurrentUser) == 1
@@ -1543,7 +1534,7 @@
mDisplayWhiteBalanceTintController.setMatrix(cct);
if (mDisplayWhiteBalanceTintController.isActivated()) {
- applyTint(mDisplayWhiteBalanceTintController, false);
+ mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
return true;
}
return false;
@@ -1603,6 +1594,9 @@
case MSG_APPLY_NIGHT_DISPLAY_ANIMATED:
applyTint(mNightDisplayTintController, false);
break;
+ case MSG_APPLY_DISPLAY_WHITE_BALANCE:
+ applyTint(mDisplayWhiteBalanceTintController, false);
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index f2c539c..2f277e4 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -27,6 +27,7 @@
import android.opengl.EGLSurface;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
+import android.os.IBinder;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -474,8 +475,14 @@
final SurfaceTexture st = new SurfaceTexture(mTexNames[0]);
final Surface s = new Surface(st);
try {
- SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
- SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), s);
+ final IBinder token = SurfaceControl.getInternalDisplayToken();
+ if (token == null) {
+ Slog.e(TAG,
+ "Failed to take screenshot because internal display is disconnected");
+ return false;
+ }
+
+ SurfaceControl.screenshot(token, s);
st.updateTexImage();
st.getTransformMatrix(mTexMatrix);
} finally {
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index b1b7d3c..ef92401 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -87,7 +87,7 @@
* Map of level -> color transformation matrix.
*/
@GuardedBy("mColorMatrix")
- private final SparseArray<float[]> mColorMatrix = new SparseArray<>(3);
+ private final SparseArray<float[]> mColorMatrix = new SparseArray<>(5);
/**
* Temporary matrix used internally by {@link #computeColorMatrixLocked()}.
*/
@@ -148,6 +148,21 @@
}
/**
+ * Sets the current Daltonization mode. This adjusts the color space to correct for or simulate
+ * various types of color blindness.
+ *
+ * @param mode the new Daltonization mode, or -1 to disable
+ */
+ public void setDaltonizerMode(int mode) {
+ synchronized (mDaltonizerModeLock) {
+ if (mDaltonizerMode != mode) {
+ mDaltonizerMode = mode;
+ applyDaltonizerMode(mode);
+ }
+ }
+ }
+
+ /**
* Returns the composition of all current color matrices, or {@code null} if there are none.
*/
@GuardedBy("mColorMatrix")
@@ -167,30 +182,6 @@
}
/**
- * Returns the current Daltonization mode.
- */
- public int getDaltonizerMode() {
- synchronized (mDaltonizerModeLock) {
- return mDaltonizerMode;
- }
- }
-
- /**
- * Sets the current Daltonization mode. This adjusts the color space to correct for or simulate
- * various types of color blindness.
- *
- * @param mode the new Daltonization mode, or -1 to disable
- */
- public void setDaltonizerMode(int mode) {
- synchronized (mDaltonizerModeLock) {
- if (mDaltonizerMode != mode) {
- mDaltonizerMode = mode;
- applyDaltonizerMode(mode);
- }
- }
- }
-
- /**
* Propagates the provided color transformation matrix to the SurfaceFlinger.
*/
private static void applyColorMatrix(float[] m) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 16d82df..28f21f63 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -17,12 +17,8 @@
package com.android.server.display;
import android.app.ActivityThread;
-import android.content.res.Resources;
-import com.android.server.LocalServices;
-import com.android.server.lights.Light;
-import com.android.server.lights.LightsManager;
-
import android.content.Context;
+import android.content.res.Resources;
import android.hardware.sidekick.SidekickInternal;
import android.os.Build;
import android.os.Handler;
@@ -31,6 +27,7 @@
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.Trace;
+import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -38,6 +35,11 @@
import android.view.DisplayEventReceiver;
import android.view.Surface;
import android.view.SurfaceControl;
+
+import com.android.server.LocalServices;
+import com.android.server.lights.Light;
+import com.android.server.lights.LightsManager;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -58,13 +60,9 @@
private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
- private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
- SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
- SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
- };
+ private final LongSparseArray<LocalDisplayDevice> mDevices =
+ new LongSparseArray<LocalDisplayDevice>();
- private final SparseArray<LocalDisplayDevice> mDevices =
- new SparseArray<LocalDisplayDevice>();
@SuppressWarnings("unused") // Becomes active at instantiation time.
private HotplugDisplayEventReceiver mHotplugReceiver;
@@ -80,28 +78,26 @@
mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
- for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
- tryConnectDisplayLocked(builtInDisplayId);
+ for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) {
+ tryConnectDisplayLocked(physicalDisplayId);
}
}
- private void tryConnectDisplayLocked(int builtInDisplayId) {
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
+ private void tryConnectDisplayLocked(long physicalDisplayId) {
+ final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(physicalDisplayId);
if (displayToken != null) {
SurfaceControl.PhysicalDisplayInfo[] configs =
SurfaceControl.getDisplayConfigs(displayToken);
if (configs == null) {
// There are no valid configs for this device, so we can't use it
- Slog.w(TAG, "No valid configs found for display device " +
- builtInDisplayId);
+ Slog.w(TAG, "No valid configs found for display device " + physicalDisplayId);
return;
}
int activeConfig = SurfaceControl.getActiveConfig(displayToken);
if (activeConfig < 0) {
// There is no active config, and for now we don't have the
// policy to set one.
- Slog.w(TAG, "No active config found for display device " +
- builtInDisplayId);
+ Slog.w(TAG, "No active config found for display device " + physicalDisplayId);
return;
}
int activeColorMode = SurfaceControl.getActiveColorMode(displayToken);
@@ -110,16 +106,17 @@
// configuration pass we'll go ahead and set it to whatever it was set to last (or
// COLOR_MODE_NATIVE if this is the first configuration).
Slog.w(TAG, "Unable to get active color mode for display device " +
- builtInDisplayId);
+ physicalDisplayId);
activeColorMode = Display.COLOR_MODE_INVALID;
}
int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
- LocalDisplayDevice device = mDevices.get(builtInDisplayId);
+ LocalDisplayDevice device = mDevices.get(physicalDisplayId);
if (device == null) {
// Display was added.
- device = new LocalDisplayDevice(displayToken, builtInDisplayId,
- configs, activeConfig, colorModes, activeColorMode);
- mDevices.put(builtInDisplayId, device);
+ final boolean isInternal = mDevices.size() == 0;
+ device = new LocalDisplayDevice(displayToken, physicalDisplayId,
+ configs, activeConfig, colorModes, activeColorMode, isInternal);
+ mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
} else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig,
colorModes, activeColorMode)) {
@@ -133,11 +130,11 @@
}
}
- private void tryDisconnectDisplayLocked(int builtInDisplayId) {
- LocalDisplayDevice device = mDevices.get(builtInDisplayId);
+ private void tryDisconnectDisplayLocked(long physicalDisplayId) {
+ LocalDisplayDevice device = mDevices.get(physicalDisplayId);
if (device != null) {
// Display was removed.
- mDevices.remove(builtInDisplayId);
+ mDevices.remove(physicalDisplayId);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
}
}
@@ -158,10 +155,11 @@
}
private final class LocalDisplayDevice extends DisplayDevice {
- private final int mBuiltInDisplayId;
+ private final long mPhysicalDisplayId;
private final Light mBacklight;
private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>();
+ private final boolean mIsInternal;
private DisplayDeviceInfo mInfo;
private boolean mHavePendingChanges;
@@ -179,16 +177,17 @@
private SurfaceControl.PhysicalDisplayInfo mDisplayInfos[];
- public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
+ LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
- int[] colorModes, int activeColorMode) {
- super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + builtInDisplayId);
- mBuiltInDisplayId = builtInDisplayId;
+ int[] colorModes, int activeColorMode, boolean isInternal) {
+ super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
+ mPhysicalDisplayId = physicalDisplayId;
+ mIsInternal = isInternal;
updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo,
colorModes, activeColorMode);
updateColorModesLocked(colorModes, activeColorMode);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
- if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
+ if (mIsInternal) {
LightsManager lights = LocalServices.getService(LightsManager.class);
mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
} else {
@@ -392,7 +391,7 @@
}
final Resources res = getOverlayContext().getResources();
- if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
+ if (mIsInternal) {
mInfo.name = res.getString(
com.android.internal.R.string.display_manager_built_in_display_name);
mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
@@ -455,7 +454,7 @@
final boolean stateChanged = (mState != state);
final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null;
if (stateChanged || brightnessChanged) {
- final int displayId = mBuiltInDisplayId;
+ final long physicalDisplayId = mPhysicalDisplayId;
final IBinder token = getDisplayTokenLocked();
final int oldState = mState;
@@ -519,7 +518,7 @@
private void setVrMode(boolean isVrEnabled) {
if (DEBUG) {
Slog.d(TAG, "setVrMode("
- + "id=" + displayId
+ + "id=" + physicalDisplayId
+ ", state=" + Display.stateToString(state) + ")");
}
mBacklight.setVrMode(isVrEnabled);
@@ -528,7 +527,7 @@
private void setDisplayState(int state) {
if (DEBUG) {
Slog.d(TAG, "setDisplayState("
- + "id=" + displayId
+ + "id=" + physicalDisplayId
+ ", state=" + Display.stateToString(state) + ")");
}
@@ -546,7 +545,7 @@
}
final int mode = getPowerModeForState(state);
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState("
- + "id=" + displayId
+ + "id=" + physicalDisplayId
+ ", state=" + Display.stateToString(state) + ")");
try {
SurfaceControl.setDisplayPowerMode(token, mode);
@@ -571,11 +570,12 @@
private void setDisplayBrightness(int brightness) {
if (DEBUG) {
Slog.d(TAG, "setDisplayBrightness("
- + "id=" + displayId + ", brightness=" + brightness + ")");
+ + "id=" + physicalDisplayId
+ + ", brightness=" + brightness + ")");
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
- + "id=" + displayId + ", brightness=" + brightness + ")");
+ + "id=" + physicalDisplayId + ", brightness=" + brightness + ")");
try {
mBacklight.setBrightness(brightness);
Trace.traceCounter(Trace.TRACE_TAG_POWER,
@@ -646,7 +646,7 @@
@Override
public void dumpLocked(PrintWriter pw) {
super.dumpLocked(pw);
- pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
+ pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
pw.println("mActivePhysIndex=" + mActivePhysIndex);
pw.println("mActiveModeId=" + mActiveModeId);
pw.println("mActiveColorMode=" + mActiveColorMode);
@@ -731,12 +731,12 @@
}
@Override
- public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
+ public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
synchronized (getSyncRoot()) {
if (connected) {
- tryConnectDisplayLocked(builtInDisplayId);
+ tryConnectDisplayLocked(physicalDisplayId);
} else {
- tryDisconnectDisplayLocked(builtInDisplayId);
+ tryDisconnectDisplayLocked(physicalDisplayId);
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 7376ed2..072238e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2412,7 +2412,8 @@
void wakeUp() {
assertRunOnServiceThread();
mWakeUpMessageReceived = true;
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.hdmi:WAKE");
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_HDMI,
+ "android.server.hdmi:WAKE");
// PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
// the intent, the sequence will continue at onWakeUp().
}
@@ -2637,7 +2638,8 @@
playback().sendStandby(0 /* unused */);
}
} else if (isPowerStandbyOrTransient() && !isStandbyModeOn) {
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.hdmi:WAKE");
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_HDMI,
+ "android.server.hdmi:WAKE");
if (playback() != null) {
oneTouchPlay(new IHdmiControlCallback.Stub() {
@Override
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 532aa01..2bfb31f 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -286,6 +286,46 @@
}
/**
+ * Sets whether the default service should be used.
+ *
+ * <p>Typically used during CTS tests to make sure only the default service doesn't interfere
+ * with the test results.
+ *
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ public final void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
+ Slog.i(mTag, "setDefaultServiceEnabled() for userId " + userId + ": " + enabled);
+ enforceCallingPermissionForManagement();
+
+ synchronized (mLock) {
+ 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);
+ }
+ }
+
+ /**
+ * Checks whether the default service should be used.
+ *
+ * <p>Typically used during CTS tests to make sure only the default service doesn't interfere
+ * with the test results.
+ *
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ public final boolean isDefaultServiceEnabled(@UserIdInt int userId) {
+ enforceCallingPermissionForManagement();
+
+ synchronized (mLock) {
+ return mServiceNameResolver.isDefaultServiceEnabled(userId);
+ }
+ }
+
+ /**
* Gets the maximum time the service implementation can be changed.
*
* @throws UnsupportedOperationException if subclass doesn't override it.
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 7f198ac..cf84e22 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -27,6 +27,7 @@
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -61,6 +62,15 @@
private final SparseArray<String> mTemporaryServiceNames = new SparseArray<>();
/**
+ * Map of default services that have been disabled by
+ * {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ */
+ @GuardedBy("mLock")
+ private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray();
+
+ /**
* When the temporary service will expire (and reset back to the default).
*/
@GuardedBy("mLock")
@@ -99,12 +109,18 @@
final String temporaryName = mTemporaryServiceNames.get(userId);
if (temporaryName != null) {
// Always log it, as it should only be used on CTS or during development
- Slog.w(TAG, "getComponentName(): using temporary name " + temporaryName
+ Slog.w(TAG, "getServiceName(): using temporary name " + temporaryName
+ " for user " + userId);
return temporaryName;
- } else {
- return getDefaultServiceName(userId);
}
+ final boolean disabled = mDefaultServicesDisabled.get(userId);
+ if (disabled) {
+ // Always log it, as it should only be used on CTS or during development
+ Slog.w(TAG, "getServiceName(): temporary name not set and default disabled for "
+ + "user " + userId);
+ return null;
+ }
+ return getDefaultServiceName(userId);
}
}
@@ -158,6 +174,24 @@
}
@Override
+ public void setDefaultServiceEnabled(int userId, boolean enabled) {
+ synchronized (mLock) {
+ if (enabled) {
+ mDefaultServicesDisabled.removeAt(userId);
+ } else {
+ mDefaultServicesDisabled.put(userId, true);
+ }
+ }
+ }
+
+ @Override
+ public boolean isDefaultServiceEnabled(int userId) {
+ synchronized (mLock) {
+ return mDefaultServicesDisabled.get(userId);
+ }
+ }
+
+ @Override
public String toString() {
return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]";
}
@@ -168,6 +202,7 @@
synchronized (mLock) {
pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
pw.print(", numberTemps="); pw.print(mTemporaryServiceNames.size());
+ pw.print(", enabledDefaults="); pw.print(mDefaultServicesDisabled.size());
}
}
@@ -181,7 +216,9 @@
final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print("), ");
}
- pw.print("defaultName="); pw.println(getDefaultServiceName(userId));
+ pw.print("defaultName="); pw.print(getDefaultServiceName(userId));
+ final boolean disabled = mDefaultServicesDisabled.get(userId);
+ pw.println(disabled ? " (disabled)" : " (enabled)");
}
}
diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java
index bc11ff3..5b60413 100644
--- a/services/core/java/com/android/server/infra/ServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java
@@ -108,6 +108,36 @@
}
/**
+ * Sets whether the default service should be used when the temporary service is not set.
+ *
+ * <p>Typically used during CTS tests to make sure only the default service doesn't interfere
+ * with the test results.
+ *
+ * @param userId user handle
+ * @param enabled whether the default service should be used when the temporary service is not
+ * set
+ *
+ * @throws UnsupportedOperationException if not implemented.
+ */
+ default void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
+ throw new UnsupportedOperationException("changing default service not supported");
+ }
+
+ /**
+ * Checks whether the default service should be used when the temporary service is not set.
+ *
+ * <p>Typically used during CTS tests to make sure only the default service doesn't interfere
+ * with the test results.
+ *
+ * @param userId user handle
+ *
+ * @throws UnsupportedOperationException if not implemented.
+ */
+ default boolean isDefaultServiceEnabled(@UserIdInt int userId) {
+ throw new UnsupportedOperationException("checking default service not supported");
+ }
+
+ /**
* Dumps the generic info in just one line (without calling {@code println}.
*/
// TODO(b/117779333): support proto
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 34c8786..afe3473 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -183,12 +183,17 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" service=" + mServiceWatcher);
+ pw.println(" service=" + mServiceWatcher);
+ synchronized (mProviderPackagesLock) {
+ if (mProviderPackages.size() > 1) {
+ pw.println(" additional packages=" + mProviderPackages);
+ }
+ }
mServiceWatcher.runOnBinderBlocking(binder -> {
try {
TransferPipe.dumpAsync(binder, fd, args);
} catch (IOException | RemoteException e) {
- pw.println(" failed to dump location provider");
+ pw.println(" <failed to dump location provider: " + e + ">");
}
return null;
}, null);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index efafdfa..c2a75ab 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -611,18 +611,43 @@
}
}
- public boolean snapshotAppData(String pkg, @UserIdInt int userId, int storageFlags)
+ /**
+ * Snapshots user data of the given package.
+ *
+ * @param pkg name of the package to snapshot user data for.
+ * @param userId id of the user whose data to snapshot.
+ * @param storageFlags flags controlling which data (CE or DE) to snapshot.
+ *
+ * @return inode of the snapshot of users CE package data, or {@code 0} if a remote calls
+ * shouldn't be continued. See {@link #checkBeforeRemote}.
+ *
+ * @throws InstallerException if failed to snapshot user data.
+ */
+ public long snapshotAppData(String pkg, @UserIdInt int userId, int storageFlags)
throws InstallerException {
- if (!checkBeforeRemote()) return false;
+ if (!checkBeforeRemote()) return 0;
try {
- mInstalld.snapshotAppData(null, pkg, userId, storageFlags);
- return true;
+ return mInstalld.snapshotAppData(null, pkg, userId, storageFlags);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
+ /**
+ * Restores user data snapshot of the given package.
+ *
+ * @param pkg name of the package to restore user data for.
+ * @param appId id of the package to restore user data for.
+ * @param ceDataInode inode of CE user data folder of this app.
+ * @param userId id of the user whose data to restore.
+ * @param storageFlags flags controlling which data (CE or DE) to restore.
+ *
+ * @return {@code true} if user data restore was successful, or {@code false} if a remote call
+ * shouldn't be continued. See {@link #checkBeforeRemote}.
+ *
+ * @throws InstallerException if failed to restore user data.
+ */
public boolean restoreAppDataSnapshot(String pkg, @AppIdInt int appId, long ceDataInode,
String seInfo, @UserIdInt int userId, int storageFlags) throws InstallerException {
if (!checkBeforeRemote()) return false;
@@ -636,6 +661,31 @@
}
}
+ /**
+ * Deletes user data snapshot of the given package.
+ *
+ * @param pkg name of the package to delete user data snapshot for.
+ * @param userId id of the user whose user data snapshot to delete.
+ * @param ceSnapshotInode inode of CE user data snapshot.
+ * @param storageFlags flags controlling which user data snapshot (CE or DE) to delete.
+ *
+ * @return {@code true} if user data snapshot was successfully deleted, or {@code false} if a
+ * remote call shouldn't be continued. See {@link #checkBeforeRemote}.
+ *
+ * @throws InstallerException if failed to delete user data snapshot.
+ */
+ public boolean destroyAppDataSnapshot(String pkg, @UserIdInt int userId, long ceSnapshotInode,
+ int storageFlags) throws InstallerException {
+ if (!checkBeforeRemote()) return false;
+
+ try {
+ mInstalld.destroyAppDataSnapshot(null, pkg, userId, ceSnapshotInode, storageFlags);
+ return true;
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
private static void assertValidInstructionSet(String instructionSet)
throws InstallerException {
for (String abi : Build.SUPPORTED_ABIS) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d06fc51..1b71904 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -44,7 +44,6 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.apex.IApexService;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -80,7 +79,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
@@ -1084,6 +1082,11 @@
dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
return;
}
+ if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+ "APEX packages can only be installed using staged sessions.");
+ }
final PackageManagerService.ActiveInstallSession committingSession =
makeSessionActiveLocked();
if (committingSession == null) {
@@ -1101,12 +1104,6 @@
final PackageManagerService.ActiveInstallSession activeSession =
session.makeSessionActiveLocked();
if (activeSession != null) {
- if ((activeSession.getSessionParams().installFlags
- & PackageManager.INSTALL_APEX) != 0) {
- throw new PackageManagerException(
- PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
- "Atomic install is not supported for APEX packages.");
- }
childSessions.add(activeSession);
}
} catch (PackageManagerException e) {
@@ -1124,27 +1121,7 @@
}
mPm.installStage(childSessions);
} else {
- if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
- commitApexLocked();
- } else {
- mPm.installStage(committingSession);
- }
- }
- }
-
- @GuardedBy("mLock")
- private void commitApexLocked() throws PackageManagerException {
- try {
- IApexService apex = IApexService.Stub.asInterface(
- ServiceManager.getService("apexservice"));
- apex.stagePackage(mResolvedBaseFile.toString());
- } catch (Throwable e) {
- // Convert all exceptions into package manager exceptions as only those are handled
- // in the code above
- throw new PackageManagerException(e);
- } finally {
- destroyInternal();
- dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "APEX installed", null);
+ mPm.installStage(committingSession);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 32dc988..eaedec5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -195,7 +195,6 @@
import android.content.pm.Signature;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
-import android.content.pm.UsesPermissionInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.pm.VersionedPackage;
@@ -11313,26 +11312,6 @@
}
}
}
-
- // Check permission usage info requirements.
- if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
- for (UsesPermissionInfo upi : pkg.usesPermissionInfos) {
- if (!mPermissionManager.isPermissionUsageInfoRequired(upi.getPermission())) {
- continue;
- }
- if (upi.getDataSentOffDevice() == UsesPermissionInfo.USAGE_UNDEFINED
- || upi.getDataSharedWithThirdParty()
- == UsesPermissionInfo.USAGE_UNDEFINED
- || upi.getDataUsedForMonetization()
- == UsesPermissionInfo.USAGE_UNDEFINED
- || upi.getDataRetention() == UsesPermissionInfo.RETENTION_UNDEFINED) {
- // STOPSHIP: Make this throw
- Slog.e(TAG, "Package " + pkg.packageName + " does not provide usage "
- + "information for permission " + upi.getPermission()
- + ". This will be a fatal error in Q.");
- }
- }
- }
}
}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 2213901..ee6995b 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -228,8 +228,11 @@
continue;
}
- mDexLogger.recordDex(loaderUserId, dexPath, searchResult.mOwningPackageName,
- loadingAppInfo.packageName);
+ if (!primaryOrSplit) {
+ // Record loading of a DEX file from an app data directory.
+ mDexLogger.recordDex(loaderUserId, dexPath, searchResult.mOwningPackageName,
+ loadingAppInfo.packageName);
+ }
if (classLoaderContexts != null) {
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 173d9a0..1957eb8 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -105,8 +105,6 @@
*/
private boolean perUser;
- boolean usageInfoRequired;
-
public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) {
name = _name;
sourcePackageName = _sourcePackageName;
@@ -375,7 +373,6 @@
}
if (bp.perm == p) {
bp.protectionLevel = p.info.protectionLevel;
- bp.usageInfoRequired = p.info.usageInfoRequired;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
Log.d(TAG, " Permissions: " + r);
@@ -455,7 +452,6 @@
permissionInfo.packageName = sourcePackageName;
permissionInfo.nonLocalizedLabel = name;
permissionInfo.protectionLevel = protectionLevel;
- permissionInfo.usageInfoRequired = usageInfoRequired;
return permissionInfo;
}
@@ -484,7 +480,6 @@
bp.protectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
- bp.usageInfoRequired = readInt(parser, null, "usageInfoRequired", 0) != 0;
if (dynamic) {
final PermissionInfo pi = new PermissionInfo();
pi.packageName = sourcePackage.intern();
@@ -492,7 +487,6 @@
pi.icon = readInt(parser, null, "icon", 0);
pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
pi.protectionLevel = bp.protectionLevel;
- pi.usageInfoRequired = bp.usageInfoRequired;
bp.pendingPermissionInfo = pi;
}
out.put(bp.name, bp);
@@ -525,7 +519,6 @@
if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
serializer.attribute(null, "protection", Integer.toString(protectionLevel));
}
- serializer.attribute(null, "usageInfoRequired", usageInfoRequired ? "1" : "0");
if (type == BasePermission.TYPE_DYNAMIC) {
final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo;
if (pi != null) {
@@ -562,7 +555,6 @@
if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
// We'll take care of setting this one.
if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
- if (pi1.usageInfoRequired != pi2.usageInfoRequired) return false;
// These are not currently stored in settings.
//if (!compareStrings(pi1.group, pi2.group)) return false;
//if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
@@ -610,8 +602,6 @@
pw.print(" enforced=");
pw.println(readEnforced);
}
- pw.print(" usageInfoRequired=");
- pw.println(usageInfoRequired);
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index 189d0f4..f4979746 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -181,9 +181,4 @@
/** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
-
- /**
- * Returns {@code true} if {@code permName} has {@code usageInfoRequired} set.
- */
- public abstract boolean isPermissionUsageInfoRequired(@NonNull String permName);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index a4413f9..38940d6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2737,12 +2737,5 @@
return mSettings.getPermissionLocked(permName);
}
}
- @Override
- public boolean isPermissionUsageInfoRequired(String permName) {
- synchronized (PermissionManagerService.this.mLock) {
- BasePermission bp = mSettings.getPermissionLocked(permName);
- return bp != null && bp.usageInfoRequired;
- }
- }
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0796a9c..b00193f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -148,6 +148,7 @@
import android.os.IDeviceIdleController;
import android.os.Message;
import android.os.PowerManager;
+import android.os.PowerManager.WakeReason;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
@@ -189,6 +190,7 @@
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
+import android.view.WindowManagerPolicyConstants;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
@@ -809,7 +811,7 @@
performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false,
"Wake Up");
wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
- "android.policy:GESTURE");
+ PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE");
}
}
}
@@ -3527,7 +3529,7 @@
if (lidOpen) {
wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch,
- "android.policy:LID");
+ PowerManager.WAKE_REASON_LID, "android.policy:LID");
} else if (!mLidControlsSleep) {
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
@@ -3550,7 +3552,7 @@
intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
}
wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromCameraLens,
- "android.policy:CAMERA_COVER");
+ PowerManager.WAKE_REASON_CAMERA_LAUNCH, "android.policy:CAMERA_COVER");
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
}
mCameraLensCoverState = lensCoverState;
@@ -3679,7 +3681,8 @@
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {
if (isWakeKey) {
- wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
+ wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey,
+ PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY");
}
return result;
}
@@ -4025,7 +4028,8 @@
}
if (isWakeKey) {
- wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
+ wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey,
+ PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY");
}
return result;
@@ -4125,7 +4129,7 @@
public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
if ((policyFlags & FLAG_WAKE) != 0) {
if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
- "android.policy:MOTION")) {
+ PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
return 0;
}
}
@@ -4139,7 +4143,7 @@
// wake up in this case.
if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming,
- "android.policy:MOTION");
+ PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
}
return 0;
@@ -4371,7 +4375,10 @@
// Called on the PowerManager's Notifier thread.
@Override
public void startedGoingToSleep(int why) {
- if (DEBUG_WAKEUP) Slog.i(TAG, "Started going to sleep... (why=" + why + ")");
+ if (DEBUG_WAKEUP) {
+ Slog.i(TAG, "Started going to sleep... (why="
+ + WindowManagerPolicyConstants.offReasonToString(why) + ")");
+ }
mGoingToSleep = true;
mRequestedOrGoingToSleep = true;
@@ -4385,7 +4392,10 @@
@Override
public void finishedGoingToSleep(int why) {
EventLog.writeEvent(70000, 0);
- if (DEBUG_WAKEUP) Slog.i(TAG, "Finished going to sleep... (why=" + why + ")");
+ if (DEBUG_WAKEUP) {
+ Slog.i(TAG, "Finished going to sleep... (why="
+ + WindowManagerPolicyConstants.offReasonToString(why) + ")");
+ }
MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
mGoingToSleep = false;
@@ -4409,9 +4419,12 @@
// Called on the PowerManager's Notifier thread.
@Override
- public void startedWakingUp() {
+ public void startedWakingUp(@OnReason int why) {
EventLog.writeEvent(70000, 1);
- if (DEBUG_WAKEUP) Slog.i(TAG, "Started waking up...");
+ if (DEBUG_WAKEUP) {
+ Slog.i(TAG, "Started waking up... (why="
+ + WindowManagerPolicyConstants.onReasonToString(why) + ")");
+ }
mDefaultDisplayPolicy.setAwake(true);
@@ -4432,8 +4445,11 @@
// Called on the PowerManager's Notifier thread.
@Override
- public void finishedWakingUp() {
- if (DEBUG_WAKEUP) Slog.i(TAG, "Finished waking up...");
+ public void finishedWakingUp(@OnReason int why) {
+ if (DEBUG_WAKEUP) {
+ Slog.i(TAG, "Finished waking up... (why="
+ + WindowManagerPolicyConstants.onReasonToString(why) + ")");
+ }
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onFinishedWakingUp();
@@ -4441,10 +4457,12 @@
}
private void wakeUpFromPowerKey(long eventTime) {
- wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, "android.policy:POWER");
+ wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
+ PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER");
}
- private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {
+ private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason,
+ String details) {
final boolean theaterModeEnabled = isTheaterModeEnabled();
if (!wakeInTheaterMode && theaterModeEnabled) {
return false;
@@ -4455,7 +4473,7 @@
Settings.Global.THEATER_MODE_ON, 0);
}
- mPowerManager.wakeUp(wakeTime, reason);
+ mPowerManager.wakeUp(wakeTime, reason, details);
return true;
}
@@ -4786,7 +4804,7 @@
mKeyguardDelegate.onBootCompleted();
}
}
- startedWakingUp();
+ startedWakingUp(ON_BECAUSE_OF_UNKNOWN);
screenTurningOn(null);
screenTurnedOn();
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e18cd17..d1bd102 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1070,12 +1070,12 @@
/**
* Called when the device has started waking up.
*/
- public void startedWakingUp();
+ void startedWakingUp(@OnReason int reason);
/**
* Called when the device has finished waking up.
*/
- public void finishedWakingUp();
+ void finishedWakingUp(@OnReason int reason);
/**
* Called when the device has started going to sleep.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index c3f20aa..1a82858 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -36,6 +36,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.PowerManager.WakeReason;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
@@ -48,6 +49,7 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.StatsLog;
+import android.view.WindowManagerPolicyConstants.OnReason;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
@@ -136,6 +138,7 @@
// broadcasted state over the course of reporting the transition asynchronously.
private boolean mInteractive = true;
private int mInteractiveChangeReason;
+ private long mInteractiveChangeStartTime; // In SystemClock.uptimeMillis()
private boolean mInteractiveChanging;
// The pending interactive state that we will eventually want to broadcast.
@@ -371,7 +374,7 @@
* which case it will assume that the state did not fully converge before the
* next transition began and will recover accordingly.
*/
- public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
+ public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) {
final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
if (DEBUG) {
Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
@@ -410,6 +413,7 @@
// Handle early behaviors.
mInteractive = interactive;
mInteractiveChangeReason = reason;
+ mInteractiveChangeStartTime = eventTime;
mInteractiveChanging = true;
handleEarlyInteractiveChange();
}
@@ -440,8 +444,8 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- // Note a SCREEN tron event is logged in PowerManagerService.
- mPolicy.startedWakingUp();
+ final int why = translateOnReason(mInteractiveChangeReason);
+ mPolicy.startedWakingUp(why);
}
});
@@ -470,12 +474,21 @@
*/
private void handleLateInteractiveChange() {
synchronized (mLock) {
+ final int interactiveChangeLatency =
+ (int) (SystemClock.uptimeMillis() - mInteractiveChangeStartTime);
if (mInteractive) {
// Finished waking up...
+ final int why = translateOnReason(mInteractiveChangeReason);
mHandler.post(new Runnable() {
@Override
public void run() {
- mPolicy.finishedWakingUp();
+ LogMaker log = new LogMaker(MetricsEvent.SCREEN);
+ log.setType(MetricsEvent.TYPE_OPEN);
+ log.setSubtype(why);
+ log.setLatency(interactiveChangeLatency);
+ MetricsLogger.action(log);
+ EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);
+ mPolicy.finishedWakingUp(why);
}
});
} else {
@@ -499,8 +512,9 @@
LogMaker log = new LogMaker(MetricsEvent.SCREEN);
log.setType(MetricsEvent.TYPE_CLOSE);
log.setSubtype(why);
+ log.setLatency(interactiveChangeLatency);
MetricsLogger.action(log);
- EventLogTags.writePowerScreenState(0, why, 0, 0, 0);
+ EventLogTags.writePowerScreenState(0, why, 0, 0, interactiveChangeLatency);
mPolicy.finishedGoingToSleep(why);
}
});
@@ -524,6 +538,23 @@
}
}
+ private static @OnReason int translateOnReason(@WakeReason int reason) {
+ switch (reason) {
+ case PowerManager.WAKE_REASON_POWER_BUTTON:
+ case PowerManager.WAKE_REASON_PLUGGED_IN:
+ case PowerManager.WAKE_REASON_GESTURE:
+ case PowerManager.WAKE_REASON_CAMERA_LAUNCH:
+ case PowerManager.WAKE_REASON_WAKE_KEY:
+ case PowerManager.WAKE_REASON_WAKE_MOTION:
+ case PowerManager.WAKE_REASON_LID:
+ return WindowManagerPolicy.ON_BECAUSE_OF_USER;
+ case PowerManager.WAKE_REASON_APPLICATION:
+ return WindowManagerPolicy.ON_BECAUSE_OF_APPLICATION;
+ default:
+ return WindowManagerPolicy.ON_BECAUSE_OF_UNKNOWN;
+ }
+ }
+
/**
* Called when screen brightness boost begins or ends.
*/
@@ -565,14 +596,16 @@
/**
* Called when the screen has turned on.
*/
- public void onWakeUp(String reason, int reasonUid, String opPackageName, int opUid) {
+ public void onWakeUp(int reason, String details, int reasonUid, String opPackageName,
+ int opUid) {
if (DEBUG) {
- Slog.d(TAG, "onWakeUp: event=" + reason + ", reasonUid=" + reasonUid
+ Slog.d(TAG, "onWakeUp: reason=" + PowerManager.wakeReasonToString(reason)
+ + ", details=" + details + ", reasonUid=" + reasonUid
+ " opPackageName=" + opPackageName + " opUid=" + opUid);
}
try {
- mBatteryStats.noteWakeUp(reason, reasonUid);
+ mBatteryStats.noteWakeUp(details, reasonUid);
if (opPackageName != null) {
mAppOps.noteOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName);
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3ccd234..af3bff0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -39,7 +39,6 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.hardware.power.V1_0.PowerHint;
-import android.metrics.LogMaker;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
@@ -52,6 +51,7 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
+import android.os.PowerManager.WakeReason;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.Process;
@@ -82,8 +82,6 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.EventLogTags;
@@ -295,6 +293,7 @@
private long mLastSleepTime;
// Last reason the device went to sleep.
+ private @WakeReason int mLastWakeReason;
private int mLastSleepReason;
// Timestamp of the last call to user activity.
@@ -1119,8 +1118,9 @@
opPackageName = wakeLock.mPackageName;
opUid = wakeLock.mOwnerUid;
}
- wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), wakeLock.mTag, opUid,
- opPackageName, opUid);
+ wakeUpNoUpdateLocked(SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
+ opUid, opPackageName, opUid);
}
}
@@ -1410,17 +1410,17 @@
}
}
- private void wakeUpInternal(long eventTime, String reason, int uid, String opPackageName,
- int opUid) {
+ private void wakeUpInternal(long eventTime, @WakeReason int reason, String details, int uid,
+ String opPackageName, int opUid) {
synchronized (mLock) {
- if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {
+ if (wakeUpNoUpdateLocked(eventTime, reason, details, uid, opPackageName, opUid)) {
updatePowerStateLocked();
}
}
}
- private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,
- String opPackageName, int opUid) {
+ private boolean wakeUpNoUpdateLocked(long eventTime, @WakeReason int reason, String details,
+ int reasonUid, String opPackageName, int opUid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid);
}
@@ -1434,25 +1434,18 @@
Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
try {
- switch (mWakefulness) {
- case WAKEFULNESS_ASLEEP:
- Slog.i(TAG, "Waking up from sleep (uid=" + reasonUid + " reason=" + reason
- + ")...");
- break;
- case WAKEFULNESS_DREAMING:
- Slog.i(TAG, "Waking up from dream (uid=" + reasonUid + " reason=" + reason
- + ")...");
- break;
- case WAKEFULNESS_DOZING:
- Slog.i(TAG, "Waking up from dozing (uid=" + reasonUid + " reason=" + reason
- + ")...");
- break;
- }
+ Slog.i(TAG, "Waking up from "
+ + PowerManagerInternal.wakefulnessToString(mWakefulness)
+ + " (uid=" + reasonUid
+ + ", reason=" + PowerManager.wakeReasonToString(reason)
+ + ", details=" + details
+ + ")...");
mLastWakeTime = eventTime;
- setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
+ mLastWakeReason = reason;
+ setWakefulnessLocked(WAKEFULNESS_AWAKE, reason, eventTime);
- mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);
+ mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid);
userActivityNoUpdateLocked(
eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
} finally {
@@ -1520,7 +1513,7 @@
mLastSleepTime = eventTime;
mLastSleepReason = reason;
mSandmanSummoned = true;
- setWakefulnessLocked(WAKEFULNESS_DOZING, reason);
+ setWakefulnessLocked(WAKEFULNESS_DOZING, reason, eventTime);
// Report the number of wake locks that will be cleared by going to sleep.
int numWakeLocksCleared = 0;
@@ -1570,7 +1563,7 @@
Slog.i(TAG, "Nap time (uid " + uid +")...");
mSandmanSummoned = true;
- setWakefulnessLocked(WAKEFULNESS_DREAMING, 0);
+ setWakefulnessLocked(WAKEFULNESS_DREAMING, 0, eventTime);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -1593,7 +1586,8 @@
try {
Slog.i(TAG, "Sleeping (uid " + uid +")...");
- setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+ setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
+ eventTime);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -1601,13 +1595,13 @@
}
@VisibleForTesting
- void setWakefulnessLocked(int wakefulness, int reason) {
+ void setWakefulnessLocked(int wakefulness, int reason, long eventTime) {
if (mWakefulness != wakefulness) {
mWakefulness = wakefulness;
mWakefulnessChanging = true;
mDirty |= DIRTY_WAKEFULNESS;
if (mNotifier != null) {
- mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
+ mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime);
}
mAttentionDetector.onWakefulnessChangeStarted(wakefulness);
}
@@ -1631,23 +1625,6 @@
}
}
- private void logScreenOn() {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
-
- final int latencyMs = (int) (SystemClock.uptimeMillis() - mLastWakeTime);
-
- LogMaker log = new LogMaker(MetricsEvent.SCREEN);
- log.setType(MetricsEvent.TYPE_OPEN);
- log.setSubtype(0); // not user initiated
- log.setLatency(latencyMs); // How long it took.
- MetricsLogger.action(log);
- EventLogTags.writePowerScreenState(1, 0, 0, 0, latencyMs);
-
- if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
- Slog.w(TAG, "Screen on took " + latencyMs+ " ms");
- }
- }
-
private void finishWakefulnessChangeIfNeededLocked() {
if (mWakefulnessChanging && mDisplayReady) {
if (mWakefulness == WAKEFULNESS_DOZING
@@ -1658,7 +1635,11 @@
logSleepTimeoutRecapturedLocked();
}
if (mWakefulness == WAKEFULNESS_AWAKE) {
- logScreenOn();
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
+ final int latencyMs = (int) (SystemClock.uptimeMillis() - mLastWakeTime);
+ if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
+ Slog.w(TAG, "Screen on took " + latencyMs + " ms");
+ }
}
mWakefulnessChanging = false;
mNotifier.onWakefulnessChangeFinished();
@@ -1786,7 +1767,8 @@
final long now = SystemClock.uptimeMillis();
if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
dockedOnWirelessCharger)) {
- wakeUpNoUpdateLocked(now, "android.server.power:POWER", Process.SYSTEM_UID,
+ wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN,
+ "android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
}
userActivityNoUpdateLocked(
@@ -2369,8 +2351,10 @@
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
} else {
- wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), "android.server.power:DREAM",
- Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID);
+ wakeUpNoUpdateLocked(SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_UNKNOWN,
+ "android.server.power:DREAM_FINISHED", Process.SYSTEM_UID,
+ mContext.getOpPackageName(), Process.SYSTEM_UID);
updatePowerStateLocked();
}
} else if (wakefulness == WAKEFULNESS_DOZING) {
@@ -4375,7 +4359,8 @@
}
@Override // Binder call
- public void wakeUp(long eventTime, String reason, String opPackageName) {
+ public void wakeUp(long eventTime, @WakeReason int reason, String details,
+ String opPackageName) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
@@ -4386,7 +4371,7 @@
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
+ wakeUpInternal(eventTime, reason, details, uid, opPackageName, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index bd46a50..fac95f9 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -665,8 +665,7 @@
config.getEnableAdjustBrightness(),
config.getEnableDataSaver(),
config.getEnableFirewall(),
- // TODO: add option to config
- config.getAdvertiseIsEnabled(),
+ config.getEnableNightMode(),
config.getEnableQuickDoze(),
/* filesForInteractive */
(new CpuFrequencies()).parseString(cpuFreqInteractive).toSysFileMap(),
@@ -674,7 +673,7 @@
(new CpuFrequencies()).parseString(cpuFreqNoninteractive).toSysFileMap(),
config.getForceAllAppsStandby(),
config.getForceBackgroundCheck(),
- config.getGpsMode()
+ config.getLocationMode()
);
}
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
index 107cb2c..4fb40db 100644
--- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -21,6 +21,7 @@
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.role.IRoleManagerCallback;
+import android.app.role.RoleManager;
import android.app.role.RoleManagerCallback;
import android.content.ComponentName;
import android.content.Context;
@@ -61,34 +62,35 @@
* Add a specific application to the holders of a role. If the role is exclusive, the previous
* holder will be replaced.
*
- * @see RoleControllerService#onAddRoleHolder(String, String, RoleManagerCallback)
+ * @see RoleControllerService#onAddRoleHolder(String, String, int, RoleManagerCallback)
*/
public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @NonNull IRoleManagerCallback callback) {
+ @RoleManager.ManageHoldersFlags int flags, @NonNull IRoleManagerCallback callback) {
mConnection.enqueueCall(new Connection.Call((service, callbackDelegate) ->
- service.onAddRoleHolder(roleName, packageName, callbackDelegate), callback));
+ service.onAddRoleHolder(roleName, packageName, flags, callbackDelegate), callback));
}
/**
* Remove a specific application from the holders of a role.
*
- * @see RoleControllerService#onRemoveRoleHolder(String, String, RoleManagerCallback)
+ * @see RoleControllerService#onRemoveRoleHolder(String, String, int, RoleManagerCallback)
*/
public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @NonNull IRoleManagerCallback callback) {
+ @RoleManager.ManageHoldersFlags int flags, @NonNull IRoleManagerCallback callback) {
mConnection.enqueueCall(new Connection.Call((service, callbackDelegate) ->
- service.onRemoveRoleHolder(roleName, packageName, callbackDelegate), callback));
+ service.onRemoveRoleHolder(roleName, packageName, flags, callbackDelegate),
+ callback));
}
/**
* Remove all holders of a role.
*
- * @see RoleControllerService#onClearRoleHolders(String, RoleManagerCallback)
+ * @see RoleControllerService#onClearRoleHolders(String, int, RoleManagerCallback)
*/
public void onClearRoleHolders(@NonNull String roleName,
- @NonNull IRoleManagerCallback callback) {
+ @RoleManager.ManageHoldersFlags int flags, @NonNull IRoleManagerCallback callback) {
mConnection.enqueueCall(new Connection.Call((service, callbackDelegate) ->
- service.onClearRoleHolders(roleName, callbackDelegate), callback));
+ service.onClearRoleHolders(roleName, flags, callbackDelegate), callback));
}
/**
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 84305be..21bf9de 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -201,10 +201,15 @@
new ContentObserver(getContext().getMainThreadHandler()) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
- getOrCreateControllerService(userId).onSmsKillSwitchToggled(
- Settings.Global.getInt(
- getContext().getContentResolver(),
- Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0) == 1);
+ boolean killSwitchEnabled = Settings.Global.getInt(
+ getContext().getContentResolver(),
+ Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0) == 1;
+ for (int user : mUserManagerInternal.getUserIds()) {
+ if (mUserManagerInternal.isUserRunning(user)) {
+ getOrCreateControllerService(user)
+ .onSmsKillSwitchToggled(killSwitchEnabled);
+ }
+ }
}
}, UserHandle.USER_ALL);
}
@@ -450,7 +455,8 @@
@Override
public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
- @UserIdInt int userId, @NonNull IRoleManagerCallback callback) {
+ @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
+ @NonNull IRoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
Preconditions.checkNotNull(callback, "callback cannot be null");
@@ -462,12 +468,14 @@
getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
"addRoleHolderAsUser");
- getOrCreateControllerService(userId).onAddRoleHolder(roleName, packageName, callback);
+ getOrCreateControllerService(userId).onAddRoleHolder(roleName, packageName, flags,
+ callback);
}
@Override
public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
- @UserIdInt int userId, @NonNull IRoleManagerCallback callback) {
+ @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
+ @NonNull IRoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
Preconditions.checkNotNull(callback, "callback cannot be null");
@@ -479,12 +487,13 @@
getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
"removeRoleHolderAsUser");
- getOrCreateControllerService(userId).onRemoveRoleHolder(roleName, packageName,
+ getOrCreateControllerService(userId).onRemoveRoleHolder(roleName, packageName, flags,
callback);
}
@Override
- public void clearRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId,
+ public void clearRoleHoldersAsUser(@NonNull String roleName,
+ @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
@NonNull IRoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkNotNull(callback, "callback cannot be null");
@@ -496,7 +505,7 @@
getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
"clearRoleHoldersAsUser");
- getOrCreateControllerService(userId).onClearRoleHolders(roleName, callback);
+ getOrCreateControllerService(userId).onClearRoleHolders(roleName, flags, callback);
}
@Override
@@ -718,9 +727,9 @@
};
if (packageName != null) {
getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER,
- packageName, callback);
+ packageName, 0, callback);
} else {
- getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER,
+ getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0,
callback);
}
try {
diff --git a/services/core/java/com/android/server/role/RoleManagerShellCommand.java b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
index b245e98..00021d7 100644
--- a/services/core/java/com/android/server/role/RoleManagerShellCommand.java
+++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
@@ -98,13 +98,22 @@
return userId;
}
+ private int getFlagsMaybe() {
+ String flags = getNextArg();
+ if (flags == null) {
+ return 0;
+ }
+ return Integer.parseInt(flags);
+ }
+
private int runAddRoleHolder() throws RemoteException {
int userId = getUserIdMaybe();
String roleName = getNextArgRequired();
String packageName = getNextArgRequired();
+ int flags = getFlagsMaybe();
Callback callback = new Callback();
- mRoleManager.addRoleHolderAsUser(roleName, packageName, userId, callback);
+ mRoleManager.addRoleHolderAsUser(roleName, packageName, flags, userId, callback);
return callback.waitForResult();
}
@@ -112,18 +121,20 @@
int userId = getUserIdMaybe();
String roleName = getNextArgRequired();
String packageName = getNextArgRequired();
+ int flags = getFlagsMaybe();
Callback callback = new Callback();
- mRoleManager.removeRoleHolderAsUser(roleName, packageName, userId, callback);
+ mRoleManager.removeRoleHolderAsUser(roleName, packageName, flags, userId, callback);
return callback.waitForResult();
}
private int runClearRoleHolders() throws RemoteException {
int userId = getUserIdMaybe();
String roleName = getNextArgRequired();
+ int flags = getFlagsMaybe();
Callback callback = new Callback();
- mRoleManager.clearRoleHoldersAsUser(roleName, userId, callback);
+ mRoleManager.clearRoleHoldersAsUser(roleName, flags, userId, callback);
return callback.waitForResult();
}
@@ -134,9 +145,9 @@
pw.println(" help");
pw.println(" Print this help text.");
pw.println();
- pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE");
- pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE");
- pw.println(" clear-role-holders [--user USER_ID] ROLE");
+ pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]");
+ pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]");
+ pw.println(" clear-role-holders [--user USER_ID] ROLE [FLAGS]");
pw.println();
}
}
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 8dd0760..f3b8385 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -22,6 +22,7 @@
import android.os.storage.StorageManager;
import android.util.IntArray;
import android.util.Log;
+import android.util.SparseLongArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.Installer;
@@ -51,11 +52,13 @@
* Creates an app data snapshot for a specified {@code packageName} for {@code installedUsers},
* a specified set of users for whom the package is installed.
*
- * @return a list of users for which the snapshot is pending, usually because data for one or
- * more users is still credential locked.
+ * @return a {@link SnapshotAppDataResult}/
+ * @see SnapshotAppDataResult
*/
- public IntArray snapshotAppData(String packageName, int[] installedUsers) {
+ public SnapshotAppDataResult snapshotAppData(String packageName, int[] installedUsers) {
final IntArray pendingBackups = new IntArray();
+ final SparseLongArray ceSnapshotInodes = new SparseLongArray();
+
for (int user : installedUsers) {
final int storageFlags;
if (isUserCredentialLocked(user)) {
@@ -69,14 +72,17 @@
}
try {
- mInstaller.snapshotAppData(packageName, user, storageFlags);
+ long ceSnapshotInode = mInstaller.snapshotAppData(packageName, user, storageFlags);
+ if ((storageFlags & Installer.FLAG_STORAGE_CE) != 0) {
+ ceSnapshotInodes.put(user, ceSnapshotInode);
+ }
} catch (InstallerException ie) {
Log.e(TAG, "Unable to create app data snapshot for: " + packageName
+ ", userId: " + user, ie);
}
}
- return pendingBackups;
+ return new SnapshotAppDataResult(pendingBackups, ceSnapshotInodes);
}
/**
@@ -138,6 +144,22 @@
}
/**
+ * Deletes an app data data snapshot for a specified package {@code packageName} for a
+ * given {@code user}.
+ */
+ public void destroyAppDataSnapshot(String packageName, int user, long ceSnapshotInode) {
+ int storageFlags = Installer.FLAG_STORAGE_DE;
+ if (ceSnapshotInode > 0) {
+ storageFlags |= Installer.FLAG_STORAGE_CE;
+ }
+ try {
+ mInstaller.destroyAppDataSnapshot(packageName, user, ceSnapshotInode, storageFlags);
+ } catch (InstallerException ie) {
+ Log.e(TAG, "Unable to delete app data snapshot for " + packageName, ie);
+ }
+ }
+
+ /**
* Computes the list of pending backups and restores for {@code userId} given lists of
* available and recent rollbacks. Packages pending backup for the given user are added
* to {@code pendingBackups} and packages pending restore are added to {@code pendingRestores}
@@ -191,16 +213,28 @@
}
/**
- * Commits the list of pending backups and restores for a given {@code userId}.
+ * Commits the list of pending backups and restores for a given {@code userId}. For the pending
+ * backups updates corresponding {@code changedRollbackData} with a mapping from {@code userId}
+ * to a inode of theirs CE user data snapshot.
*/
public void commitPendingBackupAndRestoreForUser(int userId,
- ArrayList<String> pendingBackups, Map<String, RestoreInfo> pendingRestores) {
+ ArrayList<String> pendingBackups, Map<String, RestoreInfo> pendingRestores,
+ List<RollbackData> changedRollbackData) {
if (!pendingBackups.isEmpty()) {
for (String packageName : pendingBackups) {
try {
- mInstaller.snapshotAppData(packageName, userId, Installer.FLAG_STORAGE_CE);
+ long ceSnapshotInode = mInstaller.snapshotAppData(packageName, userId,
+ Installer.FLAG_STORAGE_CE);
+ for (RollbackData data : changedRollbackData) {
+ for (PackageRollbackInfo info : data.packages) {
+ if (info.getPackageName().equals(packageName)) {
+ info.putCeSnapshotInode(userId, ceSnapshotInode);
+ }
+ }
+ }
} catch (InstallerException ie) {
- Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie);
+ Log.e(TAG, "Unable to create app data snapshot for: " + packageName
+ + ", userId: " + userId, ie);
}
}
}
@@ -233,4 +267,26 @@
return StorageManager.isFileEncryptedNativeOrEmulated()
&& !StorageManager.isUserKeyUnlocked(userId);
}
+
+ /**
+ * Encapsulates a result of {@link #snapshotAppData} method.
+ */
+ public static final class SnapshotAppDataResult {
+
+ /**
+ * A list of users for which the snapshot is pending, usually because data for one or more
+ * users is still credential locked.
+ */
+ public final IntArray pendingBackups;
+
+ /**
+ * A mapping between user and an inode of theirs CE data snapshot.
+ */
+ public final SparseLongArray ceSnapshotInodes;
+
+ public SnapshotAppDataResult(IntArray pendingBackups, SparseLongArray ceSnapshotInodes) {
+ this.pendingBackups = pendingBackups;
+ this.ceSnapshotInodes = ceSnapshotInodes;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 27f7bcf..95c3f4c 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -43,6 +43,7 @@
import android.util.IntArray;
import android.util.Log;
import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
@@ -110,7 +111,7 @@
private final HandlerThread mHandlerThread;
private final Installer mInstaller;
private final RollbackPackageHealthObserver mPackageHealthObserver;
- private final AppDataRollbackHelper mUserdataHelper;
+ private final AppDataRollbackHelper mAppDataRollbackHelper;
RollbackManagerServiceImpl(Context context) {
mContext = context;
@@ -124,7 +125,7 @@
mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
- mUserdataHelper = new AppDataRollbackHelper(mInstaller);
+ mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller);
// Kick off loading of the rollback data from strorage in a background
// thread.
@@ -339,12 +340,9 @@
// for apex?
if (!info.isApex()) {
String installerPackageName = pm.getInstallerPackageName(info.getPackageName());
- if (installerPackageName == null) {
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "Cannot find installer package");
- return;
+ if (installerPackageName != null) {
+ params.setInstallerPackageName(installerPackageName);
}
- params.setInstallerPackageName(installerPackageName);
}
params.setAllowDowngrade(true);
if (data.isStaged()) {
@@ -449,7 +447,7 @@
for (PackageRollbackInfo info : data.packages) {
if (info.getPackageName().equals(packageName)) {
iter.remove();
- mRollbackStore.deleteAvailableRollback(data);
+ deleteRollback(data);
break;
}
}
@@ -464,13 +462,13 @@
final List<RollbackData> changed;
synchronized (mLock) {
ensureRollbackDataLoadedLocked();
- changed = mUserdataHelper.computePendingBackupsAndRestores(userId,
+ changed = mAppDataRollbackHelper.computePendingBackupsAndRestores(userId,
pendingBackupPackages, pendingRestorePackages, mAvailableRollbacks,
mRecentlyExecutedRollbacks);
}
- mUserdataHelper.commitPendingBackupAndRestoreForUser(userId,
- pendingBackupPackages, pendingRestorePackages);
+ mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId,
+ pendingBackupPackages, pendingRestorePackages, changed);
for (RollbackData rd : changed) {
try {
@@ -520,7 +518,7 @@
// mAvailableRollbacks, or is it okay to leave as
// unavailable until the next reboot when it will go
// away on its own?
- mRollbackStore.deleteAvailableRollback(data);
+ deleteRollback(data);
}
}
}
@@ -592,7 +590,7 @@
info.getVersionRolledBackFrom(),
installedVersion)) {
iter.remove();
- mRollbackStore.deleteAvailableRollback(data);
+ deleteRollback(data);
break;
}
}
@@ -705,7 +703,7 @@
if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
iter.remove();
- mRollbackStore.deleteAvailableRollback(data);
+ deleteRollback(data);
} else if (oldest == null || oldest.isAfter(data.timestamp)) {
oldest = data.timestamp;
}
@@ -821,9 +819,13 @@
String packageName = newPackage.packageName;
for (PackageRollbackInfo info : rd.packages) {
if (info.getPackageName().equals(packageName)) {
- IntArray pendingBackups = mUserdataHelper.snapshotAppData(
- packageName, installedUsers);
- info.getPendingBackups().addAll(pendingBackups);
+ AppDataRollbackHelper.SnapshotAppDataResult rs =
+ mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers);
+ info.getPendingBackups().addAll(rs.pendingBackups);
+ for (int i = 0; i < rs.ceSnapshotInodes.size(); i++) {
+ info.putCeSnapshotInode(rs.ceSnapshotInodes.keyAt(i),
+ rs.ceSnapshotInodes.valueAt(i));
+ }
try {
mRollbackStore.saveAvailableRollback(rd);
} catch (IOException ioe) {
@@ -892,13 +894,18 @@
VersionedPackage installedVersion = new VersionedPackage(packageName,
pkgInfo.getLongVersionCode());
- IntArray pendingBackups = IntArray.wrap(new int[0]);
+ final AppDataRollbackHelper.SnapshotAppDataResult result;
if (snapshotUserData && !isApex) {
- pendingBackups = mUserdataHelper.snapshotAppData(packageName, installedUsers);
+ result = mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers);
+ } else {
+ result = new AppDataRollbackHelper.SnapshotAppDataResult(IntArray.wrap(new int[0]),
+ new SparseLongArray());
}
PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion,
- pendingBackups, new ArrayList<>(), isApex);
+ result.pendingBackups, new ArrayList<>(), isApex, IntArray.wrap(installedUsers),
+ result.ceSnapshotInodes);
+
RollbackData data;
try {
int childSessionId = session.getSessionId();
@@ -948,9 +955,8 @@
getHandler().post(() -> {
final RollbackData rollbackData = getRollbackForPackage(packageName);
for (int userId : userIds) {
- final boolean changedRollbackData = mUserdataHelper.restoreAppData(packageName,
- rollbackData, userId, appId, ceDataInode, seInfo);
-
+ final boolean changedRollbackData = mAppDataRollbackHelper.restoreAppData(
+ packageName, rollbackData, userId, appId, ceDataInode, seInfo);
// We've updated metadata about this rollback, so save it to flash.
if (changedRollbackData) {
try {
@@ -1142,12 +1148,12 @@
scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
} catch (IOException e) {
Log.e(TAG, "Unable to enable rollback", e);
- mRollbackStore.deleteAvailableRollback(data);
+ deleteRollback(data);
}
} else {
// The install session was aborted, clean up the pending
// install.
- mRollbackStore.deleteAvailableRollback(data);
+ deleteRollback(data);
}
}
}
@@ -1246,4 +1252,17 @@
throw new IOException("Failed to allocate rollback ID");
}
+
+ private void deleteRollback(RollbackData rollbackData) {
+ for (PackageRollbackInfo info : rollbackData.packages) {
+ IntArray installedUsers = info.getInstalledUsers();
+ SparseLongArray ceSnapshotInodes = info.getCeSnapshotInodes();
+ for (int i = 0; i < installedUsers.size(); i++) {
+ int userId = installedUsers.get(i);
+ mAppDataRollbackHelper.destroyAppDataSnapshot(info.getPackageName(), userId,
+ ceSnapshotInodes.get(userId, 0));
+ }
+ }
+ mRollbackStore.deleteAvailableRollback(rollbackData);
+ }
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index d17ebae..be904ea 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -23,6 +23,7 @@
import android.content.rollback.RollbackInfo;
import android.util.IntArray;
import android.util.Log;
+import android.util.SparseLongArray;
import libcore.io.IoUtils;
@@ -160,6 +161,28 @@
return restoreInfos;
}
+ private static @NonNull JSONArray ceSnapshotInodesToJson(
+ @NonNull SparseLongArray ceSnapshotInodes) throws JSONException {
+ JSONArray array = new JSONArray();
+ for (int i = 0; i < ceSnapshotInodes.size(); i++) {
+ JSONObject entryJson = new JSONObject();
+ entryJson.put("userId", ceSnapshotInodes.keyAt(i));
+ entryJson.put("ceSnapshotInode", ceSnapshotInodes.valueAt(i));
+ array.put(entryJson);
+ }
+ return array;
+ }
+
+ private static @NonNull SparseLongArray ceSnapshotInodesFromJson(JSONArray json)
+ throws JSONException {
+ SparseLongArray ceSnapshotInodes = new SparseLongArray(json.length());
+ for (int i = 0; i < json.length(); i++) {
+ JSONObject entry = json.getJSONObject(i);
+ ceSnapshotInodes.append(entry.getInt("userId"), entry.getLong("ceSnapshotInode"));
+ }
+ return ceSnapshotInodes;
+ }
+
/**
* Reads the list of recently executed rollbacks from persistent storage.
*/
@@ -263,8 +286,6 @@
* rollback.
*/
void deleteAvailableRollback(RollbackData data) {
- // TODO(narayan): Make sure we delete the userdata snapshot along with the backup of the
- // actual app.
removeFile(data.backupDir);
}
@@ -341,11 +362,15 @@
IntArray pendingBackups = info.getPendingBackups();
List<RestoreInfo> pendingRestores = info.getPendingRestores();
+ IntArray installedUsers = info.getInstalledUsers();
json.put("pendingBackups", convertToJsonArray(pendingBackups));
json.put("pendingRestores", convertToJsonArray(pendingRestores));
json.put("isApex", info.isApex());
+ json.put("installedUsers", convertToJsonArray(installedUsers));
+ json.put("ceSnapshotInodes", ceSnapshotInodesToJson(info.getCeSnapshotInodes()));
+
return json;
}
@@ -362,8 +387,12 @@
final boolean isApex = json.getBoolean("isApex");
+ final IntArray installedUsers = convertToIntArray(json.getJSONArray("installedUsers"));
+ final SparseLongArray ceSnapshotInodes = ceSnapshotInodesFromJson(
+ json.getJSONArray("ceSnapshotInodes"));
+
return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo,
- pendingBackups, pendingRestores, isApex);
+ pendingBackups, pendingRestores, isApex, installedUsers, ceSnapshotInodes);
}
private JSONArray versionedPackagesToJson(List<VersionedPackage> packages)
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 071dde7..b0ef8a0 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2243,9 +2243,12 @@
synchronized (mLock) {
mInAmbientMode = inAmbientMode;
final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+ final boolean hasConnection = data != null && data.connection != null;
+ final WallpaperInfo info = hasConnection ? data.connection.mInfo : null;
+
// The wallpaper info is null for image wallpaper, also use the engine in this case.
- if (data != null && data.connection != null && (data.connection.mInfo == null
- || data.connection.mInfo.supportsAmbientMode())) {
+ if (hasConnection && (info == null && isAodImageWallpaperEnabled()
+ || info != null && info.supportsAmbientMode())) {
// TODO(multi-display) Extends this method with specific display.
engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
} else {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index c0fe6e9..0a3c2fb 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -97,7 +97,6 @@
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.ResumeActivityItem;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -467,7 +466,7 @@
* initialized. So we initialize our wakelocks afterwards.
*/
void initPowerManagement() {
- mPowerManager = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
+ mPowerManager = mService.mContext.getSystemService(PowerManager.class);
mGoingToSleep = mPowerManager
.newWakeLock(PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
mLaunchingActivity = mPowerManager.newWakeLock(PARTIAL_WAKE_LOCK, "*launch*");
@@ -2456,7 +2455,8 @@
}
void wakeUp(String reason) {
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason);
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION,
+ "android.server.am:TURN_ON:" + reason);
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 1d71d876..678a896 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -753,8 +753,8 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"shouldAbortBackgroundActivityStart");
abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, callingPid,
- callingPackage, realCallingUid, callerApp, originatingPendingIntent,
- allowBackgroundActivityStart, intent);
+ callingPackage, realCallingUid, realCallingPid, callerApp,
+ originatingPendingIntent, allowBackgroundActivityStart, intent);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -914,22 +914,14 @@
}
private boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
- final String callingPackage, int realCallingUid, WindowProcessController callerApp,
- PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart,
- Intent intent) {
+ final String callingPackage, int realCallingUid, int realCallingPid,
+ WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
+ boolean allowBackgroundActivityStart, Intent intent) {
// don't abort for the most important UIDs
if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID
|| callingUid == Process.NFC_UID) {
return false;
}
- // don't abort if the callerApp has any visible activity
- if (callerApp != null && callerApp.hasForegroundActivities()) {
- return false;
- }
- // don't abort if the callerApp is instrumenting with background activity starts privileges
- if (callerApp != null && callerApp.isInstrumentingWithBackgroundActivityStartPrivileges()) {
- return false;
- }
// don't abort if the callingUid is in the foreground or is a persistent system process
final int callingUidProcState = mService.getUidStateLocked(callingUid);
final boolean callingUidHasAnyVisibleWindow =
@@ -967,9 +959,26 @@
return false;
}
}
- // don't abort if the caller is currently temporarily whitelisted
- if (callerApp != null && callerApp.areBackgroundActivityStartsAllowed()) {
- return false;
+ // If we don't have callerApp at this point, no caller was provided to startActivity().
+ // That's the case for PendingIntent-based starts, since the creator's process might not be
+ // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
+ // caller, so that we can make the decision based on its foreground/whitelisted state.
+ if (callerApp == null) {
+ callerApp = mService.getProcessController(realCallingPid, realCallingUid);
+ }
+ if (callerApp != null) {
+ // don't abort if the callerApp has any visible activity
+ if (callerApp.hasForegroundActivities()) {
+ return false;
+ }
+ // don't abort if the callerApp is instrumenting with background activity starts privs
+ if (callerApp.isInstrumentingWithBackgroundActivityStartPrivileges()) {
+ return false;
+ }
+ // don't abort if the caller is currently temporarily whitelisted
+ if (callerApp.areBackgroundActivityStartsAllowed()) {
+ return false;
+ }
}
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
@@ -996,6 +1005,7 @@
+ "; originatingPendingIntent: " + originatingPendingIntent
+ "; isBgStartWhitelisted: " + allowBackgroundActivityStart
+ "; intent: " + intent
+ + "; callerApp: " + callerApp
+ "]");
// log aborted activity start to TRON
if (mService.isActivityStartsLoggingEnabled()) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index b6bac613..7aa3481 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5666,6 +5666,20 @@
return null;
}
+ WindowProcessController getProcessController(int pid, int uid) {
+ final ArrayMap<String, SparseArray<WindowProcessController>> pmap = mProcessNames.getMap();
+ for (int i = pmap.size()-1; i >= 0; i--) {
+ final SparseArray<WindowProcessController> procs = pmap.valueAt(i);
+ for (int j = procs.size() - 1; j >= 0; j--) {
+ final WindowProcessController proc = procs.valueAt(j);
+ if (UserHandle.isApp(uid) && proc.getPid() == pid && proc.mUid == uid) {
+ return proc;
+ }
+ }
+ }
+ return null;
+ }
+
int getUidStateLocked(int uid) {
return mActiveUids.get(uid, PROCESS_STATE_NONEXISTENT);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 18df88b..7a8fd49 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -33,14 +33,11 @@
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InsetsState.TYPE_IME;
-import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
-import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.View.GONE;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_TOP;
@@ -175,7 +172,6 @@
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.View;
-import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -1162,14 +1158,12 @@
@Override
boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
ConfigurationContainer requestingContainer) {
- final int previousRotation = mRotation;
final Configuration config = updateOrientationFromAppTokens(
getRequestedOverrideConfiguration(), freezeDisplayToken, false);
- // This event is considered handled iff a configuration propagation is triggered, because
- // that's the only place lower level containers check if they need to do something to this
- // request. The only guaranteed signal is that the display is rotated to a different
- // orientation (i.e. rotating 180 degrees doesn't count).
- final boolean handled = (mRotation - previousRotation) % 2 != 0;
+ // If display rotation class tells us that it doesn't consider app requested orientation,
+ // this display won't rotate just because of an app changes its requested orientation. Thus
+ // it indicates that this display chooses not to handle this request.
+ final boolean handled = getDisplayRotation().respectAppRequestedOrientation();
if (config == null) {
return handled;
}
@@ -1191,6 +1185,11 @@
return handled;
}
+ @Override
+ boolean handlesOrientationChangeFromDescendant() {
+ return getDisplayRotation().respectAppRequestedOrientation();
+ }
+
/**
* Determine the new desired orientation of this display.
*
@@ -1371,8 +1370,7 @@
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ " selected orientation " + lastOrientation
- + ", got rotation " + rotation + " which has "
- + " metrics");
+ + ", got rotation " + rotation);
if (oldRotation == rotation) {
// No change.
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index bc165dc..5f341ee 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -329,6 +329,15 @@
return mFixedToUserRotation;
}
+ /**
+ * Returns {@code true} if this display rotation takes app requested orientation into
+ * consideration; {@code false} otherwise. For the time being the only case where this is {@code
+ * false} is when {@link #isFixedToUserRotation()} is {@code true}.
+ */
+ boolean respectAppRequestedOrientation() {
+ return !mFixedToUserRotation;
+ }
+
public int getLandscapeRotation() {
return mLandscapeRotation;
}
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index af38c06..69f0012 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -2197,11 +2197,15 @@
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent"
outOverrideBounds.setEmpty();
+ final boolean parentHandlesOrientationChange = mTask != null
+ && mTask.getParent() != null
+ && mTask.getParent().handlesOrientationChangeFromDescendant();
// If the task or its top activity requires a different orientation, make it fit the
// available bounds by scaling down its bounds.
int forcedOrientation = getTopActivityRequestedOrientation();
if (forcedOrientation != ORIENTATION_UNDEFINED
- && forcedOrientation != newParentConfig.orientation) {
+ && forcedOrientation != newParentConfig.orientation
+ && !parentHandlesOrientationChange) {
final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
final int parentWidth = parentBounds.width();
final int parentHeight = parentBounds.height();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index b246da4..a054148 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -715,6 +715,21 @@
}
/**
+ * Check if this container or its parent will handle orientation changes from descendants. It's
+ * different from the return value of {@link #onDescendantOrientationChanged(IBinder,
+ * ConfigurationContainer)} in the sense that the return value of this method tells if this
+ * container or its parent will handle the request eventually, while the return value of the
+ * other method is if it handled the request synchronously.
+ *
+ * @return {@code true} if it handles or will handle orientation change in the future; {@code
+ * false} if it won't handle the change at anytime.
+ */
+ boolean handlesOrientationChangeFromDescendant() {
+ final WindowContainer parent = getParent();
+ return parent != null && parent.handlesOrientationChangeFromDescendant();
+ }
+
+ /**
* Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2
* parameters.
*
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 0fb900a..465f413 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -986,7 +986,7 @@
@Override
public String toString() {
- return mOwner.toString();
+ return mOwner != null ? mOwner.toString() : null;
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4bc1400..85b251a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -156,6 +156,7 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.PowerManager;
+import android.os.PowerManager.WakeReason;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -611,7 +612,7 @@
}
interface PowerManagerWrapper {
- void wakeUp(long time, String reason);
+ void wakeUp(long time, @WakeReason int reason, String details);
boolean isInteractive();
@@ -623,8 +624,8 @@
this(service, s, c, token, parentWindow, appOp, seq, a, viewVisibility, ownerId,
ownerCanAddInternalSystemWindow, new PowerManagerWrapper() {
@Override
- public void wakeUp(long time, String reason) {
- service.mPowerManager.wakeUp(time, reason);
+ public void wakeUp(long time, @WakeReason int reason, String details) {
+ service.mPowerManager.wakeUp(time, reason, details);
}
@Override
@@ -2253,7 +2254,7 @@
Slog.v(TAG, "Relayout window turning screen on: " + this);
}
mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(),
- "android.server.wm:TURN_ON");
+ PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG");
}
if (mAppToken != null) {
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
new file mode 100644
index 0000000..8df0826
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import static org.junit.Assert.assertEquals;
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link ByteRange}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class ByteRangeTest {
+ @Test
+ public void getLength_includesEnd() throws Exception {
+ ByteRange byteRange = new ByteRange(5, 10);
+
+ int length = byteRange.getLength();
+
+ assertEquals(6, length);
+ }
+
+ @Test
+ public void constructor_rejectsNegativeStart() {
+ assertThrows(IllegalArgumentException.class, () -> new ByteRange(-1, 10));
+ }
+
+ @Test
+ public void constructor_rejectsEndBeforeStart() {
+ assertThrows(IllegalArgumentException.class, () -> new ByteRange(10, 9));
+ }
+
+ @Test
+ public void extend_withZeroLength_throwsException() {
+ ByteRange byteRange = new ByteRange(5, 10);
+
+ assertThrows(IllegalArgumentException.class, () -> byteRange.extend(0));
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
new file mode 100644
index 0000000..2af6f2b
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.google.common.primitives.Bytes;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.IOException;
+
+/** Tests for {@link DiffScriptBackupWriter}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class DiffScriptBackupWriterTest {
+ private static final byte[] TEST_BYTES = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+ @Captor private ArgumentCaptor<Byte> mBytesCaptor;
+ @Mock private SingleStreamDiffScriptWriter mDiffScriptWriter;
+ private BackupWriter mBackupWriter;
+
+ @Before
+ public void setUp() {
+ mDiffScriptWriter = mock(SingleStreamDiffScriptWriter.class);
+ mBackupWriter = new DiffScriptBackupWriter(mDiffScriptWriter);
+ mBytesCaptor = ArgumentCaptor.forClass(Byte.class);
+ }
+
+ @Test
+ public void writeBytes_writesBytesToWriter() throws Exception {
+ mBackupWriter.writeBytes(TEST_BYTES);
+
+ verify(mDiffScriptWriter, atLeastOnce()).writeByte(mBytesCaptor.capture());
+ assertThat(mBytesCaptor.getAllValues())
+ .containsExactlyElementsIn(Bytes.asList(TEST_BYTES))
+ .inOrder();
+ }
+
+ @Test
+ public void writeChunk_writesChunkToWriter() throws Exception {
+ mBackupWriter.writeChunk(0, 10);
+
+ verify(mDiffScriptWriter).writeChunk(0, 10);
+ }
+
+ @Test
+ public void getBytesWritten_returnsTotalSum() throws Exception {
+ mBackupWriter.writeBytes(TEST_BYTES);
+ mBackupWriter.writeBytes(TEST_BYTES);
+ mBackupWriter.writeChunk(/*start=*/ 0, /*length=*/ 10);
+
+ long bytesWritten = mBackupWriter.getBytesWritten();
+
+ assertThat(bytesWritten).isEqualTo(2 * TEST_BYTES.length + 10);
+ }
+
+ @Test
+ public void flush_flushesWriter() throws IOException {
+ mBackupWriter.flush();
+
+ verify(mDiffScriptWriter).flush();
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
new file mode 100644
index 0000000..73baf80
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Locale;
+
+/** Tests for {@link SingleStreamDiffScriptWriter}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class SingleStreamDiffScriptWriterTest {
+ private static final int MAX_CHUNK_SIZE_IN_BYTES = 256;
+ /** By default this Locale does not use Arabic numbers for %d formatting. */
+ private static final Locale HINDI = new Locale("hi", "IN");
+
+ private Locale mDefaultLocale;
+ private ByteArrayOutputStream mOutputStream;
+ private SingleStreamDiffScriptWriter mDiffScriptWriter;
+
+ @Before
+ public void setUp() {
+ mDefaultLocale = Locale.getDefault();
+ mOutputStream = new ByteArrayOutputStream();
+ mDiffScriptWriter =
+ new SingleStreamDiffScriptWriter(mOutputStream, MAX_CHUNK_SIZE_IN_BYTES);
+ }
+
+ @After
+ public void tearDown() {
+ Locale.setDefault(mDefaultLocale);
+ }
+
+ @Test
+ public void writeChunk_withNegativeStart_throwsException() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mDiffScriptWriter.writeChunk(-1, 50));
+ }
+
+ @Test
+ public void writeChunk_withZeroLength_throwsException() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mDiffScriptWriter.writeChunk(0, 0));
+ }
+
+ @Test
+ public void writeChunk_withExistingBytesInBuffer_writesBufferFirst()
+ throws IOException {
+ String testString = "abcd";
+ writeStringAsBytesToWriter(testString, mDiffScriptWriter);
+
+ mDiffScriptWriter.writeChunk(0, 20);
+ mDiffScriptWriter.flush();
+
+ // Expected format: length of abcd, newline, abcd, newline, chunk start - chunk end
+ assertThat(mOutputStream.toString("UTF-8")).isEqualTo(
+ String.format("%d\n%s\n%d-%d\n", testString.length(), testString, 0, 19));
+ }
+
+ @Test
+ public void writeChunk_overlappingPreviousChunk_combinesChunks() throws IOException {
+ mDiffScriptWriter.writeChunk(3, 4);
+
+ mDiffScriptWriter.writeChunk(7, 5);
+ mDiffScriptWriter.flush();
+
+ assertThat(mOutputStream.toString("UTF-8")).isEqualTo(String.format("3-11\n"));
+ }
+
+ @Test
+ public void writeChunk_formatsByteIndexesUsingArabicNumbers() throws Exception {
+ Locale.setDefault(HINDI);
+
+ mDiffScriptWriter.writeChunk(0, 12345);
+ mDiffScriptWriter.flush();
+
+ assertThat(mOutputStream.toString("UTF-8")).isEqualTo("0-12344\n");
+ }
+
+ @Test
+ public void flush_flushesOutputStream() throws IOException {
+ ByteArrayOutputStream mockOutputStream = mock(ByteArrayOutputStream.class);
+ SingleStreamDiffScriptWriter diffScriptWriter =
+ new SingleStreamDiffScriptWriter(mockOutputStream, MAX_CHUNK_SIZE_IN_BYTES);
+
+ diffScriptWriter.flush();
+
+ verify(mockOutputStream).flush();
+ }
+
+ private void writeStringAsBytesToWriter(String string, SingleStreamDiffScriptWriter writer)
+ throws IOException {
+ byte[] bytes = string.getBytes("UTF-8");
+ for (int i = 0; i < bytes.length; i++) {
+ writer.writeByte(bytes[i]);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index e3b1245..7081d2e 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -962,13 +962,13 @@
}
@Override
- public int getColorTemperature(Context context, int userId) {
+ public int getNightDisplayColorTemperature(Context context) {
return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
mDefaultNightModeColorTemperature);
}
@Override
- public boolean isNightModeActive(Context context, int userId) {
+ public boolean isNightDisplayActivated(Context context) {
return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
0) == 1;
}
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
index 0b01657..5900fc5 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.display.ColorDisplayManager;
+import android.hardware.display.Time;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
@@ -37,7 +38,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
-import com.android.internal.app.ColorDisplayController;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -71,7 +71,6 @@
private MockTwilightManager mTwilightManager;
private ColorDisplayService mColorDisplayService;
- private ColorDisplayController mColorDisplayController;
private ColorDisplayService.BinderService mBinderService;
@BeforeClass
@@ -97,7 +96,6 @@
mTwilightManager = new MockTwilightManager();
LocalServices.addService(TwilightManager.class, mTwilightManager);
- mColorDisplayController = new ColorDisplayController(mContext, mUserId);
mColorDisplayService = new ColorDisplayService(mContext);
mBinderService = mColorDisplayService.new BinderService();
}
@@ -988,9 +986,11 @@
* @param endTimeOffset the offset relative to now to deactivate Night display (in minutes)
*/
private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) {
- mColorDisplayController.setAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
- mColorDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset));
- mColorDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset));
+ mBinderService.setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
+ mBinderService.setNightDisplayCustomStartTime(
+ new Time(getLocalTimeRelativeToNow(startTimeOffset)));
+ mBinderService
+ .setNightDisplayCustomEndTime(new Time(getLocalTimeRelativeToNow(endTimeOffset)));
}
/**
@@ -1000,7 +1000,7 @@
* @param sunriseOffset the offset relative to now for sunrise (in minutes)
*/
private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) {
- mColorDisplayController.setAutoMode(ColorDisplayManager.AUTO_MODE_TWILIGHT);
+ mBinderService.setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_TWILIGHT);
mTwilightManager.setTwilightState(
getTwilightStateRelativeToNow(sunsetOffset, sunriseOffset));
}
@@ -1041,22 +1041,18 @@
}
/**
- * Configures color mode via ColorDisplayController.
- *
- * @param colorMode the color mode to set
+ * Configures color mode.
*/
private void setColorMode(int colorMode) {
- mColorDisplayController.setColorMode(colorMode);
+ mBinderService.setColorMode(colorMode);
}
/**
* Returns whether the color mode is valid on the device the tests are running on.
- *
- * @param mode the mode to check
*/
private boolean isColorModeValid(int mode) {
final int[] availableColorModes = mContext.getResources().getIntArray(
- R.array.config_availableColorModes);
+ R.array.config_availableColorModes);
if (availableColorModes != null) {
for (int availableMode : availableColorModes) {
if (mode == availableMode) {
@@ -1073,12 +1069,9 @@
private void startService() {
Secure.putIntForUser(mContext.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, mUserId);
- InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
- mColorDisplayService.onStartUser(mUserId);
- }
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ mColorDisplayService.onStartUser(mUserId);
});
}
@@ -1100,7 +1093,7 @@
*/
private void assertActiveColorMode(int mode) {
assertWithMessage("Unexpected color mode setting")
- .that(mColorDisplayController.getColorMode())
+ .that(mBinderService.getColorMode())
.isEqualTo(mode);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 85909d5..72357ce 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -31,7 +31,6 @@
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
-import android.content.pm.UsesPermissionInfo;
import android.os.Bundle;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -466,7 +465,6 @@
pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo()));
pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo()));
pkg.requestedPermissions.add("foo7");
- pkg.usesPermissionInfos.add(new UsesPermissionInfo("foo7"));
pkg.implicitPermissions.add("foo25");
pkg.protectedBroadcasts = new ArrayList<>();
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 2ddc71f..48ab8d6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -141,7 +141,8 @@
assertIsUsedByOtherApps(mBarUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
- assertHasDclInfo(mBarUser0, mFooUser0, mBarUser0.getBaseAndSplitDexPaths());
+ // A package loading another package's APK is not DCL (it's not app data).
+ assertNoDclInfo(mBarUser0);
}
@Test
@@ -334,7 +335,9 @@
notifyDexLoad(mFooUser0, newSplits, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
assertIsUsedByOtherApps(newSplits, pui, true);
- assertHasDclInfo(mBarUser0, mFooUser0, newSplits);
+
+ // Primary and split APKs are not recorded as DCL.
+ assertNoDclInfo(mBarUser0);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index bfa0b74..63341b6 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -150,7 +150,7 @@
@SmallTest
public void testGetDesiredScreenPolicy_WithVR() throws Exception {
// Brighten up the screen
- mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
+ mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0);
assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
DisplayPowerRequest.POLICY_BRIGHT);
@@ -160,12 +160,13 @@
DisplayPowerRequest.POLICY_VR);
// Then take a nap
- mService.setWakefulnessLocked(WAKEFULNESS_ASLEEP, 0);
+ mService.setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
+ 0);
assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
DisplayPowerRequest.POLICY_OFF);
// Wake up to VR
- mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
+ mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0);
assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
DisplayPowerRequest.POLICY_VR);
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 43e3eb0..50dbaf5 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -18,15 +18,19 @@
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.PackageRollbackInfo.RestoreInfo;
import android.util.IntArray;
+import android.util.SparseLongArray;
import com.android.server.pm.Installer;
@@ -38,6 +42,7 @@
import java.io.File;
import java.util.ArrayList;
+import java.util.HashMap;
@RunWith(JUnit4.class)
public class AppDataRollbackHelperTest {
@@ -50,10 +55,14 @@
// All users are unlocked so we should snapshot data for them.
doReturn(true).when(helper).isUserCredentialLocked(eq(10));
doReturn(true).when(helper).isUserCredentialLocked(eq(11));
- IntArray pending = helper.snapshotAppData("com.foo.bar", new int[]{10, 11});
- assertEquals(2, pending.size());
- assertEquals(10, pending.get(0));
- assertEquals(11, pending.get(1));
+ AppDataRollbackHelper.SnapshotAppDataResult result = helper.snapshotAppData("com.foo.bar",
+ new int[]{10, 11});
+
+ assertEquals(2, result.pendingBackups.size());
+ assertEquals(10, result.pendingBackups.get(0));
+ assertEquals(11, result.pendingBackups.get(1));
+
+ assertEquals(0, result.ceSnapshotInodes.size());
InOrder inOrder = Mockito.inOrder(installer);
inOrder.verify(installer).snapshotAppData(
@@ -65,10 +74,14 @@
// One of the users is unlocked but the other isn't
doReturn(false).when(helper).isUserCredentialLocked(eq(10));
doReturn(true).when(helper).isUserCredentialLocked(eq(11));
+ when(installer.snapshotAppData(anyString(), anyInt(), anyInt())).thenReturn(239L);
- pending = helper.snapshotAppData("com.foo.bar", new int[]{10, 11});
- assertEquals(1, pending.size());
- assertEquals(11, pending.get(0));
+ result = helper.snapshotAppData("com.foo.bar", new int[]{10, 11});
+ assertEquals(1, result.pendingBackups.size());
+ assertEquals(11, result.pendingBackups.get(0));
+
+ assertEquals(1, result.ceSnapshotInodes.size());
+ assertEquals(239L, result.ceSnapshotInodes.get(10));
inOrder = Mockito.inOrder(installer);
inOrder.verify(installer).snapshotAppData(
@@ -83,7 +96,7 @@
RollbackData data = new RollbackData(1, new File("/does/not/exist"), -1, true);
data.packages.add(new PackageRollbackInfo(
new VersionedPackage(packageName, 1), new VersionedPackage(packageName, 1),
- new IntArray(), new ArrayList<>(), false));
+ new IntArray(), new ArrayList<>(), false, new IntArray(), new SparseLongArray()));
data.inProgress = true;
return data;
@@ -173,4 +186,53 @@
ArrayList<RestoreInfo> pendingRestores = rd.packages.get(0).getPendingRestores();
assertEquals(0, pendingRestores.size());
}
+
+ @Test
+ public void destroyAppData() throws Exception {
+ Installer installer = mock(Installer.class);
+ AppDataRollbackHelper helper = new AppDataRollbackHelper(installer);
+ SparseLongArray ceSnapshotInodes = new SparseLongArray();
+ ceSnapshotInodes.put(11, 239L);
+
+ helper.destroyAppDataSnapshot("com.foo.bar", 10, 0L);
+ helper.destroyAppDataSnapshot("com.foo.bar", 11, 239L);
+
+ InOrder inOrder = Mockito.inOrder(installer);
+ inOrder.verify(installer).destroyAppDataSnapshot(
+ eq("com.foo.bar"), eq(10), eq(0L),
+ eq(Installer.FLAG_STORAGE_DE));
+ inOrder.verify(installer).destroyAppDataSnapshot(
+ eq("com.foo.bar"), eq(11), eq(239L),
+ eq(Installer.FLAG_STORAGE_DE | Installer.FLAG_STORAGE_CE));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void commitPendingBackupAndRestoreForUser_updatesRollbackData() throws Exception {
+ Installer installer = mock(Installer.class);
+ AppDataRollbackHelper helper = new AppDataRollbackHelper(installer);
+
+ ArrayList<RollbackData> changedRollbackData = new ArrayList<>();
+ changedRollbackData.add(createInProgressRollbackData("com.foo.bar"));
+
+ when(installer.snapshotAppData(anyString(), anyInt(), anyInt())).thenReturn(239L);
+
+ ArrayList<String> pendingBackups = new ArrayList<>();
+ pendingBackups.add("com.foo.bar");
+
+ helper.commitPendingBackupAndRestoreForUser(11, pendingBackups,
+ new HashMap<>() /* pendingRestores */, changedRollbackData);
+
+ assertEquals(1, changedRollbackData.size());
+ assertEquals(1, changedRollbackData.get(0).packages.size());
+ PackageRollbackInfo info = changedRollbackData.get(0).packages.get(0);
+
+ assertEquals(1, info.getCeSnapshotInodes().size());
+ assertEquals(239L, info.getCeSnapshotInodes().get(11));
+
+ InOrder inOrder = Mockito.inOrder(installer);
+ inOrder.verify(installer).snapshotAppData("com.foo.bar", 11 /* userId */,
+ Installer.FLAG_STORAGE_CE);
+ inOrder.verifyNoMoreInteractions();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index ee22861..2de4ae0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -57,7 +57,6 @@
*/
@SmallTest
@Presubmit
-@FlakyTest(detail="promote once confirmed non-flaky")
public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
private ActivityMetricsLogger mActivityMetricsLogger;
private ActivityMetricsLaunchObserver mLaunchObserver;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 3bf884f..defe981 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -42,7 +42,6 @@
* Build/Install/Run:
* atest WmTests:AppChangeTransitionTests
*/
-@FlakyTest(detail = "Promote when shown to be stable.")
@SmallTest
public class AppChangeTransitionTests extends WindowTestsBase {
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 f399463..3826fac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -59,7 +59,6 @@
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
-import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.Surface;
@@ -585,17 +584,15 @@
@Test
public void testOnDescendantOrientationRequestChanged() {
- final DisplayInfo info = new DisplayInfo();
- info.logicalWidth = 1080;
- info.logicalHeight = 1920;
- info.logicalDensityDpi = 240;
- final DisplayContent dc = createNewDisplay(info);
- dc.configureDisplayPolicy();
+ final DisplayContent dc = createNewDisplay();
mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+ final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
+ ? SCREEN_ORIENTATION_PORTRAIT
+ : SCREEN_ORIENTATION_LANDSCAPE;
final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
- window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE;
+ window.mAppToken.setOrientation(newOrientation);
ActivityRecord activityRecord = mock(ActivityRecord.class);
@@ -606,22 +603,21 @@
verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(),
same(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
final Configuration newDisplayConfig = captor.getValue();
- assertEquals(Configuration.ORIENTATION_LANDSCAPE, newDisplayConfig.orientation);
+ assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
}
@Test
public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
- final DisplayInfo info = new DisplayInfo();
- info.logicalWidth = 1080;
- info.logicalHeight = 1920;
- info.logicalDensityDpi = 240;
- final DisplayContent dc = createNewDisplay(info);
+ final DisplayContent dc = createNewDisplay();
dc.getDisplayRotation().setFixedToUserRotation(true);
mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+ final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
+ ? SCREEN_ORIENTATION_PORTRAIT
+ : SCREEN_ORIENTATION_LANDSCAPE;
final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
- window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE;
+ window.mAppToken.setOrientation(newOrientation);
ActivityRecord activityRecord = mock(ActivityRecord.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index d05711e..198e7ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -33,6 +33,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.ContentResolver;
@@ -611,6 +612,23 @@
// ========================
// Non-rotation API Tests
// ========================
+ @Test
+ public void testRespectsAppRequestedOrientationByDefault() throws Exception {
+ mBuilder.build();
+
+ assertTrue("Display rotation should respect app requested orientation by"
+ + " default.", mTarget.respectAppRequestedOrientation());
+ }
+
+ @Test
+ public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
+ mBuilder.build();
+ mTarget.setFixedToUserRotation(true);
+
+ assertFalse("Display rotation shouldn't respect app requested orientation if"
+ + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
+ }
+
/**
* Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
* according to given parameters.
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index a498a1a..0c363de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -39,7 +39,6 @@
import org.junit.Test;
@SmallTest
-@FlakyTest(detail = "Promote once confirmed non-flaky")
@Presubmit
public class InsetsSourceProviderTest extends WindowTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
index beaac8e..86bf3db 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
@@ -40,7 +40,6 @@
* atest WmTests:PendingRemoteAnimationRegistryTest
*/
@SmallTest
-@FlakyTest
@Presubmit
public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
index 434ba93..c3d2f33 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
@@ -47,7 +47,6 @@
* atest WmTests:PersisterQueueTests
*/
@MediumTest
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
@Presubmit
public class PersisterQueueTests implements PersisterQueue.Listener {
private static final long INTER_WRITE_DELAY_MS = 50;
diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
index 530fd6d..dad6c95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
@@ -31,7 +31,6 @@
* atest WmTests:SafeActivityOptionsTest
*/
@MediumTest
-@FlakyTest
@Presubmit
public class SafeActivityOptionsTest {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 9b84215..edb395a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -179,7 +179,6 @@
}
@Test
- @FlakyTest(detail = "Promote once confirmed non-flaky")
public void testDeferFinish() {
// Start animation
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
index df7bc11..12ed3c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
@@ -41,7 +41,6 @@
* Build/Install/Run:
* atest WmTests:TaskPersisterTest
*/
-@FlakyTest(detail = "Promote to presubmit if stable")
@Presubmit
public class TaskPersisterTest {
private static final String TEST_USER_NAME = "AM-Test-User";
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index e182c45..bcf9dd2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -25,13 +25,6 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.hamcrest.Matchers.not;
@@ -41,6 +34,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
import android.app.ActivityManager;
import android.content.ComponentName;
@@ -53,7 +47,6 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.Xml;
import android.view.DisplayInfo;
-import android.view.Surface;
import androidx.test.filters.MediumTest;
@@ -62,6 +55,7 @@
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mockito;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -284,48 +278,33 @@
}
@Test
- public void testUpdatesForcedOrientationInBackground() {
- final DisplayInfo info = new DisplayInfo();
- info.logicalWidth = 1920;
- info.logicalHeight = 1080;
- final ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
- doCallRealMethod().when(display.mDisplayContent).setDisplayRotation(any());
- display.mDisplayContent.setDisplayRotation(mock(DisplayRotation.class));
- doCallRealMethod().when(display.mDisplayContent).onDescendantOrientationChanged(any(),
- any());
- doCallRealMethod().when(display.mDisplayContent).setRotation(anyInt());
- doAnswer(invocation -> {
- display.mDisplayContent.setRotation(Surface.ROTATION_0);
- return null;
- }).when(display.mDisplayContent).updateOrientationFromAppTokens(any(), any(), anyBoolean());
+ public void testIgnoresForcedOrientationWhenParentHandles() {
+ final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+ DisplayInfo info = new DisplayInfo();
+ info.logicalWidth = fullScreenBounds.width();
+ info.logicalHeight = fullScreenBounds.height();
+ ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
- final ActivityStack stack = new StackBuilder(mRootActivityContainer)
+ display.getRequestedOverrideConfiguration().orientation =
+ Configuration.ORIENTATION_LANDSCAPE;
+ display.onRequestedOverrideConfigurationChanged(
+ display.getRequestedOverrideConfiguration());
+ ActivityStack stack = new StackBuilder(mRootActivityContainer)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
- final TaskRecord task = stack.getChildAt(0);
- final ActivityRecord activity = task.getRootActivity();
+ TaskRecord task = stack.getChildAt(0);
+ ActivityRecord root = task.getTopActivity();
- // Wire up app window token and task.
- doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt(), any(), any());
- doCallRealMethod().when(activity.mAppWindowToken).onDescendantOrientationChanged(any(),
- any());
- doReturn(task.mTask).when(activity.mAppWindowToken).getParent();
+ final WindowContainer parentWindowContainer = mock(WindowContainer.class);
+ Mockito.doReturn(parentWindowContainer).when(task.mTask).getParent();
+ Mockito.doReturn(true).when(parentWindowContainer)
+ .handlesOrientationChangeFromDescendant();
- // Wire up task and stack.
- task.mTask.mTaskRecord = task;
- doCallRealMethod().when(task.mTask).onDescendantOrientationChanged(any(), any());
- doReturn(stack.getTaskStack()).when(task.mTask).getParent();
-
- // Wire up stack and display content.
- doCallRealMethod().when(stack.mTaskStack).onDescendantOrientationChanged(any(), any());
- doReturn(display.mDisplayContent).when(stack.mTaskStack).getParent();
-
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- assertTrue("Bounds of the task should be pillarboxed.",
- task.getBounds().width() < task.getBounds().height());
-
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- assertTrue("Bounds of the task should be fullscreen.",
- task.getBounds().equals(new Rect(0, 0, 1920, 1080)));
+ // Setting app to fixed portrait fits within parent, but TaskRecord shouldn't adjust the
+ // bounds because its parent says it will handle it at a later time.
+ setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+ assertEquals(root, task.getRootActivity());
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
+ assertEquals(fullScreenBounds, task.getBounds());
}
/** Ensures that the alias intent won't have target component resolved. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index d1fe48a..bfb9193 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -28,6 +28,7 @@
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.PowerManager.WakeReason;
import android.os.RemoteException;
import android.util.proto.ProtoOutputStream;
import android.view.IWindow;
@@ -182,11 +183,11 @@
}
@Override
- public void startedWakingUp() {
+ public void startedWakingUp(@WakeReason int reason) {
}
@Override
- public void finishedWakingUp() {
+ public void finishedWakingUp(@WakeReason int reason) {
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
index de3567e..af8ccc9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
@@ -37,7 +37,6 @@
* Build/Install/Run:
* atest FrameworksServicesTests:WindowContainerControllerTests
*/
-@FlakyTest(bugId = 74078662)
@SmallTest
@Presubmit
public class WindowContainerControllerTests extends WindowTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 8628575..a9a76c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -51,6 +51,7 @@
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.mockito.Mockito;
import java.util.Comparator;
@@ -739,6 +740,18 @@
verify(root).onDescendantOrientationChanged(binder, activityRecord);
}
+ @Test
+ public void testHandlesOrientationChangeFromDescendantProgation() {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+ final TestWindowContainer root = spy(builder.build());
+
+ final TestWindowContainer child = root.addChildWindow();
+ assertFalse(child.handlesOrientationChangeFromDescendant());
+
+ Mockito.doReturn(true).when(root).handlesOrientationChangeFromDescendant();
+ assertTrue(child.handlesOrientationChangeFromDescendant());
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index c09cd46..8876214 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -21,6 +21,7 @@
import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.Surface.ROTATION_0;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -48,6 +49,7 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -60,13 +62,15 @@
import android.view.DisplayCutout;
import android.view.InsetsSource;
import android.view.SurfaceControl;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.wm.utils.WmDisplayCutout;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
import org.junit.Test;
import java.util.LinkedList;
@@ -77,10 +81,23 @@
* Build/Install/Run:
* atest FrameworksServicesTests:WindowStateTests
*/
-@FlakyTest(bugId = 74078662)
@SmallTest
@Presubmit
public class WindowStateTests extends WindowTestsBase {
+ private static int sPreviousNewInsetsMode;
+
+ @BeforeClass
+ public static void setUpOnce() {
+ sPreviousNewInsetsMode = ViewRootImpl.sNewInsetsMode;
+ // To let the insets provider control the insets visibility, the insets mode has to be
+ // NEW_INSETS_MODE_FULL.
+ ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL;
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ ViewRootImpl.sNewInsetsMode = sPreviousNewInsetsMode;
+ }
@Test
public void testIsParentWindowHidden() {
@@ -263,12 +280,12 @@
reset(sPowerManagerWrapper);
first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString());
+ verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
assertTrue(appWindowToken.canTurnScreenOn());
reset(sPowerManagerWrapper);
second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
+ verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
assertFalse(appWindowToken.canTurnScreenOn());
// Call prepareWindowToDisplayDuringRelayout for two window that have FLAG_TURN_SCREEN_ON
@@ -279,12 +296,12 @@
reset(sPowerManagerWrapper);
first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
+ verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
assertFalse(appWindowToken.canTurnScreenOn());
reset(sPowerManagerWrapper);
second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString());
+ verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
assertFalse(appWindowToken.canTurnScreenOn());
// Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an
@@ -300,11 +317,11 @@
reset(sPowerManagerWrapper);
firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
+ verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
reset(sPowerManagerWrapper);
secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
+ verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
}
@Test
@@ -435,6 +452,6 @@
root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/);
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
+ verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 3048f1a..d556886 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -39,7 +39,6 @@
* Build/Install/Run:
* atest FrameworksServicesTests:WindowTokenTests
*/
-@FlakyTest(bugId = 74078662)
@SmallTest
@Presubmit
public class WindowTokenTests extends WindowTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
index b81a8e7..b6b9a86 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -60,7 +60,6 @@
* Build/Install/Run:
* atest FrameworksServicesTests:WindowTracingTest
*/
-@FlakyTest(bugId = 74078662)
@SmallTest
@Presubmit
public class WindowTracingTest {
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
index 33f34b4..05d8237 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
@@ -37,7 +37,6 @@
* atest WmTests:RotationCacheTest
*/
@SmallTest
-@FlakyTest(bugId = 74078662)
@Presubmit
public class RotationCacheTest {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index a6d7ee6..df2f455 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -493,6 +493,8 @@
switch (event.mEventType) {
case Event.ACTIVITY_RESUMED:
synchronized (mVisibleActivities) {
+ // check if this activity has already been resumed
+ if (mVisibleActivities.get(event.mInstanceId) != null) break;
mVisibleActivities.put(event.mInstanceId, event.getClassName());
try {
mAppTimeLimit.noteUsageStart(packageName, userId);
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 37caeb2..f5b4308 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -58,8 +58,6 @@
"util.cc",
"layout_validation.cc",
],
- // b/123880763, clang-tidy analyzer has segmentation fault with dex_builder.cc
- tidy_checks: ["-clang-analyzer-*"],
host_supported: true,
}
diff --git a/startop/view_compiler/OWNERS b/startop/view_compiler/OWNERS
new file mode 100644
index 0000000..e5aead9
--- /dev/null
+++ b/startop/view_compiler/OWNERS
@@ -0,0 +1,2 @@
+eholk@google.com
+mathieuc@google.com
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 4c1a0dc7..6047e8c 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -426,7 +426,7 @@
// Some of the registers don't fit in the four bit short form of the invoke
// instruction, so we need to do an invoke/range. To do this, we need to
// first move all the arguments into contiguous temporary registers.
- std::array<Value, kMaxArgs> scratch{GetScratchRegisters<kMaxArgs>()};
+ std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>();
const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id());
CHECK(prototype.has_value());
diff --git a/telecomm/java/android/telecom/ConferenceParticipant.java b/telecomm/java/android/telecom/ConferenceParticipant.java
index 20b04eb..6317770 100644
--- a/telecomm/java/android/telecom/ConferenceParticipant.java
+++ b/telecomm/java/android/telecom/ConferenceParticipant.java
@@ -19,6 +19,10 @@
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneConstants;
/**
* Parcelable representation of a participant's state in a conference call.
@@ -27,6 +31,11 @@
public class ConferenceParticipant implements Parcelable {
/**
+ * RFC5767 states that a SIP URI with an unknown number should use an address of
+ * {@code anonymous@anonymous.invalid}. E.g. the host name is anonymous.invalid.
+ */
+ private static final String ANONYMOUS_INVALID_HOST = "anonymous.invalid";
+ /**
* The conference participant's handle (e.g., phone number).
*/
private final Uri mHandle;
@@ -50,6 +59,16 @@
private final int mState;
/**
+ * The connect time of the participant.
+ */
+ private long mConnectTime;
+
+ /**
+ * The connect elapsed time of the participant.
+ */
+ private long mConnectElapsedTime;
+
+ /**
* Creates an instance of {@code ConferenceParticipant}.
*
* @param handle The conference participant's handle (e.g., phone number).
@@ -92,6 +111,54 @@
}
/**
+ * Determines the number presentation for a conference participant. Per RFC5767, if the host
+ * name contains {@code anonymous.invalid} we can assume that there is no valid caller ID
+ * information for the caller, otherwise we'll assume that the URI can be shown.
+ *
+ * @return The number presentation.
+ */
+ @VisibleForTesting
+ public int getParticipantPresentation() {
+ Uri address = getHandle();
+ if (address == null) {
+ return PhoneConstants.PRESENTATION_RESTRICTED;
+ }
+
+ String number = address.getSchemeSpecificPart();
+ // If no number, bail early and set restricted presentation.
+ if (TextUtils.isEmpty(number)) {
+ return PhoneConstants.PRESENTATION_RESTRICTED;
+ }
+ // Per RFC3261, the host name portion can also potentially include extra information:
+ // E.g. sip:anonymous1@anonymous.invalid;legid=1
+ // In this case, hostName will be anonymous.invalid and there is an extra parameter for
+ // legid=1.
+ // Parameters are optional, and the address (e.g. test@test.com) will always be the first
+ // part, with any parameters coming afterwards.
+ String [] hostParts = number.split("[;]");
+ String addressPart = hostParts[0];
+
+ // Get the number portion from the address part.
+ // This will typically be formatted similar to: 6505551212@test.com
+ String [] numberParts = addressPart.split("[@]");
+
+ // If we can't parse the host name out of the URI, then there is probably other data
+ // present, and is likely a valid SIP URI.
+ if (numberParts.length != 2) {
+ return PhoneConstants.PRESENTATION_ALLOWED;
+ }
+ String hostName = numberParts[1];
+
+ // If the hostname portion of the SIP URI is the invalid host string, presentation is
+ // restricted.
+ if (hostName.equals(ANONYMOUS_INVALID_HOST)) {
+ return PhoneConstants.PRESENTATION_RESTRICTED;
+ }
+
+ return PhoneConstants.PRESENTATION_ALLOWED;
+ }
+
+ /**
* Writes the {@code ConferenceParticipant} to a parcel.
*
* @param dest The Parcel in which the object should be written.
@@ -121,6 +188,10 @@
sb.append(Log.pii(mEndpoint));
sb.append(" State: ");
sb.append(Connection.stateToString(mState));
+ sb.append(" ConnectTime: ");
+ sb.append(getConnectTime());
+ sb.append(" ConnectElapsedTime: ");
+ sb.append(getConnectElapsedTime());
sb.append("]");
return sb.toString();
}
@@ -155,4 +226,26 @@
public int getState() {
return mState;
}
+
+ /**
+ * The connect time of the participant to the conference.
+ */
+ public long getConnectTime() {
+ return mConnectTime;
+ }
+
+ public void setConnectTime(long connectTime) {
+ this.mConnectTime = connectTime;
+ }
+
+ /**
+ * The connect elpased time of the participant to the conference.
+ */
+ public long getConnectElapsedTime() {
+ return mConnectElapsedTime;
+ }
+
+ public void setConnectElapsedTime(long connectElapsedTime) {
+ mConnectElapsedTime = connectElapsedTime;
+ }
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 05d5a13..bd0d4ae 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -16,10 +16,6 @@
package android.telecom;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.IVideoCallback;
-import com.android.internal.telecom.IVideoProvider;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -43,6 +39,10 @@
import android.util.ArraySet;
import android.view.Surface;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.IVideoCallback;
+import com.android.internal.telecom.IVideoProvider;
+
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index 57ae5d3..aac0956d 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -78,7 +78,7 @@
try {
RoleManagerCallback.Future cb = new RoleManagerCallback.Future();
context.getSystemService(RoleManager.class).addRoleHolderAsUser(
- RoleManager.ROLE_DIALER, packageName, UserHandle.of(user),
+ RoleManager.ROLE_DIALER, packageName, 0, UserHandle.of(user),
AsyncTask.THREAD_POOL_EXECUTOR, cb);
cb.get(5, TimeUnit.SECONDS);
return true;
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 1de67a5..5a97c94 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,9 +16,9 @@
package android.telecom;
+import android.media.ToneGenerator;
import android.os.Parcel;
import android.os.Parcelable;
-import android.media.ToneGenerator;
import android.text.TextUtils;
import java.util.Objects;
@@ -91,6 +91,12 @@
*/
public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
+ /**
+ * Reason code, which indicates that the conference call is simulating single party conference.
+ * @hide
+ */
+ public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
+
private int mDisconnectCode;
private CharSequence mDisconnectLabel;
private CharSequence mDisconnectDescription;
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 5482270..4539ab3 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2118,6 +2118,608 @@
}
/**
+ * Columns for the "rcs_*" tables used by {@link android.telephony.ims.RcsMessageStore} classes.
+ *
+ * @hide - not meant for public use
+ */
+ public interface RcsColumns {
+ /**
+ * The authority for the content provider
+ */
+ String AUTHORITY = "rcs";
+
+ /**
+ * The URI to start building upon to use {@link com.android.providers.telephony.RcsProvider}
+ */
+ Uri CONTENT_AND_AUTHORITY = Uri.parse("content://" + AUTHORITY);
+
+ /**
+ * The value to be used whenever a transaction that expects an integer to be returned
+ * failed.
+ */
+ int TRANSACTION_FAILED = Integer.MIN_VALUE;
+
+ /**
+ * The value that denotes a timestamp was not set before (e.g. a message that is not
+ * delivered yet will not have a DELIVERED_TIMESTAMP)
+ */
+ long TIMESTAMP_NOT_SET = 0;
+
+ /**
+ * The table that {@link android.telephony.ims.RcsThread} gets persisted to
+ */
+ interface RcsThreadColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsThread}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String RCS_THREAD_URI_PART = "thread";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.RcsThread} via the content
+ * provider.
+ */
+ Uri RCS_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, RCS_THREAD_URI_PART);
+
+ /**
+ * The unique identifier of an {@link android.telephony.ims.RcsThread}
+ */
+ String RCS_THREAD_ID_COLUMN = "rcs_thread_id";
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.Rcs1To1Thread} gets persisted to
+ */
+ interface Rcs1To1ThreadColumns extends RcsThreadColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.Rcs1To1Thread}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String RCS_1_TO_1_THREAD_URI_PART = "p2p_thread";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.Rcs1To1Thread}s via the
+ * content provider
+ */
+ Uri RCS_1_TO_1_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ RCS_1_TO_1_THREAD_URI_PART);
+
+ /**
+ * The SMS/MMS thread to fallback to in case of an RCS outage
+ */
+ String FALLBACK_THREAD_ID_COLUMN = "rcs_fallback_thread_id";
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.RcsGroupThread} gets persisted to
+ */
+ interface RcsGroupThreadColumns extends RcsThreadColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsGroupThread}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String RCS_GROUP_THREAD_URI_PART = "group_thread";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.RcsGroupThread}s via the
+ * content provider
+ */
+ Uri RCS_GROUP_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ RCS_GROUP_THREAD_URI_PART);
+
+ /**
+ * The owner/admin of the {@link android.telephony.ims.RcsGroupThread}
+ */
+ String OWNER_PARTICIPANT_COLUMN = "owner_participant";
+
+ /**
+ * The user visible name of the group
+ */
+ String GROUP_NAME_COLUMN = "group_name";
+
+ /**
+ * The user visible icon of the group
+ */
+ String GROUP_ICON_COLUMN = "group_icon";
+
+ /**
+ * The RCS conference URI for this group
+ */
+ String CONFERENCE_URI_COLUMN = "conference_uri";
+ }
+
+ /**
+ * The view that enables polling from all types of RCS threads at once
+ */
+ interface RcsUnifiedThreadColumns extends RcsThreadColumns, Rcs1To1ThreadColumns,
+ RcsGroupThreadColumns {
+ /**
+ * The type of this {@link android.telephony.ims.RcsThread}
+ */
+ String THREAD_TYPE_COLUMN = "thread_type";
+
+ /**
+ * Integer returned as a result from a database query that denotes the thread is 1 to 1
+ */
+ int THREAD_TYPE_1_TO_1 = 0;
+
+ /**
+ * Integer returned as a result from a database query that denotes the thread is 1 to 1
+ */
+ int THREAD_TYPE_GROUP = 1;
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.RcsParticipant} gets persisted to
+ */
+ interface RcsParticipantColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsParticipant}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String RCS_PARTICIPANT_URI_PART = "participant";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.RcsParticipant}s via the
+ * content provider
+ */
+ Uri RCS_PARTICIPANT_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ RCS_PARTICIPANT_URI_PART);
+
+ /**
+ * The unique identifier of the entry in the database
+ */
+ String RCS_PARTICIPANT_ID_COLUMN = "rcs_participant_id";
+
+ /**
+ * A foreign key on canonical_address table, also used by SMS/MMS
+ */
+ String CANONICAL_ADDRESS_ID_COLUMN = "canonical_address_id";
+
+ /**
+ * The user visible RCS alias for this participant.
+ */
+ String RCS_ALIAS_COLUMN = "rcs_alias";
+ }
+
+ /**
+ * Additional constants to enable access to {@link android.telephony.ims.RcsParticipant}
+ * related data
+ */
+ interface RcsParticipantHelpers extends RcsParticipantColumns {
+ /**
+ * The view that unifies "rcs_participant" and "canonical_addresses" tables for easy
+ * access to participant address.
+ */
+ String RCS_PARTICIPANT_WITH_ADDRESS_VIEW = "rcs_participant_with_address_view";
+
+ /**
+ * The view that unifies "rcs_participant", "canonical_addresses" and
+ * "rcs_thread_participant" junction table to get full information on participants that
+ * contribute to threads.
+ */
+ String RCS_PARTICIPANT_WITH_THREAD_VIEW = "rcs_participant_with_thread_view";
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.RcsMessage} gets persisted to
+ */
+ interface RcsMessageColumns {
+ /**
+ * Denotes the type of this message (i.e.
+ * {@link android.telephony.ims.RcsIncomingMessage} or
+ * {@link android.telephony.ims.RcsOutgoingMessage}
+ */
+ String MESSAGE_TYPE_COLUMN = "rcs_message_type";
+
+ /**
+ * The unique identifier for the message in the database - i.e. the primary key.
+ */
+ String MESSAGE_ID_COLUMN = "rcs_message_row_id";
+
+ /**
+ * The globally unique RCS identifier for the message. Please see 4.4.5.2 - GSMA
+ * RCC.53 (RCS Device API 1.6 Specification)
+ */
+ String GLOBAL_ID_COLUMN = "rcs_message_global_id";
+
+ /**
+ * The subscription where this message was sent from/to.
+ */
+ String SUB_ID_COLUMN = "sub_id";
+
+ /**
+ * The sending status of the message.
+ * @see android.telephony.ims.RcsMessage.RcsMessageStatus
+ */
+ String STATUS_COLUMN = "status";
+
+ /**
+ * The creation timestamp of the message.
+ */
+ String ORIGINATION_TIMESTAMP_COLUMN = "origination_timestamp";
+
+ /**
+ * The text content of the message.
+ */
+ String MESSAGE_TEXT_COLUMN = "rcs_text";
+
+ /**
+ * The latitude content of the message, if it contains a location.
+ */
+ String LATITUDE_COLUMN = "latitude";
+
+ /**
+ * The longitude content of the message, if it contains a location.
+ */
+ String LONGITUDE_COLUMN = "longitude";
+ }
+
+ /**
+ * The table that additional information of {@link android.telephony.ims.RcsIncomingMessage}
+ * gets persisted to.
+ */
+ interface RcsIncomingMessageColumns extends RcsMessageColumns {
+ /**
+ The path that should be used for referring to
+ * {@link android.telephony.ims.RcsIncomingMessage}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String INCOMING_MESSAGE_URI_PART = "incoming_message";
+
+ /**
+ * The URI to query incoming messages through
+ * {@link com.android.providers.telephony.RcsProvider}
+ */
+ Uri INCOMING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ INCOMING_MESSAGE_URI_PART);
+
+ /**
+ * The ID of the {@link android.telephony.ims.RcsParticipant} that sent this message
+ */
+ String SENDER_PARTICIPANT_ID_COLUMN = "sender_participant";
+
+ /**
+ * The timestamp of arrival for this message.
+ */
+ String ARRIVAL_TIMESTAMP_COLUMN = "arrival_timestamp";
+
+ /**
+ * The time when the recipient has read this message.
+ */
+ String SEEN_TIMESTAMP_COLUMN = "seen_timestamp";
+ }
+
+ /**
+ * The table that additional information of {@link android.telephony.ims.RcsOutgoingMessage}
+ * gets persisted to.
+ */
+ interface RcsOutgoingMessageColumns extends RcsMessageColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsOutgoingMessage}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String OUTGOING_MESSAGE_URI_PART = "outgoing_message";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.RcsOutgoingMessage}s via the
+ * content provider
+ */
+ Uri OUTGOING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ OUTGOING_MESSAGE_URI_PART);
+ }
+
+ /**
+ * The delivery information of an {@link android.telephony.ims.RcsOutgoingMessage}
+ */
+ interface RcsMessageDeliveryColumns extends RcsOutgoingMessageColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsOutgoingMessageDelivery}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String DELIVERY_URI_PART = "delivery";
+
+ /**
+ * The timestamp of delivery of this message.
+ */
+ String DELIVERED_TIMESTAMP_COLUMN = "delivered_timestamp";
+
+ /**
+ * The time when the recipient has read this message.
+ */
+ String SEEN_TIMESTAMP_COLUMN = "seen_timestamp";
+ }
+
+ /**
+ * The views that allow querying {@link android.telephony.ims.RcsIncomingMessage} and
+ * {@link android.telephony.ims.RcsOutgoingMessage} at the same time.
+ */
+ interface RcsUnifiedMessageColumns extends RcsIncomingMessageColumns,
+ RcsOutgoingMessageColumns {
+ /**
+ * The path that is used to query all {@link android.telephony.ims.RcsMessage} in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String UNIFIED_MESSAGE_URI_PART = "message";
+
+ /**
+ * The URI to query all types of {@link android.telephony.ims.RcsMessage}s
+ */
+ Uri UNIFIED_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ UNIFIED_MESSAGE_URI_PART);
+
+ /**
+ * The name of the view that unites rcs_message and rcs_incoming_message tables.
+ */
+ String UNIFIED_INCOMING_MESSAGE_VIEW = "unified_incoming_message_view";
+
+ /**
+ * The name of the view that unites rcs_message and rcs_outgoing_message tables.
+ */
+ String UNIFIED_OUTGOING_MESSAGE_VIEW = "unified_outgoing_message_view";
+
+ /**
+ * The column that shows from which table the message entry came from.
+ */
+ String MESSAGE_TYPE_COLUMN = "message_type";
+
+ /**
+ * Integer returned as a result from a database query that denotes that the message is
+ * an incoming message
+ */
+ int MESSAGE_TYPE_INCOMING = 1;
+
+ /**
+ * Integer returned as a result from a database query that denotes that the message is
+ * an outgoing message
+ */
+ int MESSAGE_TYPE_OUTGOING = 0;
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.RcsFileTransferPart} gets persisted to.
+ */
+ interface RcsFileTransferColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsFileTransferPart}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String FILE_TRANSFER_URI_PART = "file_transfer";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.RcsFileTransferPart}s via the
+ * content provider
+ */
+ Uri FILE_TRANSFER_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ FILE_TRANSFER_URI_PART);
+
+ /**
+ * The globally unique file transfer ID for this RCS file transfer.
+ */
+ String FILE_TRANSFER_ID_COLUMN = "rcs_file_transfer_id";
+
+ /**
+ * The RCS session ID for this file transfer. The ID is implementation dependent but
+ * should be unique.
+ */
+ String SESSION_ID_COLUMN = "session_id";
+
+ /**
+ * The URI that points to the content of this file transfer
+ */
+ String CONTENT_URI_COLUMN = "content_uri";
+
+ /**
+ * The file type of this file transfer in bytes. The validity of types is not enforced
+ * in {@link android.telephony.ims.RcsMessageStore} APIs.
+ */
+ String CONTENT_TYPE_COLUMN = "content_type";
+
+ /**
+ * The size of the file transfer in bytes.
+ */
+ String FILE_SIZE_COLUMN = "file_size";
+
+ /**
+ * Number of bytes that was successfully transmitted for this file transfer
+ */
+ String SUCCESSFULLY_TRANSFERRED_BYTES = "transfer_offset";
+
+ /**
+ * The status of this file transfer
+ * @see android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus
+ */
+ String TRANSFER_STATUS_COLUMN = "transfer_status";
+
+ /**
+ * The on-screen width of the file transfer, if it contains multi-media
+ */
+ String WIDTH_COLUMN = "width";
+
+ /**
+ * The on-screen height of the file transfer, if it contains multi-media
+ */
+ String HEIGHT_COLUMN = "height";
+
+ /**
+ * The duration of the content in milliseconds if this file transfer contains
+ * multi-media
+ */
+ String DURATION_MILLIS_COLUMN = "duration";
+
+ /**
+ * The URI to the preview of the content of this file transfer
+ */
+ String PREVIEW_URI_COLUMN = "preview_uri";
+
+ /**
+ * The type of the preview of the content of this file transfer. The validity of types
+ * is not enforced in {@link android.telephony.ims.RcsMessageStore} APIs.
+ */
+ String PREVIEW_TYPE_COLUMN = "preview_type";
+ }
+
+ /**
+ * The table that holds the information for
+ * {@link android.telephony.ims.RcsGroupThreadEvent} and its subclasses.
+ */
+ interface RcsThreadEventColumns {
+ /**
+ * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+ * refer to participant joined events (example URI:
+ * {@code content://rcs/group_thread/3/participant_joined_event})
+ */
+ String PARTICIPANT_JOINED_URI_PART = "participant_joined_event";
+
+ /**
+ * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+ * refer to participant left events. (example URI:
+ * {@code content://rcs/group_thread/3/participant_left_event/4})
+ */
+ String PARTICIPANT_LEFT_URI_PART = "participant_left_event";
+
+ /**
+ * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+ * refer to name changed events. (example URI:
+ * {@code content://rcs/group_thread/3/name_changed_event})
+ */
+ String NAME_CHANGED_URI_PART = "name_changed_event";
+
+ /**
+ * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+ * refer to icon changed events. (example URI:
+ * {@code content://rcs/group_thread/3/icon_changed_event})
+ */
+ String ICON_CHANGED_URI_PART = "icon_changed_event";
+
+ /**
+ * The unique ID of this event in the database, i.e. the primary key
+ */
+ String EVENT_ID_COLUMN = "event_id";
+
+ /**
+ * The type of this event
+ *
+ * @see RcsEventTypes
+ */
+ String EVENT_TYPE_COLUMN = "event_type";
+
+ /**
+ * The timestamp in milliseconds of when this event happened
+ */
+ String TIMESTAMP_COLUMN = "origination_timestamp";
+
+ /**
+ * The participant that generated this event
+ */
+ String SOURCE_PARTICIPANT_ID_COLUMN = "source_participant";
+
+ /**
+ * The receiving participant of this event if this was an
+ * {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent} or
+ * {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent}
+ */
+ String DESTINATION_PARTICIPANT_ID_COLUMN = "destination_participant";
+
+ /**
+ * The URI for the new icon of the group thread if this was an
+ * {@link android.telephony.ims.RcsGroupThreadIconChangedEvent}
+ */
+ String NEW_ICON_URI_COLUMN = "new_icon_uri";
+
+ /**
+ * The URI for the new name of the group thread if this was an
+ * {@link android.telephony.ims.RcsGroupThreadNameChangedEvent}
+ */
+ String NEW_NAME_COLUMN = "new_name";
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.RcsParticipantAliasChangedEvent} gets
+ * persisted to
+ */
+ interface RcsParticipantEventColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsParticipantAliasChangedEvent}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String ALIAS_CHANGE_EVENT_URI_PART = "alias_change_event";
+
+ /**
+ * The new alias of the participant
+ */
+ String NEW_ALIAS_COLUMN = "new_alias";
+ }
+
+ /**
+ * These values are used in {@link com.android.providers.telephony.RcsProvider} to determine
+ * what kind of event is present in the storage.
+ */
+ interface RcsEventTypes {
+ /**
+ * Integer constant that is stored in the
+ * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+ * is of type {@link android.telephony.ims.RcsParticipantAliasChangedEvent}
+ */
+ int PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE = 1;
+
+ /**
+ * Integer constant that is stored in the
+ * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+ * is of type {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent}
+ */
+ int PARTICIPANT_JOINED_EVENT_TYPE = 2;
+
+ /**
+ * Integer constant that is stored in the
+ * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+ * is of type {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent}
+ */
+ int PARTICIPANT_LEFT_EVENT_TYPE = 4;
+
+ /**
+ * Integer constant that is stored in the
+ * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+ * is of type {@link android.telephony.ims.RcsGroupThreadIconChangedEvent}
+ */
+ int ICON_CHANGED_EVENT_TYPE = 8;
+
+ /**
+ * Integer constant that is stored in the
+ * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+ * is of type {@link android.telephony.ims.RcsGroupThreadNameChangedEvent}
+ */
+ int NAME_CHANGED_EVENT_TYPE = 16;
+ }
+
+ /**
+ * The view that allows unified querying across all events
+ */
+ interface RcsUnifiedEventHelper extends RcsParticipantEventColumns, RcsThreadEventColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsEvent}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String RCS_EVENT_QUERY_URI_PATH = "event";
+
+ /**
+ * The URI to query {@link android.telephony.ims.RcsEvent}s via the content provider.
+ */
+ Uri RCS_EVENT_QUERY_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ RCS_EVENT_QUERY_URI_PATH);
+ }
+ }
+
+ /**
* Contains all MMS messages.
*/
public static final class Mms implements BaseMmsColumns {
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
index 709b3aa..cc28ee0 100644
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
@@ -15,42 +15,72 @@
*/
package android.telephony.ims;
-import android.os.Parcel;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
/**
* Rcs1To1Thread represents a single RCS conversation thread with a total of two
- * {@link RcsParticipant}s.
- * @hide - TODO(sahinc) make this public
+ * {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal
+ * Profile Service Definition Document)
+ *
+ * @hide - TODO(109759350) make this public
*/
public class Rcs1To1Thread extends RcsThread {
+ private int mThreadId;
+
+ /**
+ * Public constructor only for RcsMessageStoreController to initialize new threads.
+ *
+ * @hide
+ */
public Rcs1To1Thread(int threadId) {
super(threadId);
+ mThreadId = threadId;
}
- public static final Creator<Rcs1To1Thread> CREATOR = new Creator<Rcs1To1Thread>() {
- @Override
- public Rcs1To1Thread createFromParcel(Parcel in) {
- return new Rcs1To1Thread(in);
- }
-
- @Override
- public Rcs1To1Thread[] newArray(int size) {
- return new Rcs1To1Thread[size];
- }
- };
-
- protected Rcs1To1Thread(Parcel in) {
- super(in);
- }
-
+ /**
+ * @return Returns {@code false} as this is always a 1 to 1 thread.
+ */
@Override
- public int describeContents() {
- return 0;
+ public boolean isGroup() {
+ return false;
}
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(RCS_1_TO_1_TYPE);
- super.writeToParcel(dest, flags);
+ /**
+ * {@link Rcs1To1Thread}s can fall back to SMS as a back-up protocol. This function returns the
+ * thread id to be used to query {@code content://mms-sms/conversation/#} to get the fallback
+ * thread.
+ *
+ * @return The thread id to be used to query the mms-sms authority
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getFallbackThreadId() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.get1To1ThreadFallbackThreadId(mThreadId));
+ }
+
+ /**
+ * If the RCS client allows falling back to SMS, it needs to create an MMS-SMS thread in the
+ * SMS/MMS Provider( see {@link android.provider.Telephony.MmsSms#CONTENT_CONVERSATIONS_URI}.
+ * Use this function to link the {@link Rcs1To1Thread} to the MMS-SMS thread. This function
+ * also updates the storage.
+ *
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setFallbackThreadId(long fallbackThreadId) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.set1To1ThreadFallbackThreadId(mThreadId, fallbackThreadId));
+ }
+
+ /**
+ * @return Returns the {@link RcsParticipant} that receives the messages sent in this thread.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @NonNull
+ @WorkerThread
+ public RcsParticipant getRecipient() throws RcsMessageStoreException {
+ return new RcsParticipant(
+ RcsControllerCall.call(iRcs -> iRcs.get1To1ThreadOtherParticipantId(mThreadId)));
}
}
diff --git a/telephony/java/android/telephony/ims/RcsControllerCall.java b/telephony/java/android/telephony/ims/RcsControllerCall.java
new file mode 100644
index 0000000..5512c4c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsControllerCall.java
@@ -0,0 +1,64 @@
+/*
+ * 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.telephony.ims;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.ims.aidl.IRcs;
+
+/**
+ * A wrapper class around RPC calls that {@link RcsMessageStore} APIs to minimize boilerplate code.
+ *
+ * @hide - not meant for public use
+ */
+class RcsControllerCall {
+ static <R> R call(RcsServiceCall<R> serviceCall) throws RcsMessageStoreException {
+ IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_RCS_SERVICE));
+ if (iRcs == null) {
+ throw new RcsMessageStoreException("Could not connect to RCS storage service");
+ }
+
+ try {
+ return serviceCall.methodOnIRcs(iRcs);
+ } catch (RemoteException exception) {
+ throw new RcsMessageStoreException(exception.getMessage());
+ }
+ }
+
+ static void callWithNoReturn(RcsServiceCallWithNoReturn serviceCall)
+ throws RcsMessageStoreException {
+ IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_RCS_SERVICE));
+ if (iRcs == null) {
+ throw new RcsMessageStoreException("Could not connect to RCS storage service");
+ }
+
+ try {
+ serviceCall.methodOnIRcs(iRcs);
+ } catch (RemoteException exception) {
+ throw new RcsMessageStoreException(exception.getMessage());
+ }
+ }
+
+ interface RcsServiceCall<R> {
+ R methodOnIRcs(IRcs iRcs) throws RemoteException;
+ }
+
+ interface RcsServiceCallWithNoReturn {
+ void methodOnIRcs(IRcs iRcs) throws RemoteException;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsPart.aidl b/telephony/java/android/telephony/ims/RcsEvent.aidl
similarity index 96%
rename from telephony/java/android/telephony/ims/RcsPart.aidl
rename to telephony/java/android/telephony/ims/RcsEvent.aidl
index 8b8077d..08974e0 100644
--- a/telephony/java/android/telephony/ims/RcsPart.aidl
+++ b/telephony/java/android/telephony/ims/RcsEvent.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsPart;
+parcelable RcsEvent;
diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java
new file mode 100644
index 0000000..744ac76
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsEvent.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s.
+ * @hide - TODO(109759350) make this public
+ */
+public abstract class RcsEvent implements Parcelable {
+ protected long mTimestamp;
+
+ protected RcsEvent(long timestamp) {
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * @return Returns the time of when this event happened. The timestamp is defined as
+ * milliseconds passed after midnight, January 1, 1970 UTC
+ */
+ public long getTimestamp() {
+ return mTimestamp;
+ }
+
+ /**
+ * Persists the event to the data store
+ *
+ * @hide
+ */
+ abstract void persist() throws RcsMessageStoreException;
+
+ RcsEvent(Parcel in) {
+ mTimestamp = in.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mTimestamp);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsEventQueryParameters.aidl
similarity index 94%
rename from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
rename to telephony/java/android/telephony/ims/RcsEventQueryParameters.aidl
index 82d985d..9a3600b 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsEventQueryParameters.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsEventQueryParameters;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParameters.java b/telephony/java/android/telephony/ims/RcsEventQueryParameters.java
new file mode 100644
index 0000000..6aee56f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsEventQueryParameters.java
@@ -0,0 +1,322 @@
+/*
+ * 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.telephony.ims;
+
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.ICON_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.NAME_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_JOINED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_LEFT_EVENT_TYPE;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+
+/**
+ * The parameters to pass into
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)} in order to select a
+ * subset of {@link RcsEvent}s present in the message store.
+ *
+ * @hide TODO - make the Builder and builder() public. The rest should stay internal only.
+ */
+public class RcsEventQueryParameters implements Parcelable {
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)} return all types of
+ * {@link RcsEvent}s
+ */
+ public static final int ALL_EVENTS = -1;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)} return sub-types of
+ * {@link RcsGroupThreadEvent}s
+ */
+ public static final int ALL_GROUP_THREAD_EVENTS = 0;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)} return only
+ * {@link RcsParticipantAliasChangedEvent}s
+ */
+ public static final int PARTICIPANT_ALIAS_CHANGED_EVENT =
+ PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)} return only
+ * {@link RcsGroupThreadParticipantJoinedEvent}s
+ */
+ public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT =
+ PARTICIPANT_JOINED_EVENT_TYPE;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)} return only
+ * {@link RcsGroupThreadParticipantLeftEvent}s
+ */
+ public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT =
+ PARTICIPANT_LEFT_EVENT_TYPE;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)} return only
+ * {@link RcsGroupThreadNameChangedEvent}s
+ */
+ public static final int GROUP_THREAD_NAME_CHANGED_EVENT = NAME_CHANGED_EVENT_TYPE;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)} return only
+ * {@link RcsGroupThreadIconChangedEvent}s
+ */
+ public static final int GROUP_THREAD_ICON_CHANGED_EVENT = ICON_CHANGED_EVENT_TYPE;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ALL_EVENTS, ALL_GROUP_THREAD_EVENTS, PARTICIPANT_ALIAS_CHANGED_EVENT,
+ GROUP_THREAD_PARTICIPANT_JOINED_EVENT, GROUP_THREAD_PARTICIPANT_LEFT_EVENT,
+ GROUP_THREAD_NAME_CHANGED_EVENT, GROUP_THREAD_ICON_CHANGED_EVENT})
+ public @interface EventType {
+ }
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted
+ * in the order of creation for faster query results.
+ */
+ public static final int SORT_BY_CREATION_ORDER = 0;
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted
+ * with respect to {@link RcsEvent#getTimestamp()}
+ */
+ public static final int SORT_BY_TIMESTAMP = 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
+ public @interface SortingProperty {
+ }
+
+ /**
+ * The key to pass into a Bundle, for usage in RcsProvider.query(Bundle)
+ * @hide - not meant for public use
+ */
+ public static final String EVENT_QUERY_PARAMETERS_KEY = "event_query_parameters";
+
+ // Which types of events the results should be limited to
+ private @EventType int mEventType;
+ // The property which the results should be sorted against
+ private int mSortingProperty;
+ // Whether the results should be sorted in ascending order
+ private boolean mIsAscending;
+ // The number of results that should be returned with this query
+ private int mLimit;
+ // The thread that the results are limited to
+ private int mThreadId;
+
+ RcsEventQueryParameters(@EventType int eventType, int threadId,
+ @SortingProperty int sortingProperty, boolean isAscending, int limit) {
+ mEventType = eventType;
+ mSortingProperty = sortingProperty;
+ mIsAscending = isAscending;
+ mLimit = limit;
+ mThreadId = threadId;
+ }
+
+ /**
+ * @return Returns the type of {@link RcsEvent}s that this {@link RcsEventQueryParameters} is
+ * set to query for.
+ */
+ public @EventType int getEventType() {
+ return mEventType;
+ }
+
+ /**
+ * @return Returns the type of {@link RcsEvent}s that this {@link RcsEventQueryParameters} is
+ * set to query for.
+ */
+ public int getLimit() {
+ return mLimit;
+ }
+
+ /**
+ * @return Returns the property where the results should be sorted against.
+ * @see SortingProperty
+ */
+ public int getSortingProperty() {
+ return mSortingProperty;
+ }
+
+ /**
+ * @return Returns {@code true} if the result set will be sorted in ascending order,
+ * {@code false} if it will be sorted in descending order.
+ */
+ public boolean getSortDirection() {
+ return mIsAscending;
+ }
+
+ /**
+ * @return Returns the ID of the {@link RcsGroupThread} that the results are limited to. As this
+ * API exposes an ID, it should stay hidden.
+ *
+ * @hide
+ */
+ public int getThreadId() {
+ return mThreadId;
+ }
+
+ /**
+ * A helper class to build the {@link RcsEventQueryParameters}.
+ */
+ public static class Builder {
+ private @EventType int mEventType;
+ private @SortingProperty int mSortingProperty;
+ private boolean mIsAscending;
+ private int mLimit = 100;
+ private int mThreadId;
+
+ /**
+ * Creates a new builder for {@link RcsEventQueryParameters} to be used in
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)}
+ */
+ public Builder() {
+ // empty implementation
+ }
+
+ /**
+ * Desired number of events to be returned from the query. Passing in 0 will return all
+ * existing events at once. The limit defaults to 100.
+ *
+ * @param limit The number to limit the query result to.
+ * @return The same instance of the builder to chain parameters.
+ * @throws InvalidParameterException If the given limit is negative.
+ */
+ @CheckResult
+ public Builder setResultLimit(@IntRange(from = 0) int limit)
+ throws InvalidParameterException {
+ if (limit < 0) {
+ throw new InvalidParameterException("The query limit must be non-negative");
+ }
+
+ mLimit = limit;
+ return this;
+ }
+
+ /**
+ * Sets the type of events to be returned from the query.
+ *
+ * @param eventType The type of event to be returned.
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setEventType(@EventType int eventType) {
+ mEventType = eventType;
+ return this;
+ }
+
+ /**
+ * Sets the property where the results should be sorted against. Defaults to
+ * {@link RcsEventQueryParameters.SortingProperty#SORT_BY_CREATION_ORDER}
+ *
+ * @param sortingProperty against which property the results should be sorted
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortProperty(@SortingProperty int sortingProperty) {
+ mSortingProperty = sortingProperty;
+ return this;
+ }
+
+ /**
+ * Sets whether the results should be sorted ascending or descending
+ *
+ * @param isAscending whether the results should be sorted ascending
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortDirection(boolean isAscending) {
+ mIsAscending = isAscending;
+ return this;
+ }
+
+ /**
+ * Limits the results to the given {@link RcsGroupThread}. Setting this value prevents
+ * returning any instances of {@link RcsParticipantAliasChangedEvent}.
+ *
+ * @param groupThread The thread to limit the results to.
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setGroupThread(@NonNull RcsGroupThread groupThread) {
+ mThreadId = groupThread.getThreadId();
+ return this;
+ }
+
+ /**
+ * Builds the {@link RcsEventQueryParameters} to use in
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)}
+ *
+ * @return An instance of {@link RcsEventQueryParameters} to use with the event query.
+ */
+ public RcsEventQueryParameters build() {
+ return new RcsEventQueryParameters(mEventType, mThreadId, mSortingProperty,
+ mIsAscending, mLimit);
+ }
+ }
+
+ protected RcsEventQueryParameters(Parcel in) {
+ mEventType = in.readInt();
+ mThreadId = in.readInt();
+ mSortingProperty = in.readInt();
+ mIsAscending = in.readBoolean();
+ mLimit = in.readInt();
+ }
+
+ public static final Creator<RcsEventQueryParameters> CREATOR =
+ new Creator<RcsEventQueryParameters>() {
+ @Override
+ public RcsEventQueryParameters createFromParcel(Parcel in) {
+ return new RcsEventQueryParameters(in);
+ }
+
+ @Override
+ public RcsEventQueryParameters[] newArray(int size) {
+ return new RcsEventQueryParameters[size];
+ }
+ };
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mEventType);
+ dest.writeInt(mThreadId);
+ dest.writeInt(mSortingProperty);
+ dest.writeBoolean(mIsAscending);
+ dest.writeInt(mLimit);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsEventQueryResult.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsEventQueryResult.aidl
index 82d985d..7d13335 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsEventQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
new file mode 100644
index 0000000..27898ab
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
@@ -0,0 +1,90 @@
+/*
+ * 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.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParameters)}
+ * call. This class allows getting the token for querying the next batch of events in order to
+ * prevent handling large amounts of data at once.
+ *
+ * @hide
+ */
+public class RcsEventQueryResult implements Parcelable {
+ private RcsQueryContinuationToken mContinuationToken;
+ private List<RcsEvent> mEvents;
+
+ /**
+ * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+ * to create query results
+ *
+ * @hide
+ */
+ public RcsEventQueryResult(
+ RcsQueryContinuationToken continuationToken,
+ List<RcsEvent> events) {
+ mContinuationToken = continuationToken;
+ mEvents = events;
+ }
+
+ /**
+ * Returns a token to call
+ * {@link RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)}
+ * to get the next batch of {@link RcsEvent}s.
+ */
+ public RcsQueryContinuationToken getContinuationToken() {
+ return mContinuationToken;
+ }
+
+ /**
+ * Returns all the {@link RcsEvent}s in the current query result. Call {@link
+ * RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)} to get the next batch
+ * of {@link RcsEvent}s.
+ */
+ public List<RcsEvent> getEvents() {
+ return mEvents;
+ }
+
+ protected RcsEventQueryResult(Parcel in) {
+ }
+
+ public static final Creator<RcsEventQueryResult> CREATOR = new Creator<RcsEventQueryResult>() {
+ @Override
+ public RcsEventQueryResult createFromParcel(Parcel in) {
+ return new RcsEventQueryResult(in);
+ }
+
+ @Override
+ public RcsEventQueryResult[] newArray(int size) {
+ return new RcsEventQueryResult[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mContinuationToken, flags);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsFileTransferCreationParameters.aidl
similarity index 93%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsFileTransferCreationParameters.aidl
index 82d985d..5fec021 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParameters.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsFileTransferCreationParameters;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParameters.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParameters.java
new file mode 100644
index 0000000..bd7cb4b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParameters.java
@@ -0,0 +1,360 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Pass an instance of this class to
+ * {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParameters)} create an
+ * {@link RcsFileTransferPart} and save it into storage.
+ *
+ * @hide - TODO(109759350) make this public
+ */
+public class RcsFileTransferCreationParameters implements Parcelable {
+ private String mRcsFileTransferSessionId;
+ private Uri mContentUri;
+ private String mContentMimeType;
+ private long mFileSize;
+ private long mTransferOffset;
+ private int mWidth;
+ private int mHeight;
+ private long mMediaDuration;
+ private Uri mPreviewUri;
+ private String mPreviewMimeType;
+ private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus;
+
+ /**
+ * @return Returns the globally unique RCS file transfer session ID for the
+ * {@link RcsFileTransferPart} to be created
+ */
+ public String getRcsFileTransferSessionId() {
+ return mRcsFileTransferSessionId;
+ }
+
+ /**
+ * @return Returns the URI for the content of the {@link RcsFileTransferPart} to be created
+ */
+ public Uri getContentUri() {
+ return mContentUri;
+ }
+
+ /**
+ * @return Returns the MIME type for the content of the {@link RcsFileTransferPart} to be
+ * created
+ */
+ public String getContentMimeType() {
+ return mContentMimeType;
+ }
+
+ /**
+ * @return Returns the file size in bytes for the {@link RcsFileTransferPart} to be created
+ */
+ public long getFileSize() {
+ return mFileSize;
+ }
+
+ /**
+ * @return Returns the transfer offset for the {@link RcsFileTransferPart} to be created. The
+ * file transfer offset is defined as how many bytes have been successfully transferred to the
+ * receiver of this file transfer.
+ */
+ public long getTransferOffset() {
+ return mTransferOffset;
+ }
+
+ /**
+ * @return Returns the width of the {@link RcsFileTransferPart} to be created. The value is in
+ * pixels.
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * @return Returns the height of the {@link RcsFileTransferPart} to be created. The value is in
+ * pixels.
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * @return Returns the duration of the {@link RcsFileTransferPart} to be created.
+ */
+ public long getMediaDuration() {
+ return mMediaDuration;
+ }
+
+ /**
+ * @return Returns the URI of the preview of the content of the {@link RcsFileTransferPart} to
+ * be created. This should only be used for multi-media files.
+ */
+ public Uri getPreviewUri() {
+ return mPreviewUri;
+ }
+
+ /**
+ * @return Returns the MIME type of the preview of the content of the
+ * {@link RcsFileTransferPart} to be created. This should only be used for multi-media files.
+ */
+ public String getPreviewMimeType() {
+ return mPreviewMimeType;
+ }
+
+ /**
+ * @return Returns the status of the {@link RcsFileTransferPart} to be created.
+ */
+ public @RcsFileTransferPart.RcsFileTransferStatus int getFileTransferStatus() {
+ return mFileTransferStatus;
+ }
+
+ /**
+ * @hide
+ */
+ RcsFileTransferCreationParameters(Builder builder) {
+ mRcsFileTransferSessionId = builder.mRcsFileTransferSessionId;
+ mContentUri = builder.mContentUri;
+ mContentMimeType = builder.mContentMimeType;
+ mFileSize = builder.mFileSize;
+ mTransferOffset = builder.mTransferOffset;
+ mWidth = builder.mWidth;
+ mHeight = builder.mHeight;
+ mMediaDuration = builder.mLength;
+ mPreviewUri = builder.mPreviewUri;
+ mPreviewMimeType = builder.mPreviewMimeType;
+ mFileTransferStatus = builder.mFileTransferStatus;
+ }
+
+ /**
+ * A builder to create instances of {@link RcsFileTransferCreationParameters}
+ */
+ public class Builder {
+ private String mRcsFileTransferSessionId;
+ private Uri mContentUri;
+ private String mContentMimeType;
+ private long mFileSize;
+ private long mTransferOffset;
+ private int mWidth;
+ private int mHeight;
+ private long mLength;
+ private Uri mPreviewUri;
+ private String mPreviewMimeType;
+ private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus;
+
+ /**
+ * Sets the globally unique RCS file transfer session ID for the {@link RcsFileTransferPart}
+ * to be created
+ *
+ * @param sessionId The RCS file transfer session ID
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setFileTransferSessionId(String sessionId) {
+ mRcsFileTransferSessionId = sessionId;
+ return this;
+ }
+
+ /**
+ * Sets the URI for the content of the {@link RcsFileTransferPart} to be created
+ *
+ * @param contentUri The URI for the file
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setContentUri(Uri contentUri) {
+ mContentUri = contentUri;
+ return this;
+ }
+
+ /**
+ * Sets the MIME type for the content of the {@link RcsFileTransferPart} to be created
+ *
+ * @param contentType The MIME type of the file
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setContentMimeType(String contentType) {
+ mContentMimeType = contentType;
+ return this;
+ }
+
+ /**
+ * Sets the file size for the {@link RcsFileTransferPart} to be created
+ *
+ * @param size The size of the file in bytes
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setFileSize(long size) {
+ mFileSize = size;
+ return this;
+ }
+
+ /**
+ * Sets the transfer offset for the {@link RcsFileTransferPart} to be created. The file
+ * transfer offset is defined as how many bytes have been successfully transferred to the
+ * receiver of this file transfer.
+ *
+ * @param offset The transfer offset in bytes
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setTransferOffset(long offset) {
+ mTransferOffset = offset;
+ return this;
+ }
+
+ /**
+ * Sets the width of the {@link RcsFileTransferPart} to be created. This should only be used
+ * for multi-media files.
+ *
+ * @param width The width of the multi-media file in pixels.
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setWidth(int width) {
+ mWidth = width;
+ return this;
+ }
+
+ /**
+ * Sets the height of the {@link RcsFileTransferPart} to be created. This should only be
+ * used for multi-media files.
+ *
+ * @param height The height of the multi-media file in pixels.
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setHeight(int height) {
+ mHeight = height;
+ return this;
+ }
+
+ /**
+ * Sets the length of the {@link RcsFileTransferPart} to be created. This should only be
+ * used for multi-media files such as audio or video.
+ *
+ * @param length The length of the multi-media file in milliseconds
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setMediaDuration(long length) {
+ mLength = length;
+ return this;
+ }
+
+ /**
+ * Sets the URI of the preview of the content of the {@link RcsFileTransferPart} to be
+ * created. This should only be used for multi-media files.
+ *
+ * @param previewUri The URI of the preview of the file transfer
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setPreviewUri(Uri previewUri) {
+ mPreviewUri = previewUri;
+ return this;
+ }
+
+ /**
+ * Sets the MIME type of the preview of the content of the {@link RcsFileTransferPart} to
+ * be created. This should only be used for multi-media files.
+ *
+ * @param previewType The MIME type of the preview of the file transfer
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setPreviewMimeType(String previewType) {
+ mPreviewMimeType = previewType;
+ return this;
+ }
+
+ /**
+ * Sets the status of the {@link RcsFileTransferPart} to be created.
+ *
+ * @param status The status of the file transfer
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setFileTransferStatus(
+ @RcsFileTransferPart.RcsFileTransferStatus int status) {
+ mFileTransferStatus = status;
+ return this;
+ }
+
+ /**
+ * Creates an instance of {@link RcsFileTransferCreationParameters} with the given
+ * parameters.
+ *
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#insertFileTransfer(RcsFileTransferCreationParameters)
+ */
+ public RcsFileTransferCreationParameters build() {
+ return new RcsFileTransferCreationParameters(this);
+ }
+ }
+
+ protected RcsFileTransferCreationParameters(Parcel in) {
+ mRcsFileTransferSessionId = in.readString();
+ mContentUri = in.readParcelable(Uri.class.getClassLoader());
+ mContentMimeType = in.readString();
+ mFileSize = in.readLong();
+ mTransferOffset = in.readLong();
+ mWidth = in.readInt();
+ mHeight = in.readInt();
+ mMediaDuration = in.readLong();
+ mPreviewUri = in.readParcelable(Uri.class.getClassLoader());
+ mPreviewMimeType = in.readString();
+ mFileTransferStatus = in.readInt();
+ }
+
+ public static final Creator<RcsFileTransferCreationParameters> CREATOR =
+ new Creator<RcsFileTransferCreationParameters>() {
+ @Override
+ public RcsFileTransferCreationParameters createFromParcel(Parcel in) {
+ return new RcsFileTransferCreationParameters(in);
+ }
+
+ @Override
+ public RcsFileTransferCreationParameters[] newArray(int size) {
+ return new RcsFileTransferCreationParameters[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mRcsFileTransferSessionId);
+ dest.writeParcelable(mContentUri, flags);
+ dest.writeString(mContentMimeType);
+ dest.writeLong(mFileSize);
+ dest.writeLong(mTransferOffset);
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ dest.writeLong(mMediaDuration);
+ dest.writeParcelable(mPreviewUri, flags);
+ dest.writeString(mPreviewMimeType);
+ dest.writeInt(mFileTransferStatus);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl b/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
deleted file mode 100644
index eaf3128..0000000
--- a/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsFileTransferPart;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
index 39c58dd..2eadc4a 100644
--- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java
+++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
@@ -15,34 +15,346 @@
*/
package android.telephony.ims;
-import android.os.Parcel;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.net.Uri;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
- * A part of a composite {@link RcsMessage} that holds a file transfer.
- * @hide - TODO(sahinc) make this public
+ * A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7
+ * (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide - TODO(109759350) make this public
*/
-public class RcsFileTransferPart extends RcsPart {
- public static final Creator<RcsFileTransferPart> CREATOR = new Creator<RcsFileTransferPart>() {
- @Override
- public RcsFileTransferPart createFromParcel(Parcel in) {
- return new RcsFileTransferPart(in);
- }
+public class RcsFileTransferPart {
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is not set yet.
+ */
+ public static final int NOT_SET = 0;
- @Override
- public RcsFileTransferPart[] newArray(int size) {
- return new RcsFileTransferPart[size];
- }
- };
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is a draft and is not in the
+ * process of sending yet.
+ */
+ public static final int DRAFT = 1;
- protected RcsFileTransferPart(Parcel in) {
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is actively being sent right
+ * now.
+ */
+ public static final int SENDING = 2;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} was being sent, but the user has
+ * paused the sending process.
+ */
+ public static final int SENDING_PAUSED = 3;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to
+ * send.
+ */
+ public static final int SENDING_FAILED = 4;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to
+ * send.
+ */
+ public static final int SENDING_CANCELLED = 5;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is actively being downloaded
+ * right now.
+ */
+ public static final int DOWNLOADING = 6;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} was being downloaded, but the
+ * user paused the downloading process.
+ */
+ public static final int DOWNLOADING_PAUSED = 7;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to
+ * download.
+ */
+ public static final int DOWNLOADING_FAILED = 8;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to
+ * download.
+ */
+ public static final int DOWNLOADING_CANCELLED = 9;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} was successfully sent or
+ * received.
+ */
+ public static final int SUCCEEDED = 10;
+
+ @IntDef({
+ DRAFT, SENDING, SENDING_PAUSED, SENDING_FAILED, SENDING_CANCELLED, DOWNLOADING,
+ DOWNLOADING_PAUSED, DOWNLOADING_FAILED, DOWNLOADING_CANCELLED, SUCCEEDED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RcsFileTransferStatus {
}
- @Override
- public int describeContents() {
- return 0;
+ private int mId;
+
+ /**
+ * @hide
+ */
+ RcsFileTransferPart(int id) {
+ mId = id;
}
- @Override
- public void writeToParcel(Parcel dest, int flags) {
+ /**
+ * @hide
+ */
+ public void setId(int id) {
+ mId = id;
+ }
+
+ /**
+ * @hide
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Sets the RCS file transfer session ID for this file transfer and persists into storage.
+ *
+ * @param sessionId The session ID to be used for this file transfer.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setFileTransferSessionId(String sessionId) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferSessionId(mId, sessionId));
+ }
+
+ /**
+ * @return Returns the file transfer session ID.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public String getFileTransferSessionId() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferSessionId(mId));
+ }
+
+ /**
+ * Sets the content URI for this file transfer and persists into storage. The file transfer
+ * should be reachable using this URI.
+ *
+ * @param contentUri The URI for this file transfer.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setContentUri(Uri contentUri) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferContentUri(mId, contentUri));
+ }
+
+ /**
+ * @return Returns the URI for this file transfer
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ @WorkerThread
+ public Uri getContentUri() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferContentUri(mId));
+ }
+
+ /**
+ * Sets the MIME type of this file transfer and persists into storage. Whether this type
+ * actually matches any known or supported types is not checked.
+ *
+ * @param contentMimeType The type of this file transfer.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setContentMimeType(String contentMimeType) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setFileTransferContentType(mId, contentMimeType));
+ }
+
+ /**
+ * @return Returns the content type of this file transfer
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ @Nullable
+ public String getContentMimeType() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferContentType(mId));
+ }
+
+ /**
+ * Sets the content length (i.e. file size) for this file transfer and persists into storage.
+ *
+ * @param contentLength The content length of this file transfer
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setFileSize(long contentLength) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setFileTransferFileSize(mId, contentLength));
+ }
+
+ /**
+ * @return Returns the content length (i.e. file size) for this file transfer.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getFileSize() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferFileSize(mId));
+ }
+
+ /**
+ * Sets the transfer offset for this file transfer and persists into storage. The file transfer
+ * offset is defined as how many bytes have been successfully transferred to the receiver of
+ * this file transfer.
+ *
+ * @param transferOffset The transfer offset for this file transfer.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setTransferOffset(long transferOffset) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setFileTransferTransferOffset(mId, transferOffset));
+ }
+
+ /**
+ * @return Returns the number of bytes that have successfully transferred.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getTransferOffset() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferTransferOffset(mId));
+ }
+
+ /**
+ * Sets the status for this file transfer and persists into storage.
+ *
+ * @param status The status of this file transfer.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setFileTransferStatus(@RcsFileTransferStatus int status)
+ throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferStatus(mId, status));
+ }
+
+ /**
+ * @return Returns the status of this file transfer.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public @RcsFileTransferStatus int getFileTransferStatus() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferStatus(mId));
+ }
+
+ /**
+ * @return Returns the width of this multi-media message part in pixels.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public int getWidth() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferWidth(mId));
+ }
+
+ /**
+ * Sets the width of this RCS multi-media message part and persists into storage.
+ *
+ * @param width The width value in pixels
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setWidth(int width) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferWidth(mId, width));
+ }
+
+ /**
+ * @return Returns the height of this multi-media message part in pixels.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public int getHeight() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferHeight(mId));
+ }
+
+ /**
+ * Sets the height of this RCS multi-media message part and persists into storage.
+ *
+ * @param height The height value in pixels
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setHeight(int height) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferHeight(mId, height));
+ }
+
+ /**
+ * @return Returns the length of this multi-media file (e.g. video or audio) in milliseconds.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getLength() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferLength(mId));
+ }
+
+ /**
+ * Sets the length of this multi-media file (e.g. video or audio) and persists into storage.
+ *
+ * @param length The length of the file in milliseconds.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setLength(long length) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferLength(mId, length));
+ }
+
+ /**
+ * @return Returns the URI for the preview of this multi-media file (e.g. an image thumbnail for
+ * a video)
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public Uri getPreviewUri() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferPreviewUri(mId));
+ }
+
+ /**
+ * Sets the URI for the preview of this multi-media file and persists into storage.
+ *
+ * @param previewUri The URI to access to the preview file.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setPreviewUri(Uri previewUri) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferPreviewUri(mId, previewUri));
+ }
+
+ /**
+ * @return Returns the MIME type of this multi-media file's preview.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public String getPreviewMimeType() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferPreviewType(mId));
+ }
+
+ /**
+ * Sets the MIME type for this multi-media file's preview and persists into storage.
+ *
+ * @param previewMimeType The MIME type for the preview
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setPreviewMimeType(String previewMimeType) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setFileTransferPreviewType(mId, previewMimeType));
}
}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.aidl b/telephony/java/android/telephony/ims/RcsGroupThread.aidl
deleted file mode 100644
index c4ce529..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThread.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsGroupThread;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
index d954b2d..6f6258e 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThread.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.java
@@ -15,38 +15,191 @@
*/
package android.telephony.ims;
-import android.os.Parcel;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.net.Uri;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
/**
* RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join
- * or leave.
+ * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service
+ * Definition Document)
+ *
* @hide - TODO(sahinc) make this public
*/
public class RcsGroupThread extends RcsThread {
- public static final Creator<RcsGroupThread> CREATOR = new Creator<RcsGroupThread>() {
- @Override
- public RcsGroupThread createFromParcel(Parcel in) {
- return new RcsGroupThread(in);
- }
-
- @Override
- public RcsGroupThread[] newArray(int size) {
- return new RcsGroupThread[size];
- }
- };
-
- protected RcsGroupThread(Parcel in) {
- super(in);
+ /**
+ * Public constructor only for RcsMessageStoreController to initialize new threads.
+ *
+ * @hide
+ */
+ public RcsGroupThread(int threadId) {
+ super(threadId);
}
+ /**
+ * @return Returns {@code true} as this is always a group thread
+ */
@Override
- public int describeContents() {
- return 0;
+ public boolean isGroup() {
+ return true;
}
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(RCS_GROUP_TYPE);
- super.writeToParcel(dest, flags);
+ /**
+ * @return Returns the given name of this {@link RcsGroupThread}. Please see US6-2 - GSMA RCC.71
+ * (RCS Universal Profile Service Definition Document)
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ @WorkerThread
+ public String getGroupName() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadName(mThreadId));
+ }
+
+ /**
+ * Sets the name of this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
+ * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setGroupName(String groupName) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setGroupThreadName(mThreadId, groupName));
+ }
+
+ /**
+ * @return Returns a URI that points to the group's icon {@link RcsGroupThread}. Please see
+ * US6-2 - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ public Uri getGroupIcon() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadIcon(mThreadId));
+ }
+
+ /**
+ * Sets the icon for this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
+ * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setGroupIcon(@Nullable Uri groupIcon) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setGroupThreadIcon(mThreadId, groupIcon));
+ }
+
+ /**
+ * @return Returns the owner of this thread or {@code null} if there doesn't exist an owner
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ @WorkerThread
+ public RcsParticipant getOwner() throws RcsMessageStoreException {
+ return new RcsParticipant(RcsControllerCall.call(
+ iRcs -> iRcs.getGroupThreadOwner(mThreadId)));
+ }
+
+ /**
+ * Sets the owner of this {@link RcsGroupThread} and saves it into storage. This is intended to
+ * be used for selecting a new owner for a group thread if the owner leaves the thread. The
+ * owner needs to be in the list of existing participants.
+ *
+ * @param participant The new owner of the thread. {@code null} values are allowed.
+ * @throws RcsMessageStoreException if the operation could not be persisted into storage
+ */
+ @WorkerThread
+ public void setOwner(@Nullable RcsParticipant participant) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setGroupThreadOwner(mThreadId, participant.getId()));
+ }
+
+ /**
+ * Adds a new {@link RcsParticipant} to this group thread and persists into storage. If the user
+ * is actively participating in this {@link RcsGroupThread}, an {@link RcsParticipant} on behalf
+ * of them should be added.
+ *
+ * @param participant The new participant to be added to the thread.
+ * @throws RcsMessageStoreException if the operation could not be persisted into storage
+ */
+ @WorkerThread
+ public void addParticipant(@NonNull RcsParticipant participant)
+ throws RcsMessageStoreException {
+ if (participant == null) {
+ return;
+ }
+
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.addParticipantToGroupThread(mThreadId, participant.getId()));
+ }
+
+ /**
+ * Removes an {@link RcsParticipant} from this group thread and persists into storage. If the
+ * removed participant was the owner of this group, the owner will become null.
+ *
+ * @throws RcsMessageStoreException if the operation could not be persisted into storage
+ */
+ @WorkerThread
+ public void removeParticipant(@NonNull RcsParticipant participant)
+ throws RcsMessageStoreException {
+ if (participant == null) {
+ return;
+ }
+
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.removeParticipantFromGroupThread(mThreadId, participant.getId()));
+ }
+
+ /**
+ * Returns the set of {@link RcsParticipant}s that contribute to this group thread. The
+ * returned set does not support modifications, please use
+ * {@link RcsGroupThread#addParticipant(RcsParticipant)}
+ * and {@link RcsGroupThread#removeParticipant(RcsParticipant)} instead.
+ *
+ * @return the immutable set of {@link RcsParticipant} in this group thread.
+ * @throws RcsMessageStoreException if the values could not be read from the storage
+ */
+ @WorkerThread
+ @NonNull
+ public Set<RcsParticipant> getParticipants() throws RcsMessageStoreException {
+ RcsParticipantQueryParameters queryParameters =
+ new RcsParticipantQueryParameters.Builder().setThread(this).build();
+
+ RcsParticipantQueryResult queryResult = RcsControllerCall.call(
+ iRcs -> iRcs.getParticipants(queryParameters));
+
+ List<RcsParticipant> participantList = queryResult.getParticipants();
+ Set<RcsParticipant> participantSet = new LinkedHashSet<>(participantList);
+ return Collections.unmodifiableSet(participantSet);
+ }
+
+ /**
+ * Returns the conference URI for this {@link RcsGroupThread}. Please see 4.4.5.2 - GSMA RCC.53
+ * (RCS Device API 1.6 Specification
+ *
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ @WorkerThread
+ public Uri getConferenceUri() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadConferenceUri(mThreadId));
+ }
+
+ /**
+ * Sets the conference URI for this {@link RcsGroupThread} and persists into storage. Please see
+ * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
+ *
+ * @param conferenceUri The URI as String to be used as the conference URI.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @Nullable
+ @WorkerThread
+ public void setConferenceUri(Uri conferenceUri) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setGroupThreadConferenceUri(mThreadId, conferenceUri));
}
}
diff --git a/telephony/java/android/telephony/ims/RcsThreadEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl
similarity index 95%
rename from telephony/java/android/telephony/ims/RcsThreadEvent.aidl
rename to telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl
index 4a40d89..77a2372 100644
--- a/telephony/java/android/telephony/ims/RcsThreadEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl
@@ -1,5 +1,4 @@
/*
- *
* Copyright 2019, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,4 +16,4 @@
package android.telephony.ims;
-parcelable RcsThreadEvent;
+parcelable RcsGroupThreadEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
new file mode 100644
index 0000000..a18437b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+/**
+ * An event that happened on an {@link RcsGroupThread}.
+ *
+ * @hide - TODO(109759350) make this public
+ */
+public abstract class RcsGroupThreadEvent extends RcsEvent {
+ private final int mRcsGroupThreadId;
+ private final int mOriginatingParticipantId;
+
+ RcsGroupThreadEvent(long timestamp, int rcsGroupThreadId,
+ int originatingParticipantId) {
+ super(timestamp);
+ mRcsGroupThreadId = rcsGroupThreadId;
+ mOriginatingParticipantId = originatingParticipantId;
+ }
+
+ /**
+ * @return Returns the {@link RcsGroupThread} that this event happened on.
+ */
+ @NonNull
+ public RcsGroupThread getRcsGroupThread() {
+ return new RcsGroupThread(mRcsGroupThreadId);
+ }
+
+ /**
+ * @return Returns the {@link RcsParticipant} that performed the event.
+ */
+ @NonNull
+ public RcsParticipant getOriginatingParticipant() {
+ return new RcsParticipant(mOriginatingParticipantId);
+ }
+
+ RcsGroupThreadEvent(Parcel in) {
+ super(in);
+ mRcsGroupThreadId = in.readInt();
+ mOriginatingParticipantId = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mRcsGroupThreadId);
+ dest.writeInt(mOriginatingParticipantId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl
similarity index 93%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl
index 82d985d..daea792 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl
@@ -1,5 +1,4 @@
/*
- *
* Copyright 2019, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,4 +16,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsGroupThreadIconChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
new file mode 100644
index 0000000..7beed3b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+
+/**
+ * An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA
+ * RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide - TODO(109759350) make this public
+ */
+public class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent {
+ private final Uri mNewIcon;
+
+ /**
+ * Creates a new {@link RcsGroupThreadIconChangedEvent}. This event is not persisted into
+ * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+ *
+ * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+ * midnight, January 1st, 1970 UTC
+ * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+ * @param originatingParticipant The {@link RcsParticipant} that changed the
+ * {@link RcsGroupThread}'s icon.
+ * @param newIcon {@link Uri} to the new icon of this {@link RcsGroupThread}
+ * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+ */
+ public RcsGroupThreadIconChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread,
+ @NonNull RcsParticipant originatingParticipant, @Nullable Uri newIcon) {
+ super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+ mNewIcon = newIcon;
+ }
+
+ /**
+ * @hide - internal constructor for queries
+ */
+ public RcsGroupThreadIconChangedEvent(long timestamp, int rcsGroupThreadId,
+ int originatingParticipantId, @Nullable Uri newIcon) {
+ super(timestamp, rcsGroupThreadId, originatingParticipantId);
+ mNewIcon = newIcon;
+ }
+
+ /**
+ * @return Returns the {@link Uri} to the icon of the {@link RcsGroupThread} after this
+ * {@link RcsGroupThreadIconChangedEvent} occured.
+ */
+ @Nullable
+ public Uri getNewIcon() {
+ return mNewIcon;
+ }
+
+ /**
+ * Persists the event to the data store.
+ *
+ * @hide - not meant for public use.
+ */
+ @Override
+ public void persist() throws RcsMessageStoreException {
+ // TODO ensure failure throws
+ RcsControllerCall.call(iRcs -> iRcs.createGroupThreadIconChangedEvent(
+ getTimestamp(), getRcsGroupThread().getThreadId(),
+ getOriginatingParticipant().getId(), mNewIcon));
+ }
+
+ public static final Creator<RcsGroupThreadIconChangedEvent> CREATOR =
+ new Creator<RcsGroupThreadIconChangedEvent>() {
+ @Override
+ public RcsGroupThreadIconChangedEvent createFromParcel(Parcel in) {
+ return new RcsGroupThreadIconChangedEvent(in);
+ }
+
+ @Override
+ public RcsGroupThreadIconChangedEvent[] newArray(int size) {
+ return new RcsGroupThreadIconChangedEvent[size];
+ }
+ };
+
+ protected RcsGroupThreadIconChangedEvent(Parcel in) {
+ super(in);
+ mNewIcon = in.readParcelable(Uri.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeParcelable(mNewIcon, flags);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl
similarity index 93%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl
index 82d985d..3ed9bd1 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsGroupThreadNameChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
new file mode 100644
index 0000000..0d2ea4f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+/**
+ * An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA
+ * RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide - TODO(109759350) make this public
+ */
+public class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent {
+ private String mNewName;
+
+ /**
+ * Creates a new {@link RcsGroupThreadNameChangedEvent}. This event is not persisted into
+ * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+ *
+ * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+ * midnight, January 1st, 1970 UTC
+ * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+ * @param originatingParticipant The {@link RcsParticipant} that changed the
+ * {@link RcsGroupThread}'s icon.
+ * @param newName The new name of the {@link RcsGroupThread}
+ * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+ */
+ public RcsGroupThreadNameChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread,
+ @NonNull RcsParticipant originatingParticipant, @Nullable String newName) {
+ super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+ mNewName = newName;
+ }
+
+ /**
+ * @hide - internal constructor for queries
+ */
+ public RcsGroupThreadNameChangedEvent(long timestamp, int rcsGroupThreadId,
+ int originatingParticipantId, @Nullable String newName) {
+ super(timestamp, rcsGroupThreadId, originatingParticipantId);
+ mNewName = newName;
+ }
+
+ /**
+ * @return Returns the name of this {@link RcsGroupThread} after this
+ * {@link RcsGroupThreadNameChangedEvent} happened.
+ */
+ @Nullable
+ public String getNewName() {
+ return mNewName;
+ }
+
+ /**
+ * Persists the event to the data store.
+ *
+ * @hide - not meant for public use.
+ */
+ @Override
+ public void persist() throws RcsMessageStoreException {
+ RcsControllerCall.call(iRcs -> iRcs.createGroupThreadNameChangedEvent(
+ getTimestamp(), getRcsGroupThread().getThreadId(),
+ getOriginatingParticipant().getId(), mNewName));
+ }
+
+ public static final Creator<RcsGroupThreadNameChangedEvent> CREATOR =
+ new Creator<RcsGroupThreadNameChangedEvent>() {
+ @Override
+ public RcsGroupThreadNameChangedEvent createFromParcel(Parcel in) {
+ return new RcsGroupThreadNameChangedEvent(in);
+ }
+
+ @Override
+ public RcsGroupThreadNameChangedEvent[] newArray(int size) {
+ return new RcsGroupThreadNameChangedEvent[size];
+ }
+ };
+
+ protected RcsGroupThreadNameChangedEvent(Parcel in) {
+ super(in);
+ mNewName = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mNewName);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessage.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl
similarity index 92%
copy from telephony/java/android/telephony/ims/RcsMessage.aidl
copy to telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl
index b32cd12..420abff 100644
--- a/telephony/java/android/telephony/ims/RcsMessage.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsMessage;
+parcelable RcsGroupThreadParticipantJoinedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
new file mode 100644
index 0000000..2adafc7
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+/**
+ * An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 -
+ * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide - TODO(109759350) make this public
+ */
+public class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent {
+ private int mJoinedParticipantId;
+
+ /**
+ * Creates a new {@link RcsGroupThreadParticipantJoinedEvent}. This event is not persisted into
+ * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+ *
+ * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+ * midnight, January 1st, 1970 UTC
+ * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+ * @param originatingParticipant The {@link RcsParticipant} that added or invited the new
+ * {@link RcsParticipant} into the {@link RcsGroupThread}
+ * @param joinedParticipant The new {@link RcsParticipant} that joined the
+ * {@link RcsGroupThread}
+ * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+ */
+ public RcsGroupThreadParticipantJoinedEvent(long timestamp,
+ @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
+ @NonNull RcsParticipant joinedParticipant) {
+ super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+ mJoinedParticipantId = joinedParticipant.getId();
+ }
+
+ /**
+ * @hide - internal constructor for queries
+ */
+ public RcsGroupThreadParticipantJoinedEvent(long timestamp, int rcsGroupThreadId,
+ int originatingParticipantId, int joinedParticipantId) {
+ super(timestamp, rcsGroupThreadId, originatingParticipantId);
+ mJoinedParticipantId = joinedParticipantId;
+ }
+
+ /**
+ * @return Returns the {@link RcsParticipant} that joined the associated {@link RcsGroupThread}
+ */
+ public RcsParticipant getJoinedParticipant() {
+ return new RcsParticipant(mJoinedParticipantId);
+ }
+
+ /**
+ * Persists the event to the data store.
+ *
+ * @hide - not meant for public use.
+ */
+ @Override
+ public void persist() throws RcsMessageStoreException {
+ RcsControllerCall.call(
+ iRcs -> iRcs.createGroupThreadParticipantJoinedEvent(getTimestamp(),
+ getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(),
+ getJoinedParticipant().getId()));
+ }
+
+ public static final Creator<RcsGroupThreadParticipantJoinedEvent> CREATOR =
+ new Creator<RcsGroupThreadParticipantJoinedEvent>() {
+ @Override
+ public RcsGroupThreadParticipantJoinedEvent createFromParcel(Parcel in) {
+ return new RcsGroupThreadParticipantJoinedEvent(in);
+ }
+
+ @Override
+ public RcsGroupThreadParticipantJoinedEvent[] newArray(int size) {
+ return new RcsGroupThreadParticipantJoinedEvent[size];
+ }
+ };
+
+ protected RcsGroupThreadParticipantJoinedEvent(Parcel in) {
+ super(in);
+ mJoinedParticipantId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mJoinedParticipantId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessage.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl
similarity index 92%
rename from telephony/java/android/telephony/ims/RcsMessage.aidl
rename to telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl
index b32cd12..ff139ac 100644
--- a/telephony/java/android/telephony/ims/RcsMessage.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsMessage;
+parcelable RcsGroupThreadParticipantLeftEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
new file mode 100644
index 0000000..87c1852
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+/**
+ * An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 -
+ * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide - TODO(109759350) make this public
+ */
+public class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent {
+ private int mLeavingParticipantId;
+
+ /**
+ * Creates a new {@link RcsGroupThreadParticipantLeftEvent}. his event is not persisted into
+ * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+ *
+ * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+ * midnight, January 1st, 1970 UTC
+ * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+ * @param originatingParticipant The {@link RcsParticipant} that removed the
+ * {@link RcsParticipant} from the {@link RcsGroupThread}. It is
+ * possible that originatingParticipant and leavingParticipant are
+ * the same (i.e. {@link RcsParticipant} left the group
+ * themselves)
+ * @param leavingParticipant The {@link RcsParticipant} that left the {@link RcsGroupThread}
+ * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+ */
+ public RcsGroupThreadParticipantLeftEvent(long timestamp,
+ @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
+ @NonNull RcsParticipant leavingParticipant) {
+ super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+ mLeavingParticipantId = leavingParticipant.getId();
+ }
+
+ /**
+ * @hide - internal constructor for queries
+ */
+ public RcsGroupThreadParticipantLeftEvent(long timestamp, int rcsGroupThreadId,
+ int originatingParticipantId, int leavingParticipantId) {
+ super(timestamp, rcsGroupThreadId, originatingParticipantId);
+ mLeavingParticipantId = leavingParticipantId;
+ }
+
+ /**
+ * @return Returns the {@link RcsParticipant} that left the associated {@link RcsGroupThread}
+ * after this {@link RcsGroupThreadParticipantLeftEvent} happened.
+ */
+ @NonNull
+ public RcsParticipant getLeavingParticipantId() {
+ return new RcsParticipant(mLeavingParticipantId);
+ }
+
+ @Override
+ public void persist() throws RcsMessageStoreException {
+ RcsControllerCall.call(
+ iRcs -> iRcs.createGroupThreadParticipantJoinedEvent(getTimestamp(),
+ getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(),
+ getLeavingParticipantId().getId()));
+ }
+
+ public static final Creator<RcsGroupThreadParticipantLeftEvent> CREATOR =
+ new Creator<RcsGroupThreadParticipantLeftEvent>() {
+ @Override
+ public RcsGroupThreadParticipantLeftEvent createFromParcel(Parcel in) {
+ return new RcsGroupThreadParticipantLeftEvent(in);
+ }
+
+ @Override
+ public RcsGroupThreadParticipantLeftEvent[] newArray(int size) {
+ return new RcsGroupThreadParticipantLeftEvent[size];
+ }
+ };
+
+ protected RcsGroupThreadParticipantLeftEvent(Parcel in) {
+ super(in);
+ mLeavingParticipantId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mLeavingParticipantId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
deleted file mode 100644
index 6552a82..0000000
--- a/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsIncomingMessage;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
index f39e06d..2ea115b 100644
--- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
@@ -15,34 +15,82 @@
*/
package android.telephony.ims;
-import android.os.Parcel;
+import android.annotation.WorkerThread;
/**
* This is a single instance of a message received over RCS.
- * @hide - TODO(sahinc) make this public
+ *
+ * @hide - TODO(109759350) make this public
*/
public class RcsIncomingMessage extends RcsMessage {
- public static final Creator<RcsIncomingMessage> CREATOR = new Creator<RcsIncomingMessage>() {
- @Override
- public RcsIncomingMessage createFromParcel(Parcel in) {
- return new RcsIncomingMessage(in);
- }
-
- @Override
- public RcsIncomingMessage[] newArray(int size) {
- return new RcsIncomingMessage[size];
- }
- };
-
- protected RcsIncomingMessage(Parcel in) {
+ /**
+ * @hide
+ */
+ RcsIncomingMessage(int id) {
+ super(id);
}
- @Override
- public int describeContents() {
- return 0;
+ /**
+ * Sets the timestamp of arrival for this message and persists into storage. The timestamp is
+ * defined as milliseconds passed after midnight, January 1, 1970 UTC
+ *
+ * @param arrivalTimestamp The timestamp to set to.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setArrivalTimestamp(long arrivalTimestamp) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setMessageArrivalTimestamp(mId, true, arrivalTimestamp));
}
+ /**
+ * @return Returns the timestamp of arrival for this message. The timestamp is defined as
+ * milliseconds passed after midnight, January 1, 1970 UTC
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getArrivalTimestamp() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessageArrivalTimestamp(mId, true));
+ }
+
+ /**
+ * Sets the timestamp of when the user saw this message and persists into storage. The timestamp
+ * is defined as milliseconds passed after midnight, January 1, 1970 UTC
+ *
+ * @param notifiedTimestamp The timestamp to set to.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setSeenTimestamp(long notifiedTimestamp) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setMessageSeenTimestamp(mId, true, notifiedTimestamp));
+ }
+
+ /**
+ * @return Returns the timestamp of when the user saw this message. The timestamp is defined as
+ * milliseconds passed after midnight, January 1, 1970 UTC
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getSeenTimestamp() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessageSeenTimestamp(mId, true));
+ }
+
+ /**
+ * @return Returns the sender of this incoming message.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public RcsParticipant getSenderParticipant() throws RcsMessageStoreException {
+ return new RcsParticipant(
+ RcsControllerCall.call(iRcs -> iRcs.getSenderParticipant(mId)));
+ }
+
+ /**
+ * @return Returns {@code true} as this is an incoming message
+ */
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public boolean isIncoming() {
+ return true;
}
}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParameters.aidl
similarity index 92%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsIncomingMessageCreationParameters.aidl
index 82d985d..76073c2 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParameters.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsIncomingMessageCreationParameters;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParameters.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParameters.java
new file mode 100644
index 0000000..acde8af
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParameters.java
@@ -0,0 +1,180 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@link RcsIncomingMessageCreationParameters} is a collection of parameters that should be passed
+ * into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParameters)} to generate an
+ * {@link RcsIncomingMessage} on that {@link RcsThread}
+ *
+ * @hide TODO:make public
+ */
+public class RcsIncomingMessageCreationParameters extends RcsMessageCreationParameters implements
+ Parcelable {
+ // The arrival timestamp for the RcsIncomingMessage to be created
+ private final long mArrivalTimestamp;
+ // The seen timestamp for the RcsIncomingMessage to be created
+ private final long mSeenTimestamp;
+ // The participant that sent this incoming message
+ private final int mSenderParticipantId;
+
+ /**
+ * Builder to help create an {@link RcsIncomingMessageCreationParameters}
+ *
+ * @see RcsThread#addIncomingMessage(RcsIncomingMessageCreationParameters)
+ */
+ public static class Builder extends RcsMessageCreationParameters.Builder {
+ private RcsParticipant mSenderParticipant;
+ private long mArrivalTimestamp;
+ private long mSeenTimestamp;
+
+ /**
+ * Creates a {@link Builder} to create an instance of
+ * {@link RcsIncomingMessageCreationParameters}
+ *
+ * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination
+ * timestamp value in milliseconds passed after midnight,
+ * January 1, 1970 UTC
+ * @param arrivalTimestamp The timestamp of arrival, defined as milliseconds passed after
+ * midnight, January 1, 1970 UTC
+ * @param subscriptionId The subscription ID that was used to send or receive this
+ * {@link RcsMessage}
+ */
+ public Builder(long originationTimestamp, long arrivalTimestamp, int subscriptionId) {
+ super(originationTimestamp, subscriptionId);
+ mArrivalTimestamp = arrivalTimestamp;
+ }
+
+ /**
+ * Sets the {@link RcsParticipant} that send this {@link RcsIncomingMessage}
+ *
+ * @param senderParticipant The {@link RcsParticipant} that sent this
+ * {@link RcsIncomingMessage}
+ * @return The same instance of {@link Builder} to chain methods.
+ */
+ @CheckResult
+ public Builder setSenderParticipant(RcsParticipant senderParticipant) {
+ mSenderParticipant = senderParticipant;
+ return this;
+ }
+
+ /**
+ * Sets the time of the arrival of this {@link RcsIncomingMessage}
+
+ * @return The same instance of {@link Builder} to chain methods.
+ * @see RcsIncomingMessage#setArrivalTimestamp(long)
+ */
+ @CheckResult
+ public Builder setArrivalTimestamp(long arrivalTimestamp) {
+ mArrivalTimestamp = arrivalTimestamp;
+ return this;
+ }
+
+ /**
+ * Sets the time of the when this user saw the {@link RcsIncomingMessage}
+ * @param seenTimestamp The seen timestamp , defined as milliseconds passed after midnight,
+ * January 1, 1970 UTC
+ * @return The same instance of {@link Builder} to chain methods.
+ * @see RcsIncomingMessage#setSeenTimestamp(long)
+ */
+ @CheckResult
+ public Builder setSeenTimestamp(long seenTimestamp) {
+ mSeenTimestamp = seenTimestamp;
+ return this;
+ }
+
+ /**
+ * Creates parameters for creating a new incoming message.
+ * @return A new instance of {@link RcsIncomingMessageCreationParameters} to create a new
+ * {@link RcsIncomingMessage}
+ */
+ public RcsIncomingMessageCreationParameters build() {
+ return new RcsIncomingMessageCreationParameters(this);
+ }
+ }
+
+ private RcsIncomingMessageCreationParameters(Builder builder) {
+ super(builder);
+ mArrivalTimestamp = builder.mArrivalTimestamp;
+ mSeenTimestamp = builder.mSeenTimestamp;
+ mSenderParticipantId = builder.mSenderParticipant.getId();
+ }
+
+ protected RcsIncomingMessageCreationParameters(Parcel in) {
+ super(in);
+ mArrivalTimestamp = in.readLong();
+ mSeenTimestamp = in.readLong();
+ mSenderParticipantId = in.readInt();
+ }
+
+ /**
+ * @return Returns the arrival timestamp for the {@link RcsIncomingMessage} to be created.
+ * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC
+ */
+ public long getArrivalTimestamp() {
+ return mArrivalTimestamp;
+ }
+
+ /**
+ * @return Returns the seen timestamp for the {@link RcsIncomingMessage} to be created.
+ * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC
+ */
+ public long getSeenTimestamp() {
+ return mSeenTimestamp;
+ }
+
+ /**
+ * Helper getter for {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
+ * create {@link RcsIncomingMessage}s
+ *
+ * Since the API doesn't expose any ID's to API users, this should be hidden.
+ * @hide
+ */
+ public int getSenderParticipantId() {
+ return mSenderParticipantId;
+ }
+
+ public static final Creator<RcsIncomingMessageCreationParameters> CREATOR =
+ new Creator<RcsIncomingMessageCreationParameters>() {
+ @Override
+ public RcsIncomingMessageCreationParameters createFromParcel(Parcel in) {
+ return new RcsIncomingMessageCreationParameters(in);
+ }
+
+ @Override
+ public RcsIncomingMessageCreationParameters[] newArray(int size) {
+ return new RcsIncomingMessageCreationParameters[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeLong(mArrivalTimestamp);
+ dest.writeLong(mSeenTimestamp);
+ dest.writeInt(mSenderParticipantId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.aidl b/telephony/java/android/telephony/ims/RcsLocationPart.aidl
deleted file mode 100644
index 4fe5ca9..0000000
--- a/telephony/java/android/telephony/ims/RcsLocationPart.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsLocationPart;
diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.java b/telephony/java/android/telephony/ims/RcsLocationPart.java
deleted file mode 100644
index 19be4ce..0000000
--- a/telephony/java/android/telephony/ims/RcsLocationPart.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a location
- * @hide - TODO(sahinc) make this public
- */
-public class RcsLocationPart extends RcsPart {
- public static final Creator<RcsLocationPart> CREATOR = new Creator<RcsLocationPart>() {
- @Override
- public RcsLocationPart createFromParcel(Parcel in) {
- return new RcsLocationPart(in);
- }
-
- @Override
- public RcsLocationPart[] newArray(int size) {
- return new RcsLocationPart[size];
- }
- };
-
- protected RcsLocationPart(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsManager.aidl b/telephony/java/android/telephony/ims/RcsManager.aidl
deleted file mode 100644
index 63bc71c..0000000
--- a/telephony/java/android/telephony/ims/RcsManager.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsManager;
diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java
index df108c8..e84d4ed 100644
--- a/telephony/java/android/telephony/ims/RcsManager.java
+++ b/telephony/java/android/telephony/ims/RcsManager.java
@@ -28,7 +28,7 @@
private static final RcsMessageStore sRcsMessageStoreInstance = new RcsMessageStore();
/**
- * Returns an instance of RcsMessageStore.
+ * Returns an instance of {@link RcsMessageStore}
*/
public RcsMessageStore getRcsMessageStore() {
return sRcsMessageStoreInstance;
diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java
index d46685c..b70a9a7 100644
--- a/telephony/java/android/telephony/ims/RcsMessage.java
+++ b/telephony/java/android/telephony/ims/RcsMessage.java
@@ -15,11 +15,314 @@
*/
package android.telephony.ims;
-import android.os.Parcelable;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
/**
* This is a single instance of a message sent or received over RCS.
- * @hide - TODO(sahinc) make this public
+ *
+ * @hide - TODO(109759350) make this public
*/
-public abstract class RcsMessage implements Parcelable {
+public abstract class RcsMessage {
+ /**
+ * The value to indicate that this {@link RcsMessage} does not have any location information.
+ */
+ public static final double LOCATION_NOT_SET = Double.MIN_VALUE;
+
+ /**
+ * The status to indicate that this {@link RcsMessage}s status is not set yet.
+ */
+ public static final int NOT_SET = 0;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} is a draft and is not in the process of
+ * sending yet.
+ */
+ public static final int DRAFT = 1;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} was successfully sent.
+ */
+ public static final int QUEUED = 2;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} is actively being sent.
+ */
+ public static final int SENDING = 3;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} was successfully sent.
+ */
+ public static final int SENT = 4;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} failed to send in an attempt before, and
+ * now being retried.
+ */
+ public static final int RETRYING = 5;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} has permanently failed to send.
+ */
+ public static final int FAILED = 6;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} was successfully received.
+ */
+ public static final int RECEIVED = 7;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} was seen.
+ */
+ public static final int SEEN = 9;
+
+ protected int mId;
+
+ @IntDef({
+ DRAFT, QUEUED, SENDING, SENT, RETRYING, FAILED, RECEIVED, SEEN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RcsMessageStatus {
+ }
+
+ RcsMessage(int id) {
+ mId = id;
+ }
+
+ /**
+ * Returns the row Id from the common message.
+ *
+ * @hide
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * @return Returns the subscription ID that this {@link RcsMessage} was sent from, or delivered
+ * to.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ * @see android.telephony.SubscriptionInfo#getSubscriptionId
+ */
+ public int getSubscriptionId() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessageSubId(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the subscription ID that this {@link RcsMessage} was sent from, or delivered to and
+ * persists it into storage.
+ *
+ * @param subId The subscription ID to persists into storage.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ * @see android.telephony.SubscriptionInfo#getSubscriptionId
+ */
+ @WorkerThread
+ public void setSubscriptionId(int subId) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setMessageSubId(mId, isIncoming(), subId));
+ }
+
+ /**
+ * Sets the status of this message and persists it into storage. Please see
+ * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers.
+ *
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setStatus(@RcsMessageStatus int rcsMessageStatus) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setMessageStatus(mId, isIncoming(), rcsMessageStatus));
+ }
+
+ /**
+ * @return Returns the status of this message. Please see
+ * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public @RcsMessageStatus int getStatus() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessageStatus(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the origination timestamp of this message and persists it into storage. Origination is
+ * defined as when the sender tapped the send button.
+ *
+ * @param timestamp The origination timestamp value in milliseconds passed after midnight,
+ * January 1, 1970 UTC
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setOriginationTimestamp(long timestamp) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setMessageOriginationTimestamp(mId, isIncoming(), timestamp));
+ }
+
+ /**
+ * @return Returns the origination timestamp of this message in milliseconds passed after
+ * midnight, January 1, 1970 UTC. Origination is defined as when the sender tapped the send
+ * button.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getOriginationTimestamp() throws RcsMessageStoreException {
+ return RcsControllerCall.call(
+ iRcs -> iRcs.getMessageOriginationTimestamp(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the globally unique RCS message identifier for this message and persists it into
+ * storage. This function does not confirm that this message id is unique. Please see 4.4.5.2
+ * - GSMA RCC.53 (RCS Device API 1.6 Specification
+ *
+ * @param rcsMessageGlobalId The globally RCS message identifier
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setRcsMessageId(String rcsMessageGlobalId) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setGlobalMessageIdForMessage(mId, isIncoming(), rcsMessageGlobalId));
+ }
+
+ /**
+ * @return Returns the globally unique RCS message identifier for this message. Please see
+ * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public String getRcsMessageId() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getGlobalMessageIdForMessage(mId, isIncoming()));
+ }
+
+ /**
+ * @return Returns the user visible text included in this message.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public String getText() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getTextForMessage(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the user visible text for this message and persists in storage.
+ *
+ * @param text The text this message now has
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setText(String text) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setTextForMessage(mId, isIncoming(), text));
+ }
+
+ /**
+ * @return Returns the associated latitude for this message, or
+ * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location.
+ *
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public double getLatitude() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getLatitudeForMessage(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the latitude for this message and persists in storage.
+ *
+ * @param latitude The latitude for this location message.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setLatitude(double latitude) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setLatitudeForMessage(mId, isIncoming(), latitude));
+ }
+
+ /**
+ * @return Returns the associated longitude for this message, or
+ * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location.
+ *
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public double getLongitude() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getLongitudeForMessage(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the longitude for this message and persists in storage.
+ *
+ * @param longitude The longitude for this location message.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setLongitude(double longitude) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setLongitudeForMessage(mId, isIncoming(), longitude));
+ }
+
+ /**
+ * Attaches an {@link RcsFileTransferPart} to this message and persists into storage.
+ *
+ * @param fileTransferCreationParameters The parameters to be used to create the
+ * {@link RcsFileTransferPart}
+ * @return A new instance of {@link RcsFileTransferPart}
+ * @throws RcsMessageStoreException if the file transfer could not be persisted into storage.
+ */
+ @NonNull
+ @WorkerThread
+ public RcsFileTransferPart insertFileTransfer(
+ RcsFileTransferCreationParameters fileTransferCreationParameters)
+ throws RcsMessageStoreException {
+ return new RcsFileTransferPart(RcsControllerCall.call(
+ iRcs -> iRcs.storeFileTransfer(mId, isIncoming(), fileTransferCreationParameters)));
+ }
+
+ /**
+ * @return Returns all the {@link RcsFileTransferPart}s associated with this message in an
+ * unmodifiable set.
+ * @throws RcsMessageStoreException if the file transfers could not be read from the storage
+ */
+ @NonNull
+ @WorkerThread
+ public Set<RcsFileTransferPart> getFileTransferParts() throws RcsMessageStoreException {
+ Set<RcsFileTransferPart> fileTransferParts = new HashSet<>();
+
+ int[] fileTransferIds = RcsControllerCall.call(
+ iRcs -> iRcs.getFileTransfersAttachedToMessage(mId, isIncoming()));
+
+ for (int fileTransfer : fileTransferIds) {
+ fileTransferParts.add(new RcsFileTransferPart(fileTransfer));
+ }
+
+ return Collections.unmodifiableSet(fileTransferParts);
+ }
+
+ /**
+ * Removes a {@link RcsFileTransferPart} from this message, and deletes it in storage.
+ *
+ * @param fileTransferPart The part to delete.
+ * @throws RcsMessageStoreException if the file transfer could not be removed from storage
+ */
+ @WorkerThread
+ public void removeFileTransferPart(@NonNull RcsFileTransferPart fileTransferPart)
+ throws RcsMessageStoreException {
+ if (fileTransferPart == null) {
+ return;
+ }
+
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.deleteFileTransfer(fileTransferPart.getId()));
+ }
+
+ /**
+ * @return Returns {@code true} if this message was received on this device, {@code false} if it
+ * was sent.
+ */
+ public abstract boolean isIncoming();
}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsMessageCreationParameters.aidl
similarity index 93%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsMessageCreationParameters.aidl
index 82d985d..5774d00 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessageCreationParameters.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsMessageCreationParameters;
diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParameters.java b/telephony/java/android/telephony/ims/RcsMessageCreationParameters.java
new file mode 100644
index 0000000..ff3f33e
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageCreationParameters.java
@@ -0,0 +1,265 @@
+/*
+ * 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.telephony.ims;
+
+import static android.telephony.ims.RcsMessage.LOCATION_NOT_SET;
+
+import android.annotation.CheckResult;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.SubscriptionInfo;
+
+/**
+ * The collection of parameters to be passed into
+ * {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParameters)} and
+ * {@link RcsThread#addOutgoingMessage(RcsMessageCreationParameters)} to create and persist
+ * {@link RcsMessage}s on an {@link RcsThread}
+ *
+ * @hide TODO - make public
+ */
+public class RcsMessageCreationParameters implements Parcelable {
+ // The globally unique id of the RcsMessage to be created.
+ private final String mRcsMessageGlobalId;
+
+ // The subscription that this message was/will be received/sent from.
+ private final int mSubId;
+ // The sending/receiving status of the message
+ private final @RcsMessage.RcsMessageStatus int mMessageStatus;
+ // The timestamp of message creation
+ private final long mOriginationTimestamp;
+ // The user visible content of the message
+ private final String mText;
+ // The latitude of the message if this is a location message
+ private final double mLatitude;
+ // The longitude of the message if this is a location message
+ private final double mLongitude;
+
+ /**
+ * @return Returns the globally unique RCS Message ID for the {@link RcsMessage} to be created.
+ * Please see 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
+ */
+ @Nullable
+ public String getRcsMessageGlobalId() {
+ return mRcsMessageGlobalId;
+ }
+
+ /**
+ * @return Returns the subscription ID that was used to send or receive the {@link RcsMessage}
+ * to be created.
+ */
+ public int getSubId() {
+ return mSubId;
+ }
+
+ /**
+ * @return Returns the status for the {@link RcsMessage} to be created.
+ * @see RcsMessage.RcsMessageStatus
+ */
+ public int getMessageStatus() {
+ return mMessageStatus;
+ }
+
+ /**
+ * @return Returns the origination timestamp of the {@link RcsMessage} to be created in
+ * milliseconds passed after midnight, January 1, 1970 UTC. Origination is defined as when
+ * the sender tapped the send button.
+ */
+ public long getOriginationTimestamp() {
+ return mOriginationTimestamp;
+ }
+
+ /**
+ * @return Returns the user visible text contained in the {@link RcsMessage} to be created
+ */
+ @Nullable
+ public String getText() {
+ return mText;
+ }
+
+ /**
+ * @return Returns the latitude of the {@link RcsMessage} to be created, or
+ * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location.
+ */
+ public double getLatitude() {
+ return mLatitude;
+ }
+
+ /**
+ * @return Returns the longitude of the {@link RcsMessage} to be created, or
+ * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location.
+ */
+ public double getLongitude() {
+ return mLongitude;
+ }
+
+ /**
+ * The base builder for creating {@link RcsMessage}s on {@link RcsThread}s.
+ *
+ * @see RcsIncomingMessageCreationParameters
+ */
+ public static class Builder {
+ private String mRcsMessageGlobalId;
+ private int mSubId;
+ private @RcsMessage.RcsMessageStatus int mMessageStatus;
+ private long mOriginationTimestamp;
+ private String mText;
+ private double mLatitude = LOCATION_NOT_SET;
+ private double mLongitude = LOCATION_NOT_SET;
+
+ /**
+ * Creates a new {@link Builder} to create an instance of
+ * {@link RcsMessageCreationParameters}.
+ *
+ * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination
+ * timestamp value in milliseconds passed after midnight,
+ * January 1, 1970 UTC
+ * @param subscriptionId The subscription ID that was used to send or receive this
+ * {@link RcsMessage}
+ * @see SubscriptionInfo#getSubscriptionId()
+ */
+ public Builder(long originationTimestamp, int subscriptionId) {
+ mOriginationTimestamp = originationTimestamp;
+ mSubId = subscriptionId;
+ }
+
+ /**
+ * Sets the status of the {@link RcsMessage} to be built.
+ *
+ * @param rcsMessageStatus The status to be set
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#setStatus(int)
+ */
+ @CheckResult
+ public Builder setStatus(@RcsMessage.RcsMessageStatus int rcsMessageStatus) {
+ mMessageStatus = rcsMessageStatus;
+ return this;
+ }
+
+ /**
+ * Sets the globally unique RCS message identifier for the {@link RcsMessage} to be built.
+ * This function does not confirm that this message id is unique. Please see 4.4.5.2 - GSMA
+ * RCC.53 (RCS Device API 1.6 Specification)
+ *
+ * @param rcsMessageId The ID to be set
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#setRcsMessageId(String)
+ */
+ @CheckResult
+ public Builder setRcsMessageId(String rcsMessageId) {
+ mRcsMessageGlobalId = rcsMessageId;
+ return this;
+ }
+
+ /**
+ * Sets the text of the {@link RcsMessage} to be built.
+ *
+ * @param text The user visible text of the message
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#setText(String)
+ */
+ @CheckResult
+ public Builder setText(String text) {
+ mText = text;
+ return this;
+ }
+
+ /**
+ * Sets the latitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71
+ * (RCS Universal Profile Service Definition Document)
+ *
+ * @param latitude The latitude of the location information associated with this message.
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#setLatitude(double)
+ */
+ @CheckResult
+ public Builder setLatitude(double latitude) {
+ mLatitude = latitude;
+ return this;
+ }
+
+ /**
+ * Sets the longitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71
+ * (RCS Universal Profile Service Definition Document)
+ *
+ * @param longitude The longitude of the location information associated with this message.
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#setLongitude(double)
+ */
+ @CheckResult
+ public Builder setLongitude(double longitude) {
+ mLongitude = longitude;
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public RcsMessageCreationParameters build() {
+ return new RcsMessageCreationParameters(this);
+ }
+ }
+
+ protected RcsMessageCreationParameters(Builder builder) {
+ mRcsMessageGlobalId = builder.mRcsMessageGlobalId;
+ mSubId = builder.mSubId;
+ mMessageStatus = builder.mMessageStatus;
+ mOriginationTimestamp = builder.mOriginationTimestamp;
+ mText = builder.mText;
+ mLatitude = builder.mLatitude;
+ mLongitude = builder.mLongitude;
+ }
+
+ protected RcsMessageCreationParameters(Parcel in) {
+ mRcsMessageGlobalId = in.readString();
+ mSubId = in.readInt();
+ mMessageStatus = in.readInt();
+ mOriginationTimestamp = in.readLong();
+ mText = in.readString();
+ mLatitude = in.readDouble();
+ mLongitude = in.readDouble();
+ }
+
+ public static final Creator<RcsMessageCreationParameters> CREATOR =
+ new Creator<RcsMessageCreationParameters>() {
+ @Override
+ public RcsMessageCreationParameters createFromParcel(Parcel in) {
+ return new RcsMessageCreationParameters(in);
+ }
+
+ @Override
+ public RcsMessageCreationParameters[] newArray(int size) {
+ return new RcsMessageCreationParameters[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mRcsMessageGlobalId);
+ dest.writeInt(mSubId);
+ dest.writeInt(mMessageStatus);
+ dest.writeLong(mOriginationTimestamp);
+ dest.writeString(mText);
+ dest.writeDouble(mLatitude);
+ dest.writeDouble(mLongitude);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryParameters.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsMessageQueryParameters.aidl
index 82d985d..c325c23 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryParameters.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsMessageQueryParameters;
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParameters.java b/telephony/java/android/telephony/ims/RcsMessageQueryParameters.java
new file mode 100644
index 0000000..c964cf9
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryParameters.java
@@ -0,0 +1,361 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+
+/**
+ * The parameters to pass into
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParameters)} in order to select a
+ * subset of {@link RcsMessage}s present in the message store.
+ *
+ * @hide TODO - make the Builder and builder() public. The rest should stay internal only.
+ */
+public class RcsMessageQueryParameters implements Parcelable {
+ /**
+ * @hide - not meant for public use
+ */
+ public static final int THREAD_ID_NOT_SET = -1;
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+ * be sorted in the same order of {@link RcsMessage}s that got persisted into storage for faster
+ * results.
+ */
+ public static final int SORT_BY_CREATION_ORDER = 0;
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+ * be sorted according to the timestamp of {@link RcsMessage#getOriginationTimestamp()}
+ */
+ public static final int SORT_BY_TIMESTAMP = 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
+ public @interface SortingProperty {
+ }
+
+ /**
+ * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParameters)} return
+ * {@link RcsIncomingMessage}s.
+ */
+ public static final int MESSAGE_TYPE_INCOMING = 0x0001;
+
+ /**
+ * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParameters)} return
+ * {@link RcsOutgoingMessage}s.
+ */
+ public static final int MESSAGE_TYPE_OUTGOING = 0x0002;
+
+ /**
+ * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParameters)} return {@link RcsMessage}s
+ * that have an {@link RcsFileTransferPart} attached.
+ */
+ public static final int MESSAGES_WITH_FILE_TRANSFERS = 0x0004;
+
+ /**
+ * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParameters)} return {@link RcsMessage}s
+ * that don't have an {@link RcsFileTransferPart} attached.
+ */
+ public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 0x0008;
+
+ /**
+ * @hide - not meant for public use
+ */
+ public static final String MESSAGE_QUERY_PARAMETERS_KEY = "message_query_parameters";
+
+ // Whether the result should be filtered against incoming or outgoing messages
+ private int mMessageType;
+ // Whether the result should have file transfer messages attached or not
+ private int mFileTransferPresence;
+ // The SQL "Like" clause to filter messages
+ private String mMessageLike;
+ // The property the messages should be sorted against
+ private @SortingProperty int mSortingProperty;
+ // Whether the messages should be sorted in ascending order
+ private boolean mIsAscending;
+ // The number of results that should be returned with this query
+ private int mLimit;
+ // The thread that the results should be limited to
+ private int mThreadId;
+
+ RcsMessageQueryParameters(int messageType, int fileTransferPresence, String messageLike,
+ int threadId, @SortingProperty int sortingProperty, boolean isAscending, int limit) {
+ mMessageType = messageType;
+ mFileTransferPresence = fileTransferPresence;
+ mMessageLike = messageLike;
+ mSortingProperty = sortingProperty;
+ mIsAscending = isAscending;
+ mLimit = limit;
+ mThreadId = threadId;
+ }
+
+ /**
+ * @return Returns the type of {@link RcsMessage}s that this {@link RcsMessageQueryParameters}
+ * is set to query for.
+ */
+ public int getMessageType() {
+ return mMessageType;
+ }
+
+ /**
+ * @return Returns whether the result query should return {@link RcsMessage}s with
+ * {@link RcsFileTransferPart}s or not
+ */
+ public int getFileTransferPresence() {
+ return mFileTransferPresence;
+ }
+
+ /**
+ * @return Returns the SQL-inspired "LIKE" clause that will be used to match {@link RcsMessage}s
+ */
+ public String getMessageLike() {
+ return mMessageLike;
+ }
+
+ /**
+ * @return Returns the number of {@link RcsThread}s to be returned from the query. A value of
+ * 0 means there is no set limit.
+ */
+ public int getLimit() {
+ return mLimit;
+ }
+
+ /**
+ * @return Returns the property that will be used to sort the result against.
+ * @see SortingProperty
+ */
+ public @SortingProperty int getSortingProperty() {
+ return mSortingProperty;
+ }
+
+ /**
+ * @return Returns {@code true} if the result set will be sorted in ascending order,
+ * {@code false} if it will be sorted in descending order.
+ */
+ public boolean getSortDirection() {
+ return mIsAscending;
+ }
+
+ /**
+ * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+ * the thread that the result query should be limited to.
+ *
+ * As we do not expose any sort of integer ID's to public usage, this should be hidden.
+ *
+ * @hide - not meant for public use
+ */
+ public int getThreadId() {
+ return mThreadId;
+ }
+
+ /**
+ * A helper class to build the {@link RcsMessageQueryParameters}.
+ */
+ public static class Builder {
+ private @SortingProperty int mSortingProperty;
+ private int mMessageType;
+ private int mFileTransferPresence;
+ private String mMessageLike;
+ private boolean mIsAscending;
+ private int mLimit = 100;
+ private int mThreadId = THREAD_ID_NOT_SET;
+
+ /**
+ * Creates a new builder for {@link RcsMessageQueryParameters} to be used in
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParameters)}
+ *
+ */
+ public Builder() {
+ // empty implementation
+ }
+
+ /**
+ * Desired number of threads to be returned from the query. Passing in 0 will return all
+ * existing threads at once. The limit defaults to 100.
+ *
+ * @param limit The number to limit the query result to.
+ * @return The same instance of the builder to chain parameters.
+ * @throws InvalidParameterException If the given limit is negative.
+ */
+ @CheckResult
+ public Builder setResultLimit(@IntRange(from = 0) int limit)
+ throws InvalidParameterException {
+ if (limit < 0) {
+ throw new InvalidParameterException("The query limit must be non-negative");
+ }
+
+ mLimit = limit;
+ return this;
+ }
+
+ /**
+ * Sets the type of messages to be returned from the query.
+ *
+ * @param messageType The type of message to be returned.
+ * @return The same instance of the builder to chain parameters.
+ * @see RcsMessageQueryParameters#MESSAGE_TYPE_INCOMING
+ * @see RcsMessageQueryParameters#MESSAGE_TYPE_OUTGOING
+ */
+ @CheckResult
+ public Builder setMessageType(int messageType) {
+ mMessageType = messageType;
+ return this;
+ }
+
+ /**
+ * Sets whether file transfer messages should be included in the query result or not.
+ *
+ * @param fileTransferPresence Whether file transfers should be included in the result
+ * @return The same instance of the builder to chain parameters.
+ * @see RcsMessageQueryParameters#MESSAGES_WITH_FILE_TRANSFERS
+ * @see RcsMessageQueryParameters#MESSAGES_WITHOUT_FILE_TRANSFERS
+ */
+ @CheckResult
+ public Builder setFileTransferPresence(int fileTransferPresence) {
+ mFileTransferPresence = fileTransferPresence;
+ return this;
+ }
+
+ /**
+ * Sets an SQL-inspired "like" clause to match with messages. Using a percent sign ('%')
+ * wildcard matches any sequence of zero or more characters. Using an underscore ('_')
+ * wildcard matches any single character. Not using any wildcards would only perform a
+ * string match. The input string is case-insensitive.
+ *
+ * The input "Wh%" would match messages "who", "where" and "what", while the input "Wh_"
+ * would only match "who"
+ *
+ * @param messageLike The "like" clause for matching {@link RcsMessage}s.
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setMessageLike(String messageLike) {
+ mMessageLike = messageLike;
+ return this;
+ }
+
+ /**
+ * Sets the property where the results should be sorted against. Defaults to
+ * {@link RcsMessageQueryParameters.SortingProperty#SORT_BY_CREATION_ORDER}
+ *
+ * @param sortingProperty against which property the results should be sorted
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortProperty(@SortingProperty int sortingProperty) {
+ mSortingProperty = sortingProperty;
+ return this;
+ }
+
+ /**
+ * Sets whether the results should be sorted ascending or descending
+ *
+ * @param isAscending whether the results should be sorted ascending
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortDirection(boolean isAscending) {
+ mIsAscending = isAscending;
+ return this;
+ }
+
+ /**
+ * Limits the results to the given thread.
+ *
+ * @param thread the {@link RcsThread} that results should be limited to. If set to
+ * {@code null}, messages on all threads will be queried
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setThread(@Nullable RcsThread thread) {
+ if (thread == null) {
+ mThreadId = THREAD_ID_NOT_SET;
+ } else {
+ mThreadId = thread.getThreadId();
+ }
+ return this;
+ }
+
+ /**
+ * Builds the {@link RcsMessageQueryParameters} to use in
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParameters)}
+ *
+ * @return An instance of {@link RcsMessageQueryParameters} to use with the message
+ * query.
+ */
+ public RcsMessageQueryParameters build() {
+ return new RcsMessageQueryParameters(mMessageType, mFileTransferPresence, mMessageLike,
+ mThreadId, mSortingProperty, mIsAscending, mLimit);
+ }
+ }
+
+ /**
+ * Parcelable boilerplate below.
+ */
+ protected RcsMessageQueryParameters(Parcel in) {
+ mMessageType = in.readInt();
+ mFileTransferPresence = in.readInt();
+ mMessageLike = in.readString();
+ mSortingProperty = in.readInt();
+ mIsAscending = in.readBoolean();
+ mLimit = in.readInt();
+ mThreadId = in.readInt();
+ }
+
+ public static final Creator<RcsMessageQueryParameters> CREATOR =
+ new Creator<RcsMessageQueryParameters>() {
+ @Override
+ public RcsMessageQueryParameters createFromParcel(Parcel in) {
+ return new RcsMessageQueryParameters(in);
+ }
+
+ @Override
+ public RcsMessageQueryParameters[] newArray(int size) {
+ return new RcsMessageQueryParameters[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMessageType);
+ dest.writeInt(mFileTransferPresence);
+ dest.writeString(mMessageLike);
+ dest.writeInt(mSortingProperty);
+ dest.writeBoolean(mIsAscending);
+ dest.writeInt(mLimit);
+ dest.writeInt(mThreadId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl
index 82d985d..a73ba50 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsMessageQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
new file mode 100644
index 0000000..c3846fd
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
@@ -0,0 +1,115 @@
+/*
+ * 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.telephony.ims;
+
+import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_INCOMING;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.ims.RcsTypeIdPair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParameters)}
+ * call. This class allows getting the token for querying the next batch of messages in order to
+ * prevent handling large amounts of data at once.
+ *
+ * @hide
+ */
+public class RcsMessageQueryResult implements Parcelable {
+ // The token to continue the query to get the next batch of results
+ private RcsQueryContinuationToken mContinuationToken;
+ // The message type and message ID pairs for all the messages in this query result
+ private List<RcsTypeIdPair> mMessageTypeIdPairs;
+
+ /**
+ * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+ * to create query results
+ *
+ * @hide
+ */
+ public RcsMessageQueryResult(
+ RcsQueryContinuationToken continuationToken,
+ List<RcsTypeIdPair> messageTypeIdPairs) {
+ mContinuationToken = continuationToken;
+ mMessageTypeIdPairs = messageTypeIdPairs;
+ }
+
+ /**
+ * Returns a token to call
+ * {@link RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)}
+ * to get the next batch of {@link RcsMessage}s.
+ */
+ @Nullable
+ public RcsQueryContinuationToken getContinuationToken() {
+ return mContinuationToken;
+ }
+
+ /**
+ * Returns all the {@link RcsMessage}s in the current query result. Call {@link
+ * RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)} to get the next batch
+ * of {@link RcsMessage}s.
+ */
+ @NonNull
+ public List<RcsMessage> getMessages() {
+ List<RcsMessage> messages = new ArrayList<>();
+ for (RcsTypeIdPair typeIdPair : mMessageTypeIdPairs) {
+ if (typeIdPair.getType() == MESSAGE_TYPE_INCOMING) {
+ messages.add(new RcsIncomingMessage(typeIdPair.getId()));
+ } else {
+ messages.add(new RcsOutgoingMessage(typeIdPair.getId()));
+ }
+ }
+
+ return messages;
+ }
+
+ protected RcsMessageQueryResult(Parcel in) {
+ mContinuationToken = in.readParcelable(
+ RcsQueryContinuationToken.class.getClassLoader());
+ in.readTypedList(mMessageTypeIdPairs, RcsTypeIdPair.CREATOR);
+ }
+
+ public static final Creator<RcsMessageQueryResult> CREATOR =
+ new Creator<RcsMessageQueryResult>() {
+ @Override
+ public RcsMessageQueryResult createFromParcel(Parcel in) {
+ return new RcsMessageQueryResult(in);
+ }
+
+ @Override
+ public RcsMessageQueryResult[] newArray(int size) {
+ return new RcsMessageQueryResult[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mContinuationToken, flags);
+ dest.writeTypedList(mMessageTypeIdPairs);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
similarity index 95%
rename from telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
rename to telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
index 9fdc41d..99b8eb7 100644
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable Rcs1To1Thread;
+parcelable RcsMessageSnippet;
diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
new file mode 100644
index 0000000..9399c20
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
@@ -0,0 +1,98 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.RcsMessage.RcsMessageStatus;
+
+/**
+ * An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread}
+ *
+ * @hide TODO: make public
+ */
+public class RcsMessageSnippet implements Parcelable {
+ private final String mText;
+ private final @RcsMessageStatus int mStatus;
+ private final long mTimestamp;
+
+ /**
+ * @hide
+ */
+ public RcsMessageSnippet(String text, @RcsMessageStatus int status, long timestamp) {
+ mText = text;
+ mStatus = status;
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * @return Returns the text of the {@link RcsMessage} with highest origination timestamp value
+ * (i.e. latest) in this thread
+ */
+ @Nullable
+ public String getSnippetText() {
+ return mText;
+ }
+
+ /**
+ * @return Returns the status of the {@link RcsMessage} with highest origination timestamp value
+ * (i.e. latest) in this thread
+ */
+ public @RcsMessageStatus int getSnippetStatus() {
+ return mStatus;
+ }
+
+ /**
+ * @return Returns the timestamp of the {@link RcsMessage} with highest origination timestamp
+ * value (i.e. latest) in this thread
+ */
+ public long getSnippetTimestamp() {
+ return mTimestamp;
+ }
+
+ protected RcsMessageSnippet(Parcel in) {
+ mText = in.readString();
+ mStatus = in.readInt();
+ mTimestamp = in.readLong();
+ }
+
+ public static final Creator<RcsMessageSnippet> CREATOR =
+ new Creator<RcsMessageSnippet>() {
+ @Override
+ public RcsMessageSnippet createFromParcel(Parcel in) {
+ return new RcsMessageSnippet(in);
+ }
+
+ @Override
+ public RcsMessageSnippet[] newArray(int size) {
+ return new RcsMessageSnippet[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mText);
+ dest.writeInt(mStatus);
+ dest.writeLong(mTimestamp);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
index 1bf6ffd..c8c36a8 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStore.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -16,106 +16,223 @@
package android.telephony.ims;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.Rlog;
-import android.telephony.ims.aidl.IRcs;
+import android.net.Uri;
+
+import java.util.List;
/**
* RcsMessageStore is the application interface to RcsProvider and provides access methods to
* RCS related database tables.
+ *
* @hide - TODO make this public
*/
public class RcsMessageStore {
- static final String TAG = "RcsMessageStore";
-
/**
* Returns the first chunk of existing {@link RcsThread}s in the common storage.
+ *
* @param queryParameters Parameters to specify to return a subset of all RcsThreads.
* Passing a value of null will return all threads.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
*/
@WorkerThread
- public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParameters queryParameters) {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- return iRcs.getRcsThreads(queryParameters);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
- }
-
- return null;
+ @NonNull
+ public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParameters queryParameters)
+ throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getRcsThreads(queryParameters));
}
/**
* Returns the next chunk of {@link RcsThread}s in the common storage.
+ *
* @param continuationToken A token to continue the query to get the next chunk. This is
- * obtained through {@link RcsThreadQueryResult#nextChunkToken}.
+ * obtained through {@link RcsThreadQueryResult#getContinuationToken}.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
*/
@WorkerThread
- public RcsThreadQueryResult getRcsThreads(RcsThreadQueryContinuationToken continuationToken) {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- return iRcs.getRcsThreadsWithToken(continuationToken);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
- }
+ @NonNull
+ public RcsThreadQueryResult getRcsThreads(@NonNull RcsQueryContinuationToken continuationToken)
+ throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getRcsThreadsWithToken(continuationToken));
+ }
- return null;
+ /**
+ * Returns the first chunk of existing {@link RcsParticipant}s in the common storage.
+ *
+ * @param queryParameters Parameters to specify to return a subset of all RcsParticipants.
+ * Passing a value of null will return all participants.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsParticipantQueryResult getRcsParticipants(
+ @Nullable RcsParticipantQueryParameters queryParameters)
+ throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getParticipants(queryParameters));
+ }
+
+ /**
+ * Returns the next chunk of {@link RcsParticipant}s in the common storage.
+ *
+ * @param continuationToken A token to continue the query to get the next chunk. This is
+ * obtained through
+ * {@link RcsParticipantQueryResult#getContinuationToken}
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsParticipantQueryResult getRcsParticipants(
+ @NonNull RcsQueryContinuationToken continuationToken)
+ throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getParticipantsWithToken(continuationToken));
+ }
+
+ /**
+ * Returns the first chunk of existing {@link RcsMessage}s in the common storage.
+ *
+ * @param queryParameters Parameters to specify to return a subset of all RcsMessages.
+ * Passing a value of null will return all messages.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsMessageQueryResult getRcsMessages(
+ @Nullable RcsMessageQueryParameters queryParameters) throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessages(queryParameters));
+ }
+
+ /**
+ * Returns the next chunk of {@link RcsMessage}s in the common storage.
+ *
+ * @param continuationToken A token to continue the query to get the next chunk. This is
+ * obtained through {@link RcsMessageQueryResult#getContinuationToken}
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsMessageQueryResult getRcsMessages(
+ @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessagesWithToken(continuationToken));
+ }
+
+ /**
+ * Returns the first chunk of existing {@link RcsEvent}s in the common storage.
+ *
+ * @param queryParameters Parameters to specify to return a subset of all RcsEvents.
+ * Passing a value of null will return all events.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsEventQueryResult getRcsEvents(
+ @Nullable RcsEventQueryParameters queryParameters) throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getEvents(queryParameters));
+ }
+
+ /**
+ * Returns the next chunk of {@link RcsEvent}s in the common storage.
+ *
+ * @param continuationToken A token to continue the query to get the next chunk. This is
+ * obtained through {@link RcsEventQueryResult#getContinuationToken}.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsEventQueryResult getRcsEvents(
+ @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getEventsWithToken(continuationToken));
+ }
+
+ /**
+ * Persists an {@link RcsEvent} to common storage.
+ *
+ * @param persistableEvent The {@link RcsEvent} to persist into storage.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ *
+ * @see RcsGroupThreadNameChangedEvent
+ * @see RcsGroupThreadIconChangedEvent
+ * @see RcsGroupThreadParticipantJoinedEvent
+ * @see RcsGroupThreadParticipantLeftEvent
+ * @see RcsParticipantAliasChangedEvent
+ */
+ @WorkerThread
+ @NonNull
+ public void persistRcsEvent(RcsEvent persistableEvent) throws RcsMessageStoreException {
+ persistableEvent.persist();
}
/**
* Creates a new 1 to 1 thread with the given participant and persists it in the storage.
+ *
+ * @param recipient The {@link RcsParticipant} that will receive the messages in this thread.
+ * @return The newly created {@link Rcs1To1Thread}
+ * @throws RcsMessageStoreException if the thread could not be persisted in the storage
*/
@WorkerThread
- public Rcs1To1Thread createRcs1To1Thread(RcsParticipant recipient) {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- return iRcs.createRcs1To1Thread(recipient);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcs1To1Thread", re);
- }
-
- return null;
+ @NonNull
+ public Rcs1To1Thread createRcs1To1Thread(@NonNull RcsParticipant recipient)
+ throws RcsMessageStoreException {
+ return new Rcs1To1Thread(
+ RcsControllerCall.call(iRcs -> iRcs.createRcs1To1Thread(recipient.getId())));
}
/**
- * Delete the {@link RcsThread} identified by the given threadId.
- * @param threadId threadId of the thread to be deleted.
+ * Creates a new group thread with the given participants and persists it in the storage.
+ *
+ * @throws RcsMessageStoreException if the thread could not be persisted in the storage
*/
@WorkerThread
- public void deleteThread(int threadId) {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- iRcs.deleteThread(threadId);
+ @NonNull
+ public RcsGroupThread createGroupThread(@Nullable List<RcsParticipant> recipients,
+ @Nullable String groupName, @Nullable Uri groupIcon) throws RcsMessageStoreException {
+ int[] recipientIds = null;
+ if (recipients != null) {
+ recipientIds = new int[recipients.size()];
+
+ for (int i = 0; i < recipients.size(); i++) {
+ recipientIds[i] = recipients.get(i).getId();
}
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsMessageStore: Exception happened during deleteThread", re);
+ }
+
+ int[] finalRecipientIds = recipientIds;
+ return new RcsGroupThread(RcsControllerCall.call(
+ iRcs -> iRcs.createGroupThread(finalRecipientIds, groupName, groupIcon)));
+ }
+
+ /**
+ * Delete the given {@link RcsThread} from the storage.
+ *
+ * @param thread The thread to be deleted.
+ * @throws RcsMessageStoreException if the thread could not be deleted from the storage
+ */
+ @WorkerThread
+ public void deleteThread(@NonNull RcsThread thread) throws RcsMessageStoreException {
+ if (thread == null) {
+ return;
+ }
+
+ boolean isDeleteSucceeded = RcsControllerCall.call(
+ iRcs -> iRcs.deleteThread(thread.getThreadId(), thread.getThreadType()));
+
+ if (!isDeleteSucceeded) {
+ throw new RcsMessageStoreException("Could not delete RcsThread");
}
}
/**
* Creates a new participant and persists it in the storage.
+ *
* @param canonicalAddress The defining address (e.g. phone number) of the participant.
+ * @param alias The RCS alias for the participant.
+ * @throws RcsMessageStoreException if the participant could not be created on the storage
*/
- public RcsParticipant createRcsParticipant(String canonicalAddress) {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- return iRcs.createRcsParticipant(canonicalAddress);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcsParticipant", re);
- }
-
- return null;
+ @WorkerThread
+ @NonNull
+ public RcsParticipant createRcsParticipant(String canonicalAddress, @Nullable String alias)
+ throws RcsMessageStoreException {
+ return new RcsParticipant(
+ RcsControllerCall.call(iRcs -> iRcs.createRcsParticipant(canonicalAddress, alias)));
}
}
diff --git a/telephony/java/android/telephony/ims/RcsMessageStoreException.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
new file mode 100644
index 0000000..e158f1a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.telephony.ims;
+
+/**
+ * An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in
+ * {@link android.telephony.ims}
+ *
+ * @hide TODO: make public
+ */
+public class RcsMessageStoreException extends Exception {
+
+ /**
+ * Constructs an {@link RcsMessageStoreException} with the specified detail message.
+ * @param message The detail message
+ * @see Throwable#getMessage()
+ */
+ public RcsMessageStoreException(String message) {
+ super(message);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java b/telephony/java/android/telephony/ims/RcsMultiMediaPart.java
deleted file mode 100644
index d295fba..0000000
--- a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a media that is rendered on the screen
- * (i.e. image, video etc)
- * @hide - TODO(sahinc) make this public
- */
-public class RcsMultiMediaPart extends RcsFileTransferPart {
- public static final Creator<RcsMultiMediaPart> CREATOR = new Creator<RcsMultiMediaPart>() {
- @Override
- public RcsMultiMediaPart createFromParcel(Parcel in) {
- return new RcsMultiMediaPart(in);
- }
-
- @Override
- public RcsMultiMediaPart[] newArray(int size) {
- return new RcsMultiMediaPart[size];
- }
- };
-
- protected RcsMultiMediaPart(Parcel in) {
- super(in);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl b/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
deleted file mode 100644
index 5992d95..0000000
--- a/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsMultimediaPart;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
deleted file mode 100644
index 6e0c80f..0000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsOutgoingMessage;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
index bfb1611..0bd55ec 100644
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
@@ -15,34 +15,53 @@
*/
package android.telephony.ims;
-import android.os.Parcel;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* This is a single instance of a message sent over RCS.
- * @hide - TODO(sahinc) make this public
+ *
+ * @hide - TODO(109759350) make this public
*/
public class RcsOutgoingMessage extends RcsMessage {
- public static final Creator<RcsOutgoingMessage> CREATOR = new Creator<RcsOutgoingMessage>() {
- @Override
- public RcsOutgoingMessage createFromParcel(Parcel in) {
- return new RcsOutgoingMessage(in);
- }
-
- @Override
- public RcsOutgoingMessage[] newArray(int size) {
- return new RcsOutgoingMessage[size];
- }
- };
-
- protected RcsOutgoingMessage(Parcel in) {
+ RcsOutgoingMessage(int id) {
+ super(id);
}
- @Override
- public int describeContents() {
- return 0;
+ /**
+ * @return Returns the {@link RcsOutgoingMessageDelivery}s associated with this message. Please
+ * note that the deliveries returned for the {@link RcsOutgoingMessage} may not always match the
+ * {@link RcsParticipant}s on the {@link RcsGroupThread} as the group recipients may have
+ * changed.
+ * @throws RcsMessageStoreException if the outgoing deliveries could not be read from storage.
+ */
+ @NonNull
+ @WorkerThread
+ public List<RcsOutgoingMessageDelivery> getOutgoingDeliveries()
+ throws RcsMessageStoreException {
+ int[] deliveryParticipants;
+ List<RcsOutgoingMessageDelivery> messageDeliveries = new ArrayList<>();
+
+ deliveryParticipants = RcsControllerCall.call(
+ iRcs -> iRcs.getMessageRecipients(mId));
+
+ if (deliveryParticipants != null) {
+ for (Integer deliveryParticipant : deliveryParticipants) {
+ messageDeliveries.add(new RcsOutgoingMessageDelivery(deliveryParticipant, mId));
+ }
+ }
+
+ return messageDeliveries;
}
+ /**
+ * @return Returns {@code false} as this is not an incoming message.
+ */
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public boolean isIncoming() {
+ return false;
}
}
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
new file mode 100644
index 0000000..b93f892
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+/**
+ * This class holds the delivery information of an {@link RcsOutgoingMessage} for each
+ * {@link RcsParticipant} that the message was intended for.
+ *
+ * @hide - TODO(109759350) make this public
+ */
+public class RcsOutgoingMessageDelivery {
+ // The participant that this delivery is intended for
+ private final int mRecipientId;
+ // The message this delivery is associated with
+ private final int mRcsOutgoingMessageId;
+
+ /**
+ * Constructor to be used with RcsOutgoingMessage.getDelivery()
+ *
+ * @hide
+ */
+ RcsOutgoingMessageDelivery(int recipientId, int messageId) {
+ mRecipientId = recipientId;
+ mRcsOutgoingMessageId = messageId;
+ }
+
+ /**
+ * Sets the delivery time of this outgoing delivery and persists into storage.
+ *
+ * @param deliveredTimestamp The timestamp to set to delivery. It is defined as milliseconds
+ * passed after midnight, January 1, 1970 UTC
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setDeliveredTimestamp(long deliveredTimestamp) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliveryDeliveredTimestamp(
+ mRcsOutgoingMessageId, mRecipientId, deliveredTimestamp));
+ }
+
+ /**
+ * @return Returns the delivered timestamp of the associated message to the associated
+ * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC.
+ * Returns 0 if the {@link RcsOutgoingMessage} is not delivered yet.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getDeliveredTimestamp() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getOutgoingDeliveryDeliveredTimestamp(
+ mRcsOutgoingMessageId, mRecipientId));
+ }
+
+ /**
+ * Sets the seen time of this outgoing delivery and persists into storage.
+ *
+ * @param seenTimestamp The timestamp to set to delivery. It is defined as milliseconds
+ * passed after midnight, January 1, 1970 UTC
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setSeenTimestamp(long seenTimestamp) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliverySeenTimestamp(
+ mRcsOutgoingMessageId, mRecipientId, seenTimestamp));
+ }
+
+ /**
+ * @return Returns the seen timestamp of the associated message by the associated
+ * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC.
+ * Returns 0 if the {@link RcsOutgoingMessage} is not seen yet.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getSeenTimestamp() throws RcsMessageStoreException {
+ return RcsControllerCall.call(
+ iRcs -> iRcs.getOutgoingDeliverySeenTimestamp(mRcsOutgoingMessageId, mRecipientId));
+ }
+
+ /**
+ * Sets the status of this outgoing delivery and persists into storage.
+ *
+ * @param status The status of the associated {@link RcsMessage}s delivery to the associated
+ * {@link RcsParticipant}
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setStatus(@RcsMessage.RcsMessageStatus int status) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliveryStatus(
+ mRcsOutgoingMessageId, mRecipientId, status));
+ }
+
+ /**
+ * @return Returns the status of this outgoing delivery.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public @RcsMessage.RcsMessageStatus int getStatus() throws RcsMessageStoreException {
+ return RcsControllerCall.call(
+ iRcs -> iRcs.getOutgoingDeliveryStatus(mRcsOutgoingMessageId, mRecipientId));
+ }
+
+ /**
+ * @return Returns the recipient associated with this delivery.
+ */
+ @NonNull
+ public RcsParticipant getRecipient() {
+ return new RcsParticipant(mRecipientId);
+ }
+
+ /**
+ * @return Returns the {@link RcsOutgoingMessage} associated with this delivery.
+ */
+ @NonNull
+ public RcsOutgoingMessage getMessage() {
+ return new RcsOutgoingMessage(mRcsOutgoingMessageId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.aidl b/telephony/java/android/telephony/ims/RcsParticipant.aidl
deleted file mode 100644
index 1c44363..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipant.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsParticipant;
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
index f678ec7..ce0ad2c 100644
--- a/telephony/java/android/telephony/ims/RcsParticipant.java
+++ b/telephony/java/android/telephony/ims/RcsParticipant.java
@@ -15,33 +15,17 @@
*/
package android.telephony.ims;
-import static android.telephony.ims.RcsMessageStore.TAG;
-
-import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.WorkerThread;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.Rlog;
-import android.telephony.ims.aidl.IRcs;
-import android.text.TextUtils;
-
-import com.android.internal.util.Preconditions;
/**
* RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
- * @hide - TODO(sahinc) make this public
+ *
+ * @hide - TODO(109759350) make this public
*/
-public class RcsParticipant implements Parcelable {
+public class RcsParticipant {
// The row ID of this participant in the database
private int mId;
- // The phone number of this participant
- private String mCanonicalAddress;
- // The RCS alias of this participant. This is different than the name of the contact in the
- // Contacts app - i.e. RCS protocol allows users to define aliases for themselves that doesn't
- // require other users to add them as contacts and give them a name.
- private String mAlias;
/**
* Constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
@@ -49,68 +33,87 @@
*
* @hide
*/
- public RcsParticipant(int id, @NonNull String canonicalAddress) {
+ public RcsParticipant(int id) {
mId = id;
- mCanonicalAddress = canonicalAddress;
}
/**
- * @return Returns the canonical address (i.e. normalized phone number) for this participant
+ * @return Returns the canonical address (i.e. normalized phone number) for this
+ * {@link RcsParticipant}
+ * @throws RcsMessageStoreException if the value could not be read from the storage
*/
- public String getCanonicalAddress() {
- return mCanonicalAddress;
+ @Nullable
+ @WorkerThread
+ public String getCanonicalAddress() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantCanonicalAddress(mId));
}
/**
- * Sets the canonical address for this participant and updates it in storage.
- * @param canonicalAddress the canonical address to update to.
+ * @return Returns the alias for this {@link RcsParticipant}. Alias is usually the real name of
+ * the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal Profile Service
+ * Definition Document)
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ @WorkerThread
+ public String getAlias() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantAlias(mId));
+ }
+
+ /**
+ * Sets the alias for this {@link RcsParticipant} and persists it in storage. Alias is usually
+ * the real name of the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal
+ * Profile Service Definition Document)
+ *
+ * @param alias The alias to set to.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
*/
@WorkerThread
- public void setCanonicalAddress(@NonNull String canonicalAddress) {
- Preconditions.checkNotNull(canonicalAddress);
- if (canonicalAddress.equals(mCanonicalAddress)) {
- return;
- }
-
- mCanonicalAddress = canonicalAddress;
-
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- iRcs.updateRcsParticipantCanonicalAddress(mId, mCanonicalAddress);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re);
- }
+ public void setAlias(String alias) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setRcsParticipantAlias(mId, alias));
}
/**
- * @return Returns the alias for this participant. Alias is usually the real name of the person
- * themselves.
+ * @return Returns the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for
+ * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device
+ * API 1.6 Specification)
+ * @throws RcsMessageStoreException if the value could not be read from the storage
*/
- public String getAlias() {
- return mAlias;
+ @Nullable
+ @WorkerThread
+ public String getContactId() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantContactId(mId));
}
/**
- * Sets the alias for this participant and persists it in storage. Alias is usually the real
- * name of the person themselves.
+ * Sets the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for
+ * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device
+ * API 1.6 Specification)
+ *
+ * @param contactId The contact ID to set to.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
*/
@WorkerThread
- public void setAlias(String alias) {
- if (TextUtils.equals(mAlias, alias)) {
- return;
- }
- mAlias = alias;
+ public void setContactId(String contactId) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setRcsParticipantContactId(mId, contactId));
+ }
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- iRcs.updateRcsParticipantAlias(mId, mAlias);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re);
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
}
+ if (!(obj instanceof RcsParticipant)) {
+ return false;
+ }
+ RcsParticipant other = (RcsParticipant) obj;
+
+ return mId == other.mId;
+ }
+
+ @Override
+ public int hashCode() {
+ return mId;
}
/**
@@ -121,34 +124,4 @@
public int getId() {
return mId;
}
-
- public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() {
- @Override
- public RcsParticipant createFromParcel(Parcel in) {
- return new RcsParticipant(in);
- }
-
- @Override
- public RcsParticipant[] newArray(int size) {
- return new RcsParticipant[size];
- }
- };
-
- protected RcsParticipant(Parcel in) {
- mId = in.readInt();
- mCanonicalAddress = in.readString();
- mAlias = in.readString();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mId);
- dest.writeString(mCanonicalAddress);
- dest.writeString(mAlias);
- }
}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
index b9ca5a8..04cdf86 100644
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
@@ -15,27 +15,93 @@
*/
package android.telephony.ims;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
/**
- * An event that indicates an {@link RcsParticipant}'s alias was changed.
- * @hide - TODO(sahinc) make this public
+ * An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA
+ * RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide - TODO(109759350) make this public
*/
-public class RcsParticipantAliasChangedEvent extends RcsParticipantEvent {
+public class RcsParticipantAliasChangedEvent extends RcsEvent {
+ // The ID of the participant that changed their alias
+ private int mParticipantId;
+ // The new alias of the above participant
+ private String mNewAlias;
+
+ /**
+ * Creates a new {@link RcsParticipantAliasChangedEvent}. This event is not persisted into
+ * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+ *
+ * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+ * midnight, January 1st, 1970 UTC
+ * @param participant The {@link RcsParticipant} that got their alias changed
+ * @param newAlias The new alias the {@link RcsParticipant} has.
+ * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+ */
+ public RcsParticipantAliasChangedEvent(long timestamp, @NonNull RcsParticipant participant,
+ @Nullable String newAlias) {
+ super(timestamp);
+ mParticipantId = participant.getId();
+ mNewAlias = newAlias;
+ }
+
+ /**
+ * @hide - internal constructor for queries
+ */
+ public RcsParticipantAliasChangedEvent(long timestamp, int participantId,
+ @Nullable String newAlias) {
+ super(timestamp);
+ mParticipantId = participantId;
+ mNewAlias = newAlias;
+ }
+
+ /**
+ * @return Returns the {@link RcsParticipant} whose alias was changed.
+ */
+ @NonNull
+ public RcsParticipant getParticipantId() {
+ return new RcsParticipant(mParticipantId);
+ }
+
+ /**
+ * @return Returns the alias of the associated {@link RcsParticipant} after this event happened
+ */
+ @Nullable
+ public String getNewAlias() {
+ return mNewAlias;
+ }
+
+ /**
+ * Persists the event to the data store.
+ *
+ * @hide - not meant for public use.
+ */
+ @Override
+ public void persist() throws RcsMessageStoreException {
+ RcsControllerCall.call(iRcs -> iRcs.createParticipantAliasChangedEvent(
+ getTimestamp(), getParticipantId().getId(), getNewAlias()));
+ }
+
public static final Creator<RcsParticipantAliasChangedEvent> CREATOR =
new Creator<RcsParticipantAliasChangedEvent>() {
- @Override
- public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) {
- return new RcsParticipantAliasChangedEvent(in);
- }
+ @Override
+ public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) {
+ return new RcsParticipantAliasChangedEvent(in);
+ }
- @Override
- public RcsParticipantAliasChangedEvent[] newArray(int size) {
- return new RcsParticipantAliasChangedEvent[size];
- }
- };
+ @Override
+ public RcsParticipantAliasChangedEvent[] newArray(int size) {
+ return new RcsParticipantAliasChangedEvent[size];
+ }
+ };
protected RcsParticipantAliasChangedEvent(Parcel in) {
+ super(in);
+ mNewAlias = in.readString();
+ mParticipantId = in.readInt();
}
@Override
@@ -45,5 +111,8 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mNewAlias);
+ dest.writeInt(mParticipantId);
}
}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl b/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
deleted file mode 100644
index c0a7789..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsParticipantEvent;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.java b/telephony/java/android/telephony/ims/RcsParticipantEvent.java
deleted file mode 100644
index 371b8b7..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantEvent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcelable;
-
-/**
- * An event that is associated with an {@link RcsParticipant}
- * @hide - TODO(sahinc) make this public
- */
-public abstract class RcsParticipantEvent implements Parcelable {
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryParameters.aidl
similarity index 93%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsParticipantQueryParameters.aidl
index 82d985d..ea8288c 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParameters.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsParticipantQueryParameters;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParameters.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParameters.java
new file mode 100644
index 0000000..3611fc1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParameters.java
@@ -0,0 +1,310 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+
+/**
+ * The parameters to pass into
+ * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParameters)} in order to select a
+ * subset of {@link RcsThread}s present in the message store.
+ *
+ * @hide TODO - make the Builder and builder() public. The rest should stay internal only.
+ */
+public class RcsParticipantQueryParameters implements Parcelable {
+ /**
+ * Flag to set with {@link Builder#setSortProperty(int)} to sort the results in the order of
+ * creation time for faster query results
+ */
+ public static final int SORT_BY_CREATION_ORDER = 0;
+
+ /**
+ * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the
+ * {@link RcsParticipant} aliases
+ */
+ public static final int SORT_BY_ALIAS = 1;
+
+ /**
+ * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the
+ * {@link RcsParticipant} canonical addresses
+ */
+ public static final int SORT_BY_CANONICAL_ADDRESS = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_ALIAS, SORT_BY_CANONICAL_ADDRESS})
+ public @interface SortingProperty {
+ }
+
+ // The SQL "like" statement to filter against participant aliases
+ private String mAliasLike;
+ // The SQL "like" statement to filter against canonical addresses
+ private String mCanonicalAddressLike;
+ // The property to sort the result against
+ private @SortingProperty int mSortingProperty;
+ // Whether to sort the result in ascending order
+ private boolean mIsAscending;
+ // The number of results to be returned from the query
+ private int mLimit;
+ // Used to limit the results to participants of a single thread
+ private int mThreadId;
+
+ /**
+ * @hide
+ */
+ public static final String PARTICIPANT_QUERY_PARAMETERS_KEY = "participant_query_parameters";
+
+ RcsParticipantQueryParameters(int rcsThreadId, String aliasLike, String canonicalAddressLike,
+ @SortingProperty int sortingProperty, boolean isAscending,
+ int limit) {
+ mThreadId = rcsThreadId;
+ mAliasLike = aliasLike;
+ mCanonicalAddressLike = canonicalAddressLike;
+ mSortingProperty = sortingProperty;
+ mIsAscending = isAscending;
+ mLimit = limit;
+ }
+
+ /**
+ * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+ * the thread that the result query should be limited to.
+ *
+ * As we do not expose any sort of integer ID's to public usage, this should be hidden.
+ *
+ * @hide - not meant for public use
+ */
+ public int getThreadId() {
+ return mThreadId;
+ }
+
+ /**
+ * @return Returns the SQL-inspired "LIKE" clause that will be used to match
+ * {@link RcsParticipant}s with respect to their aliases
+ *
+ * @see RcsParticipant#getAlias()
+ */
+ public String getAliasLike() {
+ return mAliasLike;
+ }
+
+ /**
+ * @return Returns the SQL-inspired "LIKE" clause that will be used to match
+ * {@link RcsParticipant}s with respect to their canonical addresses.
+ *
+ * @see RcsParticipant#getCanonicalAddress()
+ */
+ public String getCanonicalAddressLike() {
+ return mCanonicalAddressLike;
+ }
+
+ /**
+ * @return Returns the number of {@link RcsParticipant}s to be returned from the query. A value
+ * of 0 means there is no set limit.
+ */
+ public int getLimit() {
+ return mLimit;
+ }
+
+ /**
+ * @return Returns the property that will be used to sort the result against.
+ * @see SortingProperty
+ */
+ public int getSortingProperty() {
+ return mSortingProperty;
+ }
+
+ /**
+ * @return Returns {@code true} if the result set will be sorted in ascending order,
+ * {@code false} if it will be sorted in descending order.
+ */
+ public boolean getSortDirection() {
+ return mIsAscending;
+ }
+
+ /**
+ * A helper class to build the {@link RcsParticipantQueryParameters}.
+ */
+ public static class Builder {
+ private String mAliasLike;
+ private String mCanonicalAddressLike;
+ private @SortingProperty int mSortingProperty;
+ private boolean mIsAscending;
+ private int mLimit = 100;
+ private int mThreadId;
+
+ /**
+ * Creates a new builder for {@link RcsParticipantQueryParameters} to be used in
+ * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParameters)}
+ */
+ public Builder() {
+ // empty implementation
+ }
+
+ /**
+ * Limits the resulting {@link RcsParticipant}s to only the given {@link RcsThread}
+ *
+ * @param rcsThread The thread that the participants should be searched in.
+ * @return The same {@link Builder} to chain methods.
+ */
+ @CheckResult
+ public Builder setThread(RcsThread rcsThread) {
+ mThreadId = rcsThread.getThreadId();
+ return this;
+ }
+
+ /**
+ * Sets an SQL-inspired "like" clause to match with participant aliases. Using a percent
+ * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore
+ * ('_') wildcard matches any single character. Not using any wildcards would only perform a
+ * string match.The input string is case-insensitive.
+ *
+ * The input "An%e" would match {@link RcsParticipant}s with names Anne, Annie, Antonie,
+ * while the input "An_e" would only match Anne.
+ *
+ * @param likeClause The like clause to use for matching {@link RcsParticipant} aliases.
+ * @return The same {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setAliasLike(String likeClause) {
+ mAliasLike = likeClause;
+ return this;
+ }
+
+ /**
+ * Sets an SQL-inspired "like" clause to match with participant addresses. Using a percent
+ * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore
+ * ('_') wildcard matches any single character. Not using any wildcards would only perform a
+ * string match. The input string is case-insensitive.
+ *
+ * The input "+999%111" would match {@link RcsParticipant}s with addresses like "+9995111"
+ * or "+99955555111", while the input "+999_111" would only match "+9995111".
+ *
+ * @param likeClause The like clause to use for matching {@link RcsParticipant} canonical
+ * addresses.
+ * @return The same {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setCanonicalAddressLike(String likeClause) {
+ mCanonicalAddressLike = likeClause;
+ return this;
+ }
+
+ /**
+ * Desired number of threads to be returned from the query. Passing in 0 will return all
+ * existing threads at once. The limit defaults to 100.
+ *
+ * @param limit The number to limit the query result to.
+ * @return The same instance of the builder to chain parameters.
+ * @throws InvalidParameterException If the given limit is negative.
+ */
+ @CheckResult
+ public Builder setResultLimit(@IntRange(from = 0) int limit)
+ throws InvalidParameterException {
+ if (limit < 0) {
+ throw new InvalidParameterException("The query limit must be non-negative");
+ }
+
+ mLimit = limit;
+ return this;
+ }
+
+ /**
+ * Sets the property where the results should be sorted against. Defaults to
+ * {@link RcsParticipantQueryParameters.SortingProperty#SORT_BY_CREATION_ORDER}
+ *
+ * @param sortingProperty against which property the results should be sorted
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortProperty(@SortingProperty int sortingProperty) {
+ mSortingProperty = sortingProperty;
+ return this;
+ }
+
+ /**
+ * Sets whether the results should be sorted ascending or descending
+ *
+ * @param isAscending whether the results should be sorted ascending
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortDirection(boolean isAscending) {
+ mIsAscending = isAscending;
+ return this;
+ }
+
+ /**
+ * Builds the {@link RcsParticipantQueryParameters} to use in
+ * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParameters)}
+ *
+ * @return An instance of {@link RcsParticipantQueryParameters} to use with the participant
+ * query.
+ */
+ public RcsParticipantQueryParameters build() {
+ return new RcsParticipantQueryParameters(mThreadId, mAliasLike, mCanonicalAddressLike,
+ mSortingProperty, mIsAscending, mLimit);
+ }
+ }
+
+ /**
+ * Parcelable boilerplate below.
+ */
+ protected RcsParticipantQueryParameters(Parcel in) {
+ mAliasLike = in.readString();
+ mCanonicalAddressLike = in.readString();
+ mSortingProperty = in.readInt();
+ mIsAscending = in.readByte() == 1;
+ mLimit = in.readInt();
+ mThreadId = in.readInt();
+ }
+
+ public static final Creator<RcsParticipantQueryParameters> CREATOR =
+ new Creator<RcsParticipantQueryParameters>() {
+ @Override
+ public RcsParticipantQueryParameters createFromParcel(Parcel in) {
+ return new RcsParticipantQueryParameters(in);
+ }
+
+ @Override
+ public RcsParticipantQueryParameters[] newArray(int size) {
+ return new RcsParticipantQueryParameters[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mAliasLike);
+ dest.writeString(mCanonicalAddressLike);
+ dest.writeInt(mSortingProperty);
+ dest.writeByte((byte) (mIsAscending ? 1 : 0));
+ dest.writeInt(mLimit);
+ dest.writeInt(mThreadId);
+ }
+
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl
index 82d985d..db5c00c 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsParticipantQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
new file mode 100644
index 0000000..2f4ab46
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParameters)}
+ * call. This class allows getting the token for querying the next batch of participants in order to
+ * prevent handling large amounts of data at once.
+ *
+ * @hide
+ */
+public class RcsParticipantQueryResult implements Parcelable {
+ // A token for the caller to continue their query for the next batch of results
+ private RcsQueryContinuationToken mContinuationToken;
+ // The list of participant IDs returned with this query
+ private List<Integer> mParticipants;
+
+ /**
+ * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+ * to create query results
+ *
+ * @hide
+ */
+ public RcsParticipantQueryResult(
+ RcsQueryContinuationToken continuationToken,
+ List<Integer> participants) {
+ mContinuationToken = continuationToken;
+ mParticipants = participants;
+ }
+
+ /**
+ * Returns a token to call
+ * {@link RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)}
+ * to get the next batch of {@link RcsParticipant}s.
+ */
+ @Nullable
+ public RcsQueryContinuationToken getContinuationToken() {
+ return mContinuationToken;
+ }
+
+ /**
+ * Returns all the {@link RcsParticipant}s in the current query result. Call {@link
+ * RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)} to get the next
+ * batch of {@link RcsParticipant}s.
+ */
+ @NonNull
+ public List<RcsParticipant> getParticipants() {
+ List<RcsParticipant> participantList = new ArrayList<>();
+ for (Integer participantId : mParticipants) {
+ participantList.add(new RcsParticipant(participantId));
+ }
+
+ return participantList;
+ }
+
+ protected RcsParticipantQueryResult(Parcel in) {
+ mContinuationToken = in.readParcelable(
+ RcsQueryContinuationToken.class.getClassLoader());
+ }
+
+ public static final Creator<RcsParticipantQueryResult> CREATOR =
+ new Creator<RcsParticipantQueryResult>() {
+ @Override
+ public RcsParticipantQueryResult createFromParcel(Parcel in) {
+ return new RcsParticipantQueryResult(in);
+ }
+
+ @Override
+ public RcsParticipantQueryResult[] newArray(int size) {
+ return new RcsParticipantQueryResult[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mContinuationToken, flags);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
index 82d985d..319379a 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
new file mode 100644
index 0000000..e880651
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
@@ -0,0 +1,151 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This interface allows using the same implementation for continuation token usage in
+ * {@link com.android.providers.telephony.RcsProvider}
+ * @hide - TODO make getQueryType() and types public - the rest should stay internal
+ */
+public class RcsQueryContinuationToken implements Parcelable {
+ /**
+ * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+ * {@link RcsEvent} queries
+ */
+ public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0;
+
+ /**
+ * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+ * {@link RcsMessage} queries
+ */
+ public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1;
+
+ /**
+ * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+ * {@link RcsParticipant} queries
+ */
+ public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2;
+
+ /**
+ * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+ * {@link RcsThread} queries
+ */
+ public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3;
+
+ /**
+ * @hide - not meant for public use
+ */
+ public static final String QUERY_CONTINUATION_TOKEN = "query_continuation_token";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({EVENT_QUERY_CONTINUATION_TOKEN_TYPE, MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE,
+ PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, THREAD_QUERY_CONTINUATION_TOKEN_TYPE})
+ public @interface ContinuationTokenType {}
+
+ // The type of query this token should allow to continue
+ private @ContinuationTokenType int mQueryType;
+ // The raw query string for the initial query
+ private final String mRawQuery;
+ // The number of results that is returned with each query
+ private final int mLimit;
+ // The offset value that this query should start the query from
+ private int mOffset;
+
+ /**
+ * @hide
+ */
+ public RcsQueryContinuationToken(@ContinuationTokenType int queryType, String rawQuery,
+ int limit, int offset) {
+ mQueryType = queryType;
+ mRawQuery = rawQuery;
+ mLimit = limit;
+ mOffset = offset;
+ }
+
+ /**
+ * Returns the original raw query used on {@link com.android.providers.telephony.RcsProvider}
+ * @hide
+ */
+ public String getRawQuery() {
+ return mRawQuery;
+ }
+
+ /**
+ * Returns which index this continuation query should start from
+ * @hide
+ */
+ public int getOffset() {
+ return mOffset;
+ }
+
+ /**
+ * Increments the offset by the amount of result rows returned with the continuation query for
+ * the next query.
+ * @hide
+ */
+ public void incrementOffset() {
+ mOffset += mLimit;
+ }
+
+ /**
+ * Returns the type of query that this {@link RcsQueryContinuationToken} is intended to be used
+ * to continue.
+ */
+ public @ContinuationTokenType int getQueryType() {
+ return mQueryType;
+ }
+
+ protected RcsQueryContinuationToken(Parcel in) {
+ mQueryType = in.readInt();
+ mRawQuery = in.readString();
+ mLimit = in.readInt();
+ mOffset = in.readInt();
+ }
+
+ public static final Creator<RcsQueryContinuationToken> CREATOR =
+ new Creator<RcsQueryContinuationToken>() {
+ @Override
+ public RcsQueryContinuationToken createFromParcel(Parcel in) {
+ return new RcsQueryContinuationToken(in);
+ }
+
+ @Override
+ public RcsQueryContinuationToken[] newArray(int size) {
+ return new RcsQueryContinuationToken[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mQueryType);
+ dest.writeString(mRawQuery);
+ dest.writeInt(mLimit);
+ dest.writeInt(mOffset);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsTextPart.aidl b/telephony/java/android/telephony/ims/RcsTextPart.aidl
deleted file mode 100644
index 4f9fe1f..0000000
--- a/telephony/java/android/telephony/ims/RcsTextPart.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsTextPart;
diff --git a/telephony/java/android/telephony/ims/RcsTextPart.java b/telephony/java/android/telephony/ims/RcsTextPart.java
deleted file mode 100644
index 2a72df1..0000000
--- a/telephony/java/android/telephony/ims/RcsTextPart.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a string
- * @hide - TODO(sahinc) make this public
- */
-public class RcsTextPart extends RcsPart {
- public static final Creator<RcsTextPart> CREATOR = new Creator<RcsTextPart>() {
- @Override
- public RcsTextPart createFromParcel(Parcel in) {
- return new RcsTextPart(in);
- }
-
- @Override
- public RcsTextPart[] newArray(int size) {
- return new RcsTextPart[size];
- }
- };
-
- protected RcsTextPart(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThread.aidl b/telephony/java/android/telephony/ims/RcsThread.aidl
deleted file mode 100644
index d9cf6db..0000000
--- a/telephony/java/android/telephony/ims/RcsThread.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony;
-
-parcelable RcsThread;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
index c0a0d94..238f5e7 100644
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -16,60 +16,117 @@
package android.telephony.ims;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_GROUP;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+import com.android.internal.annotations.VisibleForTesting;
/**
* RcsThread represents a single RCS conversation thread. It holds messages that were sent and
* received and events that occurred on that thread.
- * @hide - TODO(sahinc) make this public
+ *
+ * @hide - TODO(109759350) make this public
*/
-public abstract class RcsThread implements Parcelable {
- // Since this is an abstract class that gets parcelled, the sub-classes need to write these
- // magic values into the parcel so that we know which type to unparcel into.
- protected static final int RCS_1_TO_1_TYPE = 998;
- protected static final int RCS_GROUP_TYPE = 999;
-
+public abstract class RcsThread {
+ // The rcs_participant_thread_id that represents this thread in the database
protected int mThreadId;
+ /**
+ * @hide
+ */
protected RcsThread(int threadId) {
mThreadId = threadId;
}
- protected RcsThread(Parcel in) {
- mThreadId = in.readInt();
+ /**
+ * @return Returns the summary of the latest message in this {@link RcsThread} packaged in an
+ * {@link RcsMessageSnippet} object
+ */
+ @WorkerThread
+ @NonNull
+ public RcsMessageSnippet getSnippet() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessageSnippet(mThreadId));
}
- public static final Creator<RcsThread> CREATOR = new Creator<RcsThread>() {
- @Override
- public RcsThread createFromParcel(Parcel in) {
- int type = in.readInt();
-
- switch (type) {
- case RCS_1_TO_1_TYPE:
- return new Rcs1To1Thread(in);
- case RCS_GROUP_TYPE:
- return new RcsGroupThread(in);
- default:
- Log.e(RcsMessageStore.TAG, "Cannot unparcel RcsThread, wrong type: " + type);
- }
- return null;
- }
-
- @Override
- public RcsThread[] newArray(int size) {
- return new RcsThread[0];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
+ /**
+ * Adds a new {@link RcsIncomingMessage} to this RcsThread and persists it in storage.
+ *
+ * @throws RcsMessageStoreException if the message could not be persisted into storage.
+ */
+ @WorkerThread
+ @NonNull
+ public RcsIncomingMessage addIncomingMessage(
+ @NonNull RcsIncomingMessageCreationParameters rcsIncomingMessageCreationParameters)
+ throws RcsMessageStoreException {
+ return new RcsIncomingMessage(RcsControllerCall.call(iRcs -> iRcs.addIncomingMessage(
+ mThreadId, rcsIncomingMessageCreationParameters)));
}
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mThreadId);
+ /**
+ * Adds a new {@link RcsOutgoingMessage} to this RcsThread and persists it in storage.
+ *
+ * @throws RcsMessageStoreException if the message could not be persisted into storage.
+ */
+ @WorkerThread
+ @NonNull
+ public RcsOutgoingMessage addOutgoingMessage(
+ @NonNull RcsMessageCreationParameters rcsMessageCreationParameters)
+ throws RcsMessageStoreException {
+ int messageId = RcsControllerCall.call(iRcs -> iRcs.addOutgoingMessage(
+ mThreadId, rcsMessageCreationParameters));
+
+ return new RcsOutgoingMessage(messageId);
+ }
+
+ /**
+ * Deletes an {@link RcsMessage} from this RcsThread and updates the storage.
+ *
+ * @param rcsMessage The message to delete from the thread
+ * @throws RcsMessageStoreException if the message could not be deleted
+ */
+ @WorkerThread
+ public void deleteMessage(@NonNull RcsMessage rcsMessage) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.deleteMessage(rcsMessage.getId(), rcsMessage.isIncoming(), mThreadId,
+ isGroup()));
+ }
+
+ /**
+ * Convenience function for loading all the {@link RcsMessage}s in this {@link RcsThread}. For
+ * a more detailed and paginated query, please use
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParameters)}
+ *
+ * @return Loads the {@link RcsMessage}s in this thread and returns them in an immutable list.
+ * @throws RcsMessageStoreException if the messages could not be read from the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsMessageQueryResult getMessages() throws RcsMessageStoreException {
+ RcsMessageQueryParameters queryParameters =
+ new RcsMessageQueryParameters.Builder().setThread(this).build();
+ return RcsControllerCall.call(iRcs -> iRcs.getMessages(queryParameters));
+ }
+
+ /**
+ * @return Returns whether this is a group thread or not
+ */
+ public abstract boolean isGroup();
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public int getThreadId() {
+ return mThreadId;
+ }
+
+ /**
+ * @hide
+ */
+ public int getThreadType() {
+ return isGroup() ? THREAD_TYPE_GROUP : THREAD_TYPE_1_TO_1;
}
}
diff --git a/telephony/java/android/telephony/ims/RcsThreadEvent.java b/telephony/java/android/telephony/ims/RcsThreadEvent.java
deleted file mode 100644
index e10baab..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadEvent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcelable;
-
-/**
- * An event that happened on an {@link RcsThread}.
- * @hide - TODO(sahinc) make this public
- */
-public abstract class RcsThreadEvent implements Parcelable {
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java
deleted file mode 100644
index b308fef..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an {@link RcsGroupThread}'s icon was changed.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadIconChangedEvent extends RcsThreadEvent {
- public static final Creator<RcsThreadIconChangedEvent> CREATOR =
- new Creator<RcsThreadIconChangedEvent>() {
- @Override
- public RcsThreadIconChangedEvent createFromParcel(Parcel in) {
- return new RcsThreadIconChangedEvent(in);
- }
-
- @Override
- public RcsThreadIconChangedEvent[] newArray(int size) {
- return new RcsThreadIconChangedEvent[size];
- }
- };
-
- protected RcsThreadIconChangedEvent(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
deleted file mode 100644
index 54a311d..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsThreadNameChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java
deleted file mode 100644
index 6f5cfdf..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an {@link RcsGroupThread}'s name was changed.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadNameChangedEvent extends RcsThreadEvent {
- public static final Creator<RcsThreadNameChangedEvent> CREATOR =
- new Creator<RcsThreadNameChangedEvent>() {
- @Override
- public RcsThreadNameChangedEvent createFromParcel(Parcel in) {
- return new RcsThreadNameChangedEvent(in);
- }
-
- @Override
- public RcsThreadNameChangedEvent[] newArray(int size) {
- return new RcsThreadNameChangedEvent[size];
- }
- };
-
- protected RcsThreadNameChangedEvent(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
deleted file mode 100644
index 047a424..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsThreadParticipantJoinedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java
deleted file mode 100644
index 5c4073c..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an RCS participant has joined an {@link RcsGroupThread}.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadParticipantJoinedEvent extends RcsThreadEvent {
- public static final Creator<RcsThreadParticipantJoinedEvent> CREATOR =
- new Creator<RcsThreadParticipantJoinedEvent>() {
- @Override
- public RcsThreadParticipantJoinedEvent createFromParcel(Parcel in) {
- return new RcsThreadParticipantJoinedEvent(in);
- }
-
- @Override
- public RcsThreadParticipantJoinedEvent[] newArray(int size) {
- return new RcsThreadParticipantJoinedEvent[size];
- }
- };
-
- protected RcsThreadParticipantJoinedEvent(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
deleted file mode 100644
index 52f9bbd..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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.telephony.ims;
-
-parcelable RcsThreadParticipantLeftEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java
deleted file mode 100644
index 4bf86b9..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an RCS participant has left an {@link RcsGroupThread}.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadParticipantLeftEvent extends RcsThreadEvent {
- public static final Creator<RcsThreadParticipantLeftEvent> CREATOR =
- new Creator<RcsThreadParticipantLeftEvent>() {
- @Override
- public RcsThreadParticipantLeftEvent createFromParcel(Parcel in) {
- return new RcsThreadParticipantLeftEvent(in);
- }
-
- @Override
- public RcsThreadParticipantLeftEvent[] newArray(int size) {
- return new RcsThreadParticipantLeftEvent[size];
- }
- };
-
- protected RcsThreadParticipantLeftEvent(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
deleted file mode 100644
index 7bcebfa..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.telephony.ims;
-
-parcelable RcsThreadQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
deleted file mode 100644
index 931e93d..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A continuation token to provide for {@link RcsMessageStore#getRcsThreads}. Use this token to
- * break large queries into manageable chunks
- * @hide - TODO make this public
- */
-public class RcsThreadQueryContinuationToken implements Parcelable {
- protected RcsThreadQueryContinuationToken(Parcel in) {
- }
-
- public static final Creator<RcsThreadQueryContinuationToken> CREATOR =
- new Creator<RcsThreadQueryContinuationToken>() {
- @Override
- public RcsThreadQueryContinuationToken createFromParcel(Parcel in) {
- return new RcsThreadQueryContinuationToken(in);
- }
-
- @Override
- public RcsThreadQueryContinuationToken[] newArray(int size) {
- return new RcsThreadQueryContinuationToken[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
index feb2d4d..52e73ce 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
@@ -1,19 +1,19 @@
/*
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ *
+ * Copyright 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.telephony.ims;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
index f2c4ab1..4aa4207 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -17,72 +17,133 @@
package android.telephony.ims;
import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.InvalidParameterException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
* The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} in
* order to select a subset of {@link RcsThread}s present in the message store.
+ *
* @hide TODO - make the Builder and builder() public. The rest should stay internal only.
*/
public class RcsThreadQueryParameters implements Parcelable {
- private final boolean mIsGroup;
- private final Set<RcsParticipant> mRcsParticipants;
+ /**
+ * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make
+ * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} return
+ * {@link RcsGroupThread}s.
+ */
+ public static final int THREAD_TYPE_GROUP = 0x0001;
+
+ /**
+ * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make
+ * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} return
+ * {@link Rcs1To1Thread}s.
+ */
+ public static final int THREAD_TYPE_1_TO_1 = 0x0002;
+
+ // The type of threads to be filtered with the query
+ private final int mThreadType;
+ // The list of participants that are expected in the resulting threads
+ private final List<Integer> mRcsParticipantIds;
+ // The number of RcsThread's that should be returned with this query
private final int mLimit;
+ // The property which the result of the query should be sorted against
+ private final @SortingProperty int mSortingProperty;
+ // Whether the sorting should be done in ascending
private final boolean mIsAscending;
- RcsThreadQueryParameters(boolean isGroup, Set<RcsParticipant> participants, int limit,
- boolean isAscending) {
- mIsGroup = isGroup;
- mRcsParticipants = participants;
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+ * be sorted in the order of {@link RcsThread} creation time for faster results.
+ */
+ public static final int SORT_BY_CREATION_ORDER = 0;
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+ * be sorted according to the timestamp of {@link RcsThread#getSnippet()}
+ */
+ public static final int SORT_BY_TIMESTAMP = 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
+ public @interface SortingProperty {
+ }
+
+ /**
+ * @hide
+ */
+ public static final String THREAD_QUERY_PARAMETERS_KEY = "thread_query_parameters";
+
+ RcsThreadQueryParameters(int threadType, Set<RcsParticipant> participants,
+ int limit, int sortingProperty, boolean isAscending) {
+ mThreadType = threadType;
+ mRcsParticipantIds = convertParticipantSetToIdList(participants);
mLimit = limit;
+ mSortingProperty = sortingProperty;
mIsAscending = isAscending;
}
- /**
- * Returns a new builder to build a query with.
- * TODO - make public
- */
- public static Builder builder() {
- return new Builder();
+ private static List<Integer> convertParticipantSetToIdList(Set<RcsParticipant> participants) {
+ List<Integer> ids = new ArrayList<>(participants.size());
+ for (RcsParticipant participant : participants) {
+ ids.add(participant.getId());
+ }
+ return ids;
}
/**
* This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
- * the list of participants.
- * @hide
+ * the list of participant IDs.
+ *
+ * As we don't expose any integer ID's to API users, this should stay hidden
+ *
+ * @hide - not meant for public use
*/
- public Set<RcsParticipant> getRcsParticipants() {
- return mRcsParticipants;
+ public List<Integer> getRcsParticipantsIds() {
+ return Collections.unmodifiableList(mRcsParticipantIds);
}
/**
- * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
- * whether group threads should be queried
- * @hide
+ * @return Returns the bitmask flag for types of {@link RcsThread}s that this query should
+ * return.
*/
- public boolean isGroupThread() {
- return mIsGroup;
+ public int getThreadType() {
+ return mThreadType;
}
/**
- * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
- * the number of tuples the result query should be limited to.
+ * @return Returns the number of {@link RcsThread}s to be returned from the query. A value
+ * of 0 means there is no set limit.
*/
public int getLimit() {
return mLimit;
}
/**
- * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
- * determine the sort order.
+ * @return Returns the property that will be used to sort the result against.
+ * @see SortingProperty
*/
- public boolean isAscending() {
+ public @SortingProperty int getSortingProperty() {
+ return mSortingProperty;
+ }
+
+ /**
+ * @return Returns {@code true} if the result set will be sorted in ascending order,
+ * {@code false} if it will be sorted in descending order.
+ */
+ public boolean getSortDirection() {
return mIsAscending;
}
@@ -90,64 +151,74 @@
* A helper class to build the {@link RcsThreadQueryParameters}.
*/
public static class Builder {
- private boolean mIsGroupThread;
+ private int mThreadType;
private Set<RcsParticipant> mParticipants;
private int mLimit = 100;
+ private @SortingProperty int mSortingProperty;
private boolean mIsAscending;
/**
- * Package private constructor for {@link RcsThreadQueryParameters.Builder}. To obtain this,
- * {@link RcsThreadQueryParameters#builder()} needs to be called.
+ * Constructs a {@link RcsThreadQueryParameters.Builder} to help build an
+ * {@link RcsThreadQueryParameters}
*/
- Builder() {
+ public Builder() {
mParticipants = new HashSet<>();
}
/**
* Limits the query to only return group threads.
- * @param isGroupThread Whether to limit the query result to group threads.
+ *
+ * @param threadType Whether to limit the query result to group threads.
* @return The same instance of the builder to chain parameters.
+ * @see RcsThreadQueryParameters#THREAD_TYPE_GROUP
+ * @see RcsThreadQueryParameters#THREAD_TYPE_1_TO_1
*/
@CheckResult
- public Builder isGroupThread(boolean isGroupThread) {
- mIsGroupThread = isGroupThread;
+ public Builder setThreadType(int threadType) {
+ mThreadType = threadType;
return this;
}
/**
- * Limits the query to only return threads that contain the given participant.
+ * Limits the query to only return threads that contain the given participant. If this
+ * property was not set, participants will not be taken into account while querying for
+ * threads.
+ *
* @param participant The participant that must be included in all of the returned threads.
* @return The same instance of the builder to chain parameters.
*/
@CheckResult
- public Builder withParticipant(RcsParticipant participant) {
+ public Builder setParticipant(@NonNull RcsParticipant participant) {
mParticipants.add(participant);
return this;
}
/**
- * Limits the query to only return threads that contain the given list of participants.
+ * Limits the query to only return threads that contain the given list of participants. If
+ * this property was not set, participants will not be taken into account while querying
+ * for threads.
+ *
* @param participants An iterable list of participants that must be included in all of the
* returned threads.
* @return The same instance of the builder to chain parameters.
*/
@CheckResult
- public Builder withParticipants(Iterable<RcsParticipant> participants) {
- for (RcsParticipant participant : participants) {
- mParticipants.add(participant);
- }
+ public Builder setParticipants(@NonNull List<RcsParticipant> participants) {
+ mParticipants.addAll(participants);
return this;
}
/**
* Desired number of threads to be returned from the query. Passing in 0 will return all
* existing threads at once. The limit defaults to 100.
+ *
* @param limit The number to limit the query result to.
* @return The same instance of the builder to chain parameters.
* @throws InvalidParameterException If the given limit is negative.
*/
@CheckResult
- public Builder limitResultsTo(int limit) throws InvalidParameterException {
+ public Builder setResultLimit(@IntRange(from = 0) int limit)
+ throws InvalidParameterException {
if (limit < 0) {
throw new InvalidParameterException("The query limit must be non-negative");
}
@@ -157,15 +228,26 @@
}
/**
- * Sorts the results returned from the query via thread IDs.
+ * Sets the property where the results should be sorted against. Defaults to
+ * {@link SortingProperty#SORT_BY_CREATION_ORDER}
*
- * TODO - add sorting support for other fields
- *
- * @param isAscending whether to sort in ascending order or not
+ * @param sortingProperty whether to sort in ascending order or not
* @return The same instance of the builder to chain parameters.
*/
@CheckResult
- public Builder sort(boolean isAscending) {
+ public Builder setSortProperty(@SortingProperty int sortingProperty) {
+ mSortingProperty = sortingProperty;
+ return this;
+ }
+
+ /**
+ * Sets whether the results should be sorted ascending or descending
+ *
+ * @param isAscending whether the results should be sorted ascending
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortDirection(boolean isAscending) {
mIsAscending = isAscending;
return this;
}
@@ -177,8 +259,8 @@
* @return An instance of {@link RcsThreadQueryParameters} to use with the thread query.
*/
public RcsThreadQueryParameters build() {
- return new RcsThreadQueryParameters(
- mIsGroupThread, mParticipants, mLimit, mIsAscending);
+ return new RcsThreadQueryParameters(mThreadType, mParticipants, mLimit,
+ mSortingProperty, mIsAscending);
}
}
@@ -186,14 +268,12 @@
* Parcelable boilerplate below.
*/
protected RcsThreadQueryParameters(Parcel in) {
- mIsGroup = in.readBoolean();
-
- ArrayList<RcsParticipant> participantArrayList = new ArrayList<>();
- in.readTypedList(participantArrayList, RcsParticipant.CREATOR);
- mRcsParticipants = new HashSet<>(participantArrayList);
-
+ mThreadType = in.readInt();
+ mRcsParticipantIds = new ArrayList<>();
+ in.readList(mRcsParticipantIds, Integer.class.getClassLoader());
mLimit = in.readInt();
- mIsAscending = in.readBoolean();
+ mSortingProperty = in.readInt();
+ mIsAscending = in.readByte() == 1;
}
public static final Creator<RcsThreadQueryParameters> CREATOR =
@@ -216,10 +296,10 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeBoolean(mIsGroup);
- dest.writeTypedList(new ArrayList<>(mRcsParticipants));
+ dest.writeInt(mThreadType);
+ dest.writeList(mRcsParticipantIds);
dest.writeInt(mLimit);
- dest.writeBoolean(mIsAscending);
+ dest.writeInt(mSortingProperty);
+ dest.writeByte((byte) (mIsAscending ? 1 : 0));
}
-
}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
index 4b06529..b1d5cf4 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
@@ -1,19 +1,19 @@
/*
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ *
+ * Copyright 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.telephony.ims;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
index 47715f8..6515933 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -16,22 +16,30 @@
package android.telephony.ims;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.ims.RcsTypeIdPair;
+
+import java.util.ArrayList;
import java.util.List;
/**
- * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken,
- * RcsThreadQueryParameters)}
+ * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)}
* call. This class allows getting the token for querying the next batch of threads in order to
* prevent handling large amounts of data at once.
*
* @hide
*/
public class RcsThreadQueryResult implements Parcelable {
- private RcsThreadQueryContinuationToken mContinuationToken;
- private List<RcsThread> mRcsThreads;
+ // A token for the caller to continue their query for the next batch of results
+ private RcsQueryContinuationToken mContinuationToken;
+ // The list of thread IDs returned with this query
+ private List<RcsTypeIdPair> mRcsThreadIds;
/**
* Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
@@ -40,31 +48,47 @@
* @hide
*/
public RcsThreadQueryResult(
- RcsThreadQueryContinuationToken continuationToken, List<RcsThread> rcsThreads) {
+ RcsQueryContinuationToken continuationToken,
+ List<RcsTypeIdPair> rcsThreadIds) {
mContinuationToken = continuationToken;
- mRcsThreads = rcsThreads;
+ mRcsThreadIds = rcsThreadIds;
}
/**
* Returns a token to call
- * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)}
+ * {@link RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)}
* to get the next batch of {@link RcsThread}s.
*/
- public RcsThreadQueryContinuationToken nextChunkToken() {
+ @Nullable
+ public RcsQueryContinuationToken getContinuationToken() {
return mContinuationToken;
}
/**
* Returns all the RcsThreads in the current query result. Call {@link
- * RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)} to get the next batch of
+ * RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)} to get the next batch of
* {@link RcsThread}s.
*/
+ @NonNull
public List<RcsThread> getThreads() {
- return mRcsThreads;
+ List<RcsThread> rcsThreads = new ArrayList<>();
+
+ for (RcsTypeIdPair typeIdPair : mRcsThreadIds) {
+ if (typeIdPair.getType() == THREAD_TYPE_1_TO_1) {
+ rcsThreads.add(new Rcs1To1Thread(typeIdPair.getId()));
+ } else {
+ rcsThreads.add(new RcsGroupThread(typeIdPair.getId()));
+ }
+ }
+
+ return rcsThreads;
}
protected RcsThreadQueryResult(Parcel in) {
- // TODO - implement
+ mContinuationToken = in.readParcelable(
+ RcsQueryContinuationToken.class.getClassLoader());
+ mRcsThreadIds = new ArrayList<>();
+ in.readList(mRcsThreadIds, Integer.class.getClassLoader());
}
public static final Creator<RcsThreadQueryResult> CREATOR =
@@ -87,6 +111,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- // TODO - implement
+ dest.writeParcelable(mContinuationToken, flags);
+ dest.writeList(mRcsThreadIds);
}
}
diff --git a/telephony/java/android/telephony/ims/aidl/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
index 0c958ba..a399786 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
@@ -16,9 +16,18 @@
package android.telephony.ims.aidl;
-import android.telephony.ims.RcsParticipant;
-import android.telephony.ims.Rcs1To1Thread;
-import android.telephony.ims.RcsThreadQueryContinuationToken;
+import android.net.Uri;
+import android.telephony.ims.RcsEventQueryParameters;
+import android.telephony.ims.RcsEventQueryResult;
+import android.telephony.ims.RcsFileTransferCreationParameters;
+import android.telephony.ims.RcsIncomingMessageCreationParameters;
+import android.telephony.ims.RcsMessageCreationParameters;
+import android.telephony.ims.RcsMessageSnippet;
+import android.telephony.ims.RcsMessageQueryParameters;
+import android.telephony.ims.RcsMessageQueryResult;
+import android.telephony.ims.RcsParticipantQueryParameters;
+import android.telephony.ims.RcsParticipantQueryResult;
+import android.telephony.ims.RcsQueryContinuationToken;
import android.telephony.ims.RcsThreadQueryParameters;
import android.telephony.ims.RcsThreadQueryResult;
@@ -27,23 +36,231 @@
* {@hide}
*/
interface IRcs {
+ /////////////////////////
// RcsMessageStore APIs
+ /////////////////////////
RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParameters queryParameters);
RcsThreadQueryResult getRcsThreadsWithToken(
- in RcsThreadQueryContinuationToken continuationToken);
+ in RcsQueryContinuationToken continuationToken);
- void deleteThread(int threadId);
+ RcsParticipantQueryResult getParticipants(in RcsParticipantQueryParameters queryParameters);
- Rcs1To1Thread createRcs1To1Thread(in RcsParticipant participant);
+ RcsParticipantQueryResult getParticipantsWithToken(
+ in RcsQueryContinuationToken continuationToken);
+ RcsMessageQueryResult getMessages(in RcsMessageQueryParameters queryParameters);
+
+ RcsMessageQueryResult getMessagesWithToken(
+ in RcsQueryContinuationToken continuationToken);
+
+ RcsEventQueryResult getEvents(in RcsEventQueryParameters queryParameters);
+
+ RcsEventQueryResult getEventsWithToken(
+ in RcsQueryContinuationToken continuationToken);
+
+ // returns true if the thread was successfully deleted
+ boolean deleteThread(int threadId, int threadType);
+
+ // Creates an Rcs1To1Thread and returns its row ID
+ int createRcs1To1Thread(int participantId);
+
+ // Creates an RcsGroupThread and returns its row ID
+ int createGroupThread(in int[] participantIds, String groupName, in Uri groupIcon);
+
+ /////////////////////////
// RcsThread APIs
- int getMessageCount(int rcsThreadId);
+ /////////////////////////
+ // Creates a new RcsIncomingMessage on the given thread and returns its row ID
+ int addIncomingMessage(int rcsThreadId,
+ in RcsIncomingMessageCreationParameters rcsIncomingMessageCreationParameters);
+
+ // Creates a new RcsOutgoingMessage on the given thread and returns its row ID
+ int addOutgoingMessage(int rcsThreadId,
+ in RcsMessageCreationParameters rcsMessageCreationParameters);
+
+ // TODO: modify RcsProvider URI's to allow deleting a message without specifying its thread
+ void deleteMessage(int rcsMessageId, boolean isIncoming, int rcsThreadId, boolean isGroup);
+
+ RcsMessageSnippet getMessageSnippet(int rcsThreadId);
+
+ /////////////////////////
+ // Rcs1To1Thread APIs
+ /////////////////////////
+ void set1To1ThreadFallbackThreadId(int rcsThreadId, long fallbackId);
+
+ long get1To1ThreadFallbackThreadId(int rcsThreadId);
+
+ int get1To1ThreadOtherParticipantId(int rcsThreadId);
+
+ /////////////////////////
+ // RcsGroupThread APIs
+ /////////////////////////
+ void setGroupThreadName(int rcsThreadId, String groupName);
+
+ String getGroupThreadName(int rcsThreadId);
+
+ void setGroupThreadIcon(int rcsThreadId, in Uri groupIcon);
+
+ Uri getGroupThreadIcon(int rcsThreadId);
+
+ void setGroupThreadOwner(int rcsThreadId, int participantId);
+
+ int getGroupThreadOwner(int rcsThreadId);
+
+ void setGroupThreadConferenceUri(int rcsThreadId, in Uri conferenceUri);
+
+ Uri getGroupThreadConferenceUri(int rcsThreadId);
+
+ void addParticipantToGroupThread(int rcsThreadId, int participantId);
+
+ void removeParticipantFromGroupThread(int rcsThreadId, int participantId);
+
+ /////////////////////////
// RcsParticipant APIs
- RcsParticipant createRcsParticipant(String canonicalAddress);
+ /////////////////////////
- void updateRcsParticipantCanonicalAddress(int id, String canonicalAddress);
+ // Creates a new RcsParticipant and returns its rowId
+ int createRcsParticipant(String canonicalAddress, String alias);
- void updateRcsParticipantAlias(int id, String alias);
+ String getRcsParticipantCanonicalAddress(int participantId);
+
+ String getRcsParticipantAlias(int participantId);
+
+ void setRcsParticipantAlias(int id, String alias);
+
+ String getRcsParticipantContactId(int participantId);
+
+ void setRcsParticipantContactId(int participantId, String contactId);
+
+ /////////////////////////
+ // RcsMessage APIs
+ /////////////////////////
+ void setMessageSubId(int messageId, boolean isIncoming, int subId);
+
+ int getMessageSubId(int messageId, boolean isIncoming);
+
+ void setMessageStatus(int messageId, boolean isIncoming, int status);
+
+ int getMessageStatus(int messageId, boolean isIncoming);
+
+ void setMessageOriginationTimestamp(int messageId, boolean isIncoming, long originationTimestamp);
+
+ long getMessageOriginationTimestamp(int messageId, boolean isIncoming);
+
+ void setGlobalMessageIdForMessage(int messageId, boolean isIncoming, String globalId);
+
+ String getGlobalMessageIdForMessage(int messageId, boolean isIncoming);
+
+ void setMessageArrivalTimestamp(int messageId, boolean isIncoming, long arrivalTimestamp);
+
+ long getMessageArrivalTimestamp(int messageId, boolean isIncoming);
+
+ void setMessageSeenTimestamp(int messageId, boolean isIncoming, long seenTimestamp);
+
+ long getMessageSeenTimestamp(int messageId, boolean isIncoming);
+
+ void setTextForMessage(int messageId, boolean isIncoming, String text);
+
+ String getTextForMessage(int messageId, boolean isIncoming);
+
+ void setLatitudeForMessage(int messageId, boolean isIncoming, double latitude);
+
+ double getLatitudeForMessage(int messageId, boolean isIncoming);
+
+ void setLongitudeForMessage(int messageId, boolean isIncoming, double longitude);
+
+ double getLongitudeForMessage(int messageId, boolean isIncoming);
+
+ // Returns the ID's of the file transfers attached to the given message
+ int[] getFileTransfersAttachedToMessage(int messageId, boolean isIncoming);
+
+ int getSenderParticipant(int messageId);
+
+ /////////////////////////
+ // RcsOutgoingMessageDelivery APIs
+ /////////////////////////
+
+ // Returns the participant ID's that this message is intended to be delivered to
+ int[] getMessageRecipients(int messageId);
+
+ long getOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId);
+
+ void setOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId, long deliveredTimestamp);
+
+ long getOutgoingDeliverySeenTimestamp(int messageId, int participantId);
+
+ void setOutgoingDeliverySeenTimestamp(int messageId, int participantId, long seenTimestamp);
+
+ int getOutgoingDeliveryStatus(int messageId, int participantId);
+
+ void setOutgoingDeliveryStatus(int messageId, int participantId, int status);
+
+ /////////////////////////
+ // RcsFileTransferPart APIs
+ /////////////////////////
+
+ // Performs the initial write to storage and returns the row ID.
+ int storeFileTransfer(int messageId, boolean isIncoming,
+ in RcsFileTransferCreationParameters fileTransferCreationParameters);
+
+ void deleteFileTransfer(int partId);
+
+ void setFileTransferSessionId(int partId, String sessionId);
+
+ String getFileTransferSessionId(int partId);
+
+ void setFileTransferContentUri(int partId, in Uri contentUri);
+
+ Uri getFileTransferContentUri(int partId);
+
+ void setFileTransferContentType(int partId, String contentType);
+
+ String getFileTransferContentType(int partId);
+
+ void setFileTransferFileSize(int partId, long fileSize);
+
+ long getFileTransferFileSize(int partId);
+
+ void setFileTransferTransferOffset(int partId, long transferOffset);
+
+ long getFileTransferTransferOffset(int partId);
+
+ void setFileTransferStatus(int partId, int transferStatus);
+
+ int getFileTransferStatus(int partId);
+
+ void setFileTransferWidth(int partId, int width);
+
+ int getFileTransferWidth(int partId);
+
+ void setFileTransferHeight(int partId, int height);
+
+ int getFileTransferHeight(int partId);
+
+ void setFileTransferLength(int partId, long length);
+
+ long getFileTransferLength(int partId);
+
+ void setFileTransferPreviewUri(int partId, in Uri uri);
+
+ Uri getFileTransferPreviewUri(int partId);
+
+ void setFileTransferPreviewType(int partId, String type);
+
+ String getFileTransferPreviewType(int partId);
+
+ /////////////////////////
+ // RcsEvent APIs
+ /////////////////////////
+ int createGroupThreadNameChangedEvent(long timestamp, int threadId, int originationParticipantId, String newName);
+
+ int createGroupThreadIconChangedEvent(long timestamp, int threadId, int originationParticipantId, in Uri newIcon);
+
+ int createGroupThreadParticipantJoinedEvent(long timestamp, int threadId, int originationParticipantId, int participantId);
+
+ int createGroupThreadParticipantLeftEvent(long timestamp, int threadId, int originationParticipantId, int participantId);
+
+ int createParticipantAliasChangedEvent(long timestamp, int participantId, String newAlias);
}
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/RcsTypeIdPair.java b/telephony/java/com/android/ims/RcsTypeIdPair.java
new file mode 100644
index 0000000..a517735
--- /dev/null
+++ b/telephony/java/com/android/ims/RcsTypeIdPair.java
@@ -0,0 +1,79 @@
+/*
+ * 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.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A utility class to pass RCS IDs and types in RPC calls
+ *
+ * @hide
+ */
+public class RcsTypeIdPair implements Parcelable {
+ private int mType;
+ private int mId;
+
+ public RcsTypeIdPair(int type, int id) {
+ mType = type;
+ mId = id;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public void setType(int type) {
+ mType = type;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public void setId(int id) {
+ mId = id;
+ }
+
+ public RcsTypeIdPair(Parcel in) {
+ mType = in.readInt();
+ mId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeInt(mId);
+ }
+
+ public static final Creator<RcsTypeIdPair> CREATOR =
+ new Creator<RcsTypeIdPair>() {
+ @Override
+ public RcsTypeIdPair createFromParcel(Parcel in) {
+ return new RcsTypeIdPair(in);
+ }
+
+ @Override
+ public RcsTypeIdPair[] newArray(int size) {
+ return new RcsTypeIdPair[size];
+ }
+ };
+}
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index d3420ee..7478a00 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -636,7 +636,7 @@
// Update the setting.
RoleManagerCallback.Future res = new RoleManagerCallback.Future();
context.getSystemService(RoleManager.class).addRoleHolderAsUser(
- RoleManager.ROLE_SMS, applicationData.mPackageName, UserHandle.of(userId),
+ RoleManager.ROLE_SMS, applicationData.mPackageName, 0, UserHandle.of(userId),
AsyncTask.THREAD_POOL_EXECUTOR, res);
try {
res.get(5, TimeUnit.SECONDS);
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 964a313..9080e23 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -21,7 +21,6 @@
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsCbLocation;
import android.telephony.SmsCbMessage;
-import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaSmsCbProgramData;
import android.telephony.Rlog;
import android.util.Log;
@@ -746,8 +745,10 @@
/**
* Parses a broadcast SMS, possibly containing a CMAS alert.
+ *
+ * @param plmn the PLMN for a broadcast SMS
*/
- public SmsCbMessage parseBroadcastSms() {
+ public SmsCbMessage parseBroadcastSms(String plmn) {
BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory);
if (bData == null) {
Rlog.w(LOG_TAG, "BearerData.decode() returned null");
@@ -758,7 +759,6 @@
Rlog.d(LOG_TAG, "MT raw BearerData = " + HexDump.toHexString(mEnvelope.bearerData));
}
- String plmn = TelephonyManager.getDefault().getNetworkOperator();
SmsCbLocation location = new SmsCbLocation(plmn);
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
@@ -858,11 +858,11 @@
bearerData.userData = userData;
byte[] encodedBearerData = BearerData.encode(bearerData);
+ if (encodedBearerData == null) return null;
if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
Rlog.d(LOG_TAG, "MO (encoded) BearerData = " + bearerData);
Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'");
}
- if (encodedBearerData == null) return null;
int teleservice = bearerData.hasUserDataHeader ?
SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
new file mode 100644
index 0000000..915a260
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadIconChangedEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadIconChangedEventTest {
+
+ @Test
+ public void testCanUnparcel() {
+ RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+ RcsParticipant rcsParticipant = new RcsParticipant(2);
+ Uri newIconUri = Uri.parse("content://new_icon");
+
+ RcsGroupThreadIconChangedEvent iconChangedEvent =
+ new RcsGroupThreadIconChangedEvent(1234567890, rcsGroupThread, rcsParticipant,
+ newIconUri);
+
+ Parcel parcel = Parcel.obtain();
+ iconChangedEvent.writeToParcel(parcel, iconChangedEvent.describeContents());
+
+ parcel.setDataPosition(0);
+
+ iconChangedEvent = RcsGroupThreadIconChangedEvent.CREATOR.createFromParcel(parcel);
+
+ assertThat(iconChangedEvent.getNewIcon()).isEqualTo(newIconUri);
+ assertThat(iconChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+ assertThat(iconChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
+ assertThat(iconChangedEvent.getTimestamp()).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
new file mode 100644
index 0000000..1384c01
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadNameChangedEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadNameChangedEventTest {
+ @Test
+ public void testCanUnparcel() {
+ String newName = "new name";
+
+ RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+ RcsParticipant rcsParticipant = new RcsParticipant(2);
+
+ RcsGroupThreadNameChangedEvent nameChangedEvent =
+ new RcsGroupThreadNameChangedEvent(1234567890, rcsGroupThread, rcsParticipant,
+ newName);
+
+ Parcel parcel = Parcel.obtain();
+ nameChangedEvent.writeToParcel(parcel, nameChangedEvent.describeContents());
+
+ parcel.setDataPosition(0);
+
+ nameChangedEvent = RcsGroupThreadNameChangedEvent.CREATOR.createFromParcel(
+ parcel);
+
+ assertThat(nameChangedEvent.getNewName()).isEqualTo(newName);
+ assertThat(nameChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+ assertThat(nameChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
+ assertThat(nameChangedEvent.getTimestamp()).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
new file mode 100644
index 0000000..d0af7db
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadParticipantJoinedEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadParticipantJoinedEventTest {
+
+ @Test
+ public void testCanUnparcel() {
+ RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+ RcsParticipant rcsParticipant = new RcsParticipant(2);
+
+ RcsGroupThreadParticipantJoinedEvent participantJoinedEvent =
+ new RcsGroupThreadParticipantJoinedEvent(1234567890, rcsGroupThread, rcsParticipant,
+ rcsParticipant);
+
+ Parcel parcel = Parcel.obtain();
+ participantJoinedEvent.writeToParcel(parcel, participantJoinedEvent.describeContents());
+
+ parcel.setDataPosition(0);
+
+ participantJoinedEvent = RcsGroupThreadParticipantJoinedEvent.CREATOR.createFromParcel(
+ parcel);
+
+ assertThat(participantJoinedEvent.getJoinedParticipant().getId()).isEqualTo(2);
+ assertThat(participantJoinedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+ assertThat(participantJoinedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
+ assertThat(participantJoinedEvent.getTimestamp()).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
new file mode 100644
index 0000000..7ba5fa6
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadParticipantLeftEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadParticipantLeftEventTest {
+ @Test
+ public void testCanUnparcel() {
+ RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+ RcsParticipant rcsParticipant = new RcsParticipant(2);
+
+ RcsGroupThreadParticipantLeftEvent participantLeftEvent =
+ new RcsGroupThreadParticipantLeftEvent(1234567890, rcsGroupThread, rcsParticipant,
+ rcsParticipant);
+
+ Parcel parcel = Parcel.obtain();
+ participantLeftEvent.writeToParcel(parcel, participantLeftEvent.describeContents());
+
+ parcel.setDataPosition(0);
+
+ // create from parcel
+ parcel.setDataPosition(0);
+ participantLeftEvent = RcsGroupThreadParticipantLeftEvent.CREATOR.createFromParcel(
+ parcel);
+ assertThat(participantLeftEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+ assertThat(participantLeftEvent.getLeavingParticipantId().getId()).isEqualTo(2);
+ assertThat(participantLeftEvent.getTimestamp()).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
deleted file mode 100644
index 44277ed..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tests.ims;
-
-import android.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.RcsMessageStore;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsMessageStoreTest {
- //TODO(sahinc): Add meaningful tests once we have more of the implementation in place
- @Test
- public void testDeleteThreadDoesntCrash() {
- RcsMessageStore mRcsMessageStore = new RcsMessageStore();
- mRcsMessageStore.deleteThread(0);
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
new file mode 100644
index 0000000..3e2bbbf
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.RcsParticipantAliasChangedEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsParticipantAliasChangedEventTest {
+ private static final String OLD_ALIAS = "old alias";
+ private static final String NEW_ALIAS = "new alias";
+ private RcsParticipant mParticipant;
+
+ @Before
+ public void setUp() {
+ mParticipant = new RcsParticipant(3);
+ }
+
+ @Test
+ public void testCanUnparcel() {
+ RcsParticipantAliasChangedEvent aliasChangedEvent =
+ new RcsParticipantAliasChangedEvent(1234567890, mParticipant, NEW_ALIAS);
+
+ Parcel parcel = Parcel.obtain();
+ aliasChangedEvent.writeToParcel(parcel, aliasChangedEvent.describeContents());
+
+ parcel.setDataPosition(0);
+
+ aliasChangedEvent = RcsParticipantAliasChangedEvent.CREATOR.createFromParcel(
+ parcel);
+
+ assertThat(aliasChangedEvent.getParticipantId().getId()).isEqualTo(3);
+ assertThat(aliasChangedEvent.getNewAlias()).isEqualTo(NEW_ALIAS);
+ assertThat(aliasChangedEvent.getTimestamp()).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParametersTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParametersTest.java
new file mode 100644
index 0000000..b4bcb5d
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParametersTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipantQueryParameters;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsParticipantQueryParametersTest {
+
+ @Test
+ public void testCanUnparcel() {
+ RcsParticipantQueryParameters rcsParticipantQueryParameters =
+ new RcsParticipantQueryParameters.Builder()
+ .setAliasLike("%alias_")
+ .setCanonicalAddressLike("_canonical%")
+ .setSortProperty(RcsParticipantQueryParameters.SORT_BY_CANONICAL_ADDRESS)
+ .setSortDirection(true)
+ .setResultLimit(432)
+ .build();
+
+
+ Parcel parcel = Parcel.obtain();
+ rcsParticipantQueryParameters.writeToParcel(parcel,
+ rcsParticipantQueryParameters.describeContents());
+
+ parcel.setDataPosition(0);
+ rcsParticipantQueryParameters = RcsParticipantQueryParameters.CREATOR.createFromParcel(
+ parcel);
+
+ assertThat(rcsParticipantQueryParameters.getAliasLike()).isEqualTo("%alias_");
+ assertThat(rcsParticipantQueryParameters.getCanonicalAddressLike()).contains("_canonical%");
+ assertThat(rcsParticipantQueryParameters.getLimit()).isEqualTo(432);
+ assertThat(rcsParticipantQueryParameters.getSortingProperty()).isEqualTo(
+ RcsParticipantQueryParameters.SORT_BY_CANONICAL_ADDRESS);
+ assertThat(rcsParticipantQueryParameters.getSortDirection()).isTrue();
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java
deleted file mode 100644
index c402dbf..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-import android.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.RcsParticipant;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsParticipantTest {
- private static final int ID = 123;
- private static final String ALIAS = "alias";
- private static final String CANONICAL_ADDRESS = "+1234567890";
-
- @Test
- public void testCanUnparcel() {
- RcsParticipant rcsParticipant = new RcsParticipant(ID, CANONICAL_ADDRESS);
- rcsParticipant.setAlias(ALIAS);
-
- Bundle bundle = new Bundle();
- bundle.putParcelable("Some key", rcsParticipant);
- rcsParticipant = bundle.getParcelable("Some key");
-
- assertThat(rcsParticipant.getId()).isEqualTo(ID);
- assertThat(rcsParticipant.getAlias()).isEqualTo(ALIAS);
- assertThat(rcsParticipant.getCanonicalAddress()).isEqualTo(CANONICAL_ADDRESS);
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
index a890a38..0a70eec 100644
--- a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
@@ -15,39 +15,44 @@
*/
package com.android.tests.ims;
+import static android.telephony.ims.RcsThreadQueryParameters.SORT_BY_TIMESTAMP;
+import static android.telephony.ims.RcsThreadQueryParameters.THREAD_TYPE_GROUP;
+
import static com.google.common.truth.Truth.assertThat;
-import android.os.Bundle;
+import android.os.Parcel;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.ims.RcsParticipant;
import android.telephony.ims.RcsThreadQueryParameters;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
@RunWith(AndroidJUnit4.class)
public class RcsThreadQueryParametersTest {
- private RcsThreadQueryParameters mRcsThreadQueryParameters;
- @Mock RcsParticipant mMockParticipant;
@Test
- public void testUnparceling() {
- String key = "some key";
- mRcsThreadQueryParameters = RcsThreadQueryParameters.builder()
- .isGroupThread(true)
- .withParticipant(mMockParticipant)
- .limitResultsTo(50)
- .sort(true)
+ public void testCanUnparcel() {
+ RcsParticipant rcsParticipant = new RcsParticipant(1);
+ RcsThreadQueryParameters rcsThreadQueryParameters = new RcsThreadQueryParameters.Builder()
+ .setThreadType(THREAD_TYPE_GROUP)
+ .setParticipant(rcsParticipant)
+ .setResultLimit(50)
+ .setSortProperty(SORT_BY_TIMESTAMP)
+ .setSortDirection(true)
.build();
- Bundle bundle = new Bundle();
- bundle.putParcelable(key, mRcsThreadQueryParameters);
- mRcsThreadQueryParameters = bundle.getParcelable(key);
+ Parcel parcel = Parcel.obtain();
+ rcsThreadQueryParameters.writeToParcel(parcel, rcsThreadQueryParameters.describeContents());
- assertThat(mRcsThreadQueryParameters.isGroupThread()).isTrue();
- assertThat(mRcsThreadQueryParameters.getRcsParticipants()).contains(mMockParticipant);
- assertThat(mRcsThreadQueryParameters.getLimit()).isEqualTo(50);
- assertThat(mRcsThreadQueryParameters.isAscending()).isTrue();
+ parcel.setDataPosition(0);
+ rcsThreadQueryParameters = RcsThreadQueryParameters.CREATOR.createFromParcel(parcel);
+
+ assertThat(rcsThreadQueryParameters.getThreadType()).isEqualTo(THREAD_TYPE_GROUP);
+ assertThat(rcsThreadQueryParameters.getRcsParticipantsIds())
+ .contains(rcsParticipant.getId());
+ assertThat(rcsThreadQueryParameters.getLimit()).isEqualTo(50);
+ assertThat(rcsThreadQueryParameters.getSortingProperty()).isEqualTo(SORT_BY_TIMESTAMP);
+ assertThat(rcsThreadQueryParameters.getSortDirection()).isTrue();
}
}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 4b277ae..f07ae9f 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -685,7 +685,7 @@
ActivityManager am = context.getSystemService(ActivityManager.class);
am.killBackgroundProcesses(TEST_APP_A);
// Allow another package launch
- crashQueue.offer(intent.getIntExtra("count", 0), 5, TimeUnit.SECONDS);
+ crashQueue.put(intent.getIntExtra("count", 0));
} catch (InterruptedException e) {
fail("Failed to communicate with test app");
}
@@ -694,14 +694,9 @@
context.registerReceiver(crashCountReceiver, crashCountFilter);
// Start apps PackageWatchdog#TRIGGER_FAILURE_COUNT times so TEST_APP_A crashes
- Integer crashCount = null;
do {
RollbackTestUtils.launchPackage(TEST_APP_A);
- crashCount = crashQueue.poll(5, TimeUnit.SECONDS);
- if (crashCount == null) {
- fail("Timed out waiting for crash signal from test app");
- }
- } while(crashCount < 5);
+ } while(crashQueue.take() < 5);
// TEST_APP_A is automatically rolled back by the RollbackPackageHealthObserver
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 257043b..a8d970e 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -48,6 +48,7 @@
primaryFields(that.primaryFields),
exclusiveField(that.exclusiveField),
uidField(that.uidField),
+ whitelisted(that.whitelisted),
binaryFields(that.binaryFields) {}
AtomDecl::AtomDecl(int c, const string& n, const string& m)
@@ -162,6 +163,7 @@
vector<java_type_t> *signature) {
int errorCount = 0;
+
// Build a sorted list of the fields. Descriptor has them in source file
// order.
map<int, const FieldDescriptor *> fields;
@@ -387,6 +389,11 @@
const Descriptor *atom = atomField->message_type();
AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
+
+ if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) {
+ atomDecl.whitelisted = true;
+ }
+
vector<java_type_t> signature;
errorCount += collate_atom(atom, &atomDecl, &signature);
if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) {
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 450b305..6b86b862 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -89,6 +89,8 @@
int uidField = 0;
+ bool whitelisted = false;
+
vector<int> binaryFields;
AtomDecl();
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 4491a85..55440d2 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -158,6 +158,20 @@
}
}
}
+
+ fprintf(out, "};\n");
+ fprintf(out, "\n");
+
+ fprintf(out,
+ "const std::set<int> AtomsInfo::kWhitelistedAtoms = {\n");
+ for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+ atom != atoms.decls.end(); atom++) {
+ if (atom->whitelisted) {
+ string constant = make_constant_name(atom->name);
+ fprintf(out, " %s,\n", constant.c_str());
+ }
+ }
+
fprintf(out, "};\n");
fprintf(out, "\n");
@@ -728,6 +742,8 @@
fprintf(out,
" const static std::map<int, std::vector<int>> "
"kBytesFieldAtoms;");
+ fprintf(out,
+ " const static std::set<int> kWhitelistedAtoms;\n");
fprintf(out, "};\n");
fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index 3be87d9..24ebf4d 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -195,4 +195,22 @@
[(android.os.statsd.state_field_option).option = PRIMARY];
optional int32 state = 3
[(android.os.statsd.state_field_option).option = EXCLUSIVE];
+}
+
+message WhitelistedAtom {
+ optional int32 field = 1;
+}
+
+message NonWhitelistedAtom {
+ optional int32 field = 1;
+}
+
+message ListedAtoms {
+ oneof event {
+ // Atoms can be whitelisted i.e. they can be triggered by any source
+ WhitelistedAtom whitelisted_atom = 1 [(android.os.statsd.allow_from_any_uid) = true];
+ // Atoms are not whitelisted by default, so they can only be triggered
+ // by whitelisted sources
+ NonWhitelistedAtom non_whitelisted_atom = 2;
+ }
}
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index ad3bffac..dc585c1 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -226,5 +226,25 @@
EXPECT_TRUE(errorCount > 0);
}
+TEST(CollationTest, PassOnWhitelistedAtom) {
+ Atoms atoms;
+ int errorCount =
+ collate_atoms(ListedAtoms::descriptor(), &atoms);
+ EXPECT_EQ(errorCount, 0);
+ EXPECT_EQ(atoms.decls.size(), 2ul);
+}
+
+TEST(CollationTest, RecogniseWhitelistedAtom) {
+ Atoms atoms;
+ collate_atoms(ListedAtoms::descriptor(), &atoms);
+ for (const auto& atomDecl : atoms.decls) {
+ if (atomDecl.code == 1) {
+ EXPECT_TRUE(atomDecl.whitelisted);
+ } else {
+ EXPECT_FALSE(atomDecl.whitelisted);
+ }
+ }
+}
+
} // namespace stats_log_api_gen
} // namespace android
\ No newline at end of file