Merge "Fix intents in OngoingPrivacyDialog"
diff --git a/Android.bp b/Android.bp
index b075f5c..7bdedc7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -77,7 +77,6 @@
"core/java/android/app/ISearchManager.aidl",
"core/java/android/app/ISearchManagerCallback.aidl",
"core/java/android/app/IServiceConnection.aidl",
- "core/java/android/app/ISmsAppService.aidl",
"core/java/android/app/IStopUserCallback.aidl",
"core/java/android/app/job/IJobCallback.aidl",
"core/java/android/app/job/IJobScheduler.aidl",
@@ -288,6 +287,7 @@
"core/java/android/service/carrier/ICarrierService.aidl",
"core/java/android/service/carrier/ICarrierMessagingCallback.aidl",
"core/java/android/service/carrier/ICarrierMessagingService.aidl",
+ "core/java/android/service/carrier/ICarrierMessagingClientService.aidl",
"core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl",
"core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl",
"core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl",
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 16b7b74..b321c224 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26,6 +26,7 @@
field public static final String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
field public static final String BIND_AUTOFILL_SERVICE = "android.permission.BIND_AUTOFILL_SERVICE";
field public static final String BIND_CALL_REDIRECTION_SERVICE = "android.permission.BIND_CALL_REDIRECTION_SERVICE";
+ field public static final String BIND_CARRIER_MESSAGING_CLIENT_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE";
field @Deprecated public static final String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES";
field public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
@@ -41,7 +42,6 @@
field public static final String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
field public static final String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE";
- field public static final String BIND_SMS_APP_SERVICE = "android.permission.BIND_SMS_APP_SERVICE";
field public static final String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
field public static final String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
field public static final String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
@@ -645,6 +645,7 @@
field public static final int footerDividersEnabled = 16843311; // 0x101022f
field public static final int forceDarkAllowed = 16844172; // 0x101058c
field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
+ field public static final int forceUriPermissions = 16844197; // 0x10105a5
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundServiceType = 16844191; // 0x101059f
@@ -6210,11 +6211,6 @@
method public void onSharedElementsReady();
}
- public class SmsAppService extends android.app.Service {
- ctor public SmsAppService();
- method public final android.os.IBinder onBind(android.content.Intent);
- }
-
public class StatusBarManager {
}
@@ -6742,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);
@@ -7252,6 +7249,7 @@
ctor public BackupManager(android.content.Context);
method public void dataChanged();
method public static void dataChanged(String);
+ method @Nullable public android.os.UserHandle getUserForAncestralSerialNumber(long);
method @Deprecated public int requestRestore(android.app.backup.RestoreObserver);
}
@@ -11879,6 +11877,7 @@
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public String authority;
field public int flags;
+ field public boolean forceUriPermissions;
field public boolean grantUriPermissions;
field public int initOrder;
field @Deprecated public boolean isSyncable;
@@ -19447,9 +19446,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 +19465,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
@@ -23747,6 +23746,7 @@
ctor public ExifInterface(@NonNull java.io.InputStream) throws java.io.IOException;
method public double getAltitude(double);
method @Nullable public String getAttribute(@NonNull String);
+ method @Nullable public byte[] getAttributeBytes(@NonNull String);
method public double getAttributeDouble(@NonNull String, double);
method public int getAttributeInt(@NonNull String, int);
method @Nullable public long[] getAttributeRange(@NonNull String);
@@ -23902,6 +23902,7 @@
field public static final String TAG_USER_COMMENT = "UserComment";
field public static final String TAG_WHITE_BALANCE = "WhiteBalance";
field public static final String TAG_WHITE_POINT = "WhitePoint";
+ field public static final String TAG_XMP = "Xmp";
field public static final String TAG_X_RESOLUTION = "XResolution";
field public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
field public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
@@ -34202,7 +34203,6 @@
}
public static class Build.Partition {
- ctor public Build.Partition();
method public long getBuildTimeMillis();
method @NonNull public String getFingerprint();
method @NonNull public String getName();
@@ -41360,6 +41360,11 @@
field public static final android.os.Parcelable.Creator<android.service.carrier.CarrierIdentifier> CREATOR;
}
+ public class CarrierMessagingClientService extends android.app.Service {
+ ctor public CarrierMessagingClientService();
+ method public final android.os.IBinder onBind(android.content.Intent);
+ }
+
public abstract class CarrierMessagingService extends android.app.Service {
ctor public CarrierMessagingService();
method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
@@ -45166,13 +45171,13 @@
method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int);
method public boolean updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>);
+ field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE";
field public static final String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED";
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE";
field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
- field public static final String ACTION_SMS_APP_SERVICE = "android.telephony.action.SMS_APP_SERVICE";
field public static final String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
field public static final String ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED";
field public static final int APPTYPE_CSIM = 4; // 0x4
@@ -56368,7 +56373,11 @@
method @Nullable public android.graphics.PorterDuff.Mode getIndeterminateTintMode();
method public android.view.animation.Interpolator getInterpolator();
method @android.view.ViewDebug.ExportedProperty(category="progress") public int getMax();
+ method @Px public int getMaxHeight();
+ method @Px public int getMaxWidth();
method @android.view.ViewDebug.ExportedProperty(category="progress") public int getMin();
+ method @Px public int getMinHeight();
+ method @Px public int getMinWidth();
method @android.view.ViewDebug.ExportedProperty(category="progress") public int getProgress();
method @Nullable public android.content.res.ColorStateList getProgressBackgroundTintList();
method @Nullable public android.graphics.PorterDuff.Mode getProgressBackgroundTintMode();
@@ -56392,7 +56401,11 @@
method public void setInterpolator(android.content.Context, @InterpolatorRes int);
method public void setInterpolator(android.view.animation.Interpolator);
method public void setMax(int);
+ method public void setMaxHeight(@Px int);
+ method public void setMaxWidth(@Px int);
method public void setMin(int);
+ method public void setMinHeight(@Px int);
+ method public void setMinWidth(@Px int);
method public void setProgress(int);
method public void setProgress(int, boolean);
method public void setProgressBackgroundTintList(@Nullable android.content.res.ColorStateList);
@@ -56656,6 +56669,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 a0eef1d..b7fc339 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -711,6 +711,7 @@
method @Deprecated public int requestRestore(android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor);
method @Deprecated @RequiresPermission(android.Manifest.permission.BACKUP) public String selectBackupTransport(String);
method @RequiresPermission(android.Manifest.permission.BACKUP) public void selectBackupTransport(android.content.ComponentName, android.app.backup.SelectBackupTransportCallback);
+ method @RequiresPermission(android.Manifest.permission.BACKUP) public void setAncestralSerialNumber(long);
method @RequiresPermission(android.Manifest.permission.BACKUP) public void setAutoRestore(boolean);
method @RequiresPermission(android.Manifest.permission.BACKUP) public void setBackupEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.BACKUP) public void updateTransportAttributes(android.content.ComponentName, String, @Nullable android.content.Intent, String, @Nullable android.content.Intent, @Nullable String);
@@ -1071,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";
}
@@ -3912,11 +3914,13 @@
public class ConnectivityManager {
method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull java.io.FileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+ method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
method public boolean getAvoidBadWifi();
method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
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);
@@ -4999,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;
}
@@ -5024,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 {
@@ -6035,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/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/atoms.proto b/cmds/statsd/src/atoms.proto
index fc3aa91..07aced6 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -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/external/PullDataReceiver.h b/cmds/statsd/src/external/PullDataReceiver.h
index 0d505cb..b071682 100644
--- a/cmds/statsd/src/external/PullDataReceiver.h
+++ b/cmds/statsd/src/external/PullDataReceiver.h
@@ -28,9 +28,15 @@
class PullDataReceiver : virtual public RefBase{
public:
virtual ~PullDataReceiver() {}
- virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) = 0;
+ /**
+ * @param data The pulled data.
+ * @param pullSuccess Whether the pull succeeded. If the pull does not succeed, the data for the
+ * bucket should be invalidated.
+ */
+ virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data,
+ bool pullSuccess) = 0;
};
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index c69384c..9b603d6 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -358,12 +358,13 @@
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
- if (!Pull(pullInfo.first, &data)) {
+ bool pullSuccess = Pull(pullInfo.first, &data);
+ if (pullSuccess) {
+ StatsdStats::getInstance().notePullDelay(
+ pullInfo.first, getElapsedRealtimeNs() - elapsedTimeNs);
+ } else {
VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
- continue;
}
- StatsdStats::getInstance().notePullDelay(pullInfo.first,
- getElapsedRealtimeNs() - elapsedTimeNs);
// Convention is to mark pull atom timestamp at request time.
// If we pull at t0, puller starts at t1, finishes at t2, and send back
@@ -380,8 +381,8 @@
for (const auto& receiverInfo : pullInfo.second) {
sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
if (receiverPtr != nullptr) {
- receiverPtr->onDataPulled(data);
- // we may have just come out of a coma, compute next pull time
+ receiverPtr->onDataPulled(data, pullSuccess);
+ // We may have just come out of a coma, compute next pull time.
int numBucketsAhead =
(elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs;
receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs;
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 37ccad5..37d5ba0 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -443,11 +443,21 @@
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++;
}
+void StatsdStats::noteInvalidatedBucket(int metricId) {
+ lock_guard<std::mutex> lock(mLock);
+ getAtomMetricStats(metricId).invalidatedBucket++;
+}
+
StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int metricId) {
auto atomMetricStatsIter = mAtomMetricStats.find(metricId);
if (atomMetricStatsIter != mAtomMetricStats.end()) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 01e9ca1..20ea7e5 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -360,11 +360,21 @@
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);
/**
+ * A bucket has been tagged as invalid.
+ */
+ void noteInvalidatedBucket(int metricId);
+
+ /**
* Reset the historical stats. Including all stats in icebox, and the tracked stats about
* metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
* to collect stats after reset() has been called.
@@ -408,6 +418,8 @@
long skippedForwardBuckets = 0;
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 c2878f0..2609937 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -406,9 +406,10 @@
return gaugeFields;
}
-void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
+void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
+ bool pullSuccess) {
std::lock_guard<std::mutex> lock(mMutex);
- if (allData.size() == 0) {
+ if (!pullSuccess || allData.size() == 0) {
return;
}
for (const auto& data : allData) {
@@ -509,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/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index df08779..d480941 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -67,7 +67,8 @@
virtual ~GaugeMetricProducer();
// Handles when the pulled data arrives.
- void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
+ void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data,
+ bool pullSuccess) override;
// GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
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..ca68117 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() {
@@ -304,8 +311,10 @@
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 +332,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 +430,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..80982c3 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"
@@ -123,6 +124,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 +231,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 +265,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 6aa8e84..d52c9ef 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -104,6 +104,7 @@
mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
mUseZeroDefaultBase(metric.use_zero_default_base()),
mHasGlobalBase(false),
+ mCurrentBucketIsInvalid(false),
mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
: StatsdStats::kPullMaxDelayNs),
mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) {
@@ -174,6 +175,7 @@
void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
flushIfNeededLocked(dropTimeNs);
+ StatsdStats::getInstance().noteBucketDropped(mMetricId);
mPastBuckets.clear();
}
@@ -308,6 +310,15 @@
}
}
+void ValueMetricProducer::invalidateCurrentBucket() {
+ if (!mCurrentBucketIsInvalid) {
+ // Only report once per invalid bucket.
+ StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
+ }
+ mCurrentBucketIsInvalid = true;
+ resetBase();
+}
+
void ValueMetricProducer::resetBase() {
for (auto& slice : mCurrentSlicedBucket) {
for (auto& interval : slice.second) {
@@ -323,6 +334,7 @@
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
+ invalidateCurrentBucket();
return;
}
@@ -346,19 +358,20 @@
vector<std::shared_ptr<LogEvent>> allData;
if (!mPullerManager->Pull(mPullTagId, &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
- resetBase();
+ invalidateCurrentBucket();
return;
}
const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
if (pullDelayNs > mMaxPullDelayNs) {
ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId,
(long long)mMaxPullDelayNs);
StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
- resetBase();
+ // We are missing one pull from the bucket which means we will not have a complete view of
+ // what's going on.
+ invalidateCurrentBucket();
return;
}
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
if (timestampNs < mCurrentBucketStartTimeNs) {
// The data will be skipped in onMatchedLogEventInternalLocked, but we don't want to report
@@ -382,9 +395,16 @@
return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
}
-void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
+void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
+ bool pullSuccess) {
std::lock_guard<std::mutex> lock(mMutex);
if (mCondition) {
+ if (!pullSuccess) {
+ // If the pull failed, we won't be able to compute a diff.
+ invalidateCurrentBucket();
+ return;
+ }
+
if (allData.size() == 0) {
VLOG("Data pulled is empty");
StatsdStats::getInstance().noteEmptyData(mPullTagId);
@@ -399,12 +419,13 @@
// if the diff base will be cleared and this new data will serve as new diff base.
int64_t realEventTime = allData.at(0)->GetElapsedTimestampNs();
int64_t bucketEndTime = calcPreviousBucketEndTime(realEventTime) - 1;
- if (bucketEndTime < mCurrentBucketStartTimeNs) {
+ bool isEventLate = bucketEndTime < mCurrentBucketStartTimeNs;
+ if (isEventLate) {
VLOG("Skip bucket end pull due to late arrival: %lld vs %lld", (long long)bucketEndTime,
(long long)mCurrentBucketStartTimeNs);
StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
- return;
}
+
for (const auto& data : allData) {
LogEvent localCopy = data->makeCopy();
if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
@@ -679,31 +700,13 @@
VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
(int)mCurrentSlicedBucket.size());
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
-
int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
- if (bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs) {
+ bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
+ if (isBucketLargeEnough && !mCurrentBucketIsInvalid) {
// The current bucket is large enough to keep.
for (const auto& slice : mCurrentSlicedBucket) {
- ValueBucket bucket;
- bucket.mBucketStartNs = mCurrentBucketStartTimeNs;
- bucket.mBucketEndNs = bucketEndTime;
- for (const auto& interval : slice.second) {
- if (interval.hasValue) {
- // skip the output if the diff is zero
- if (mSkipZeroDiffOutput && mUseDiff && interval.value.isZero()) {
- continue;
- }
- bucket.valueIndex.push_back(interval.valueIndex);
- if (mAggregationType != ValueMetric::AVG) {
- bucket.values.push_back(interval.value);
- } else {
- double sum = interval.value.type == LONG ? (double)interval.value.long_value
- : interval.value.double_value;
- bucket.values.push_back(Value((double)sum / interval.sampleSize));
- }
- }
- }
+ ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
// it will auto create new vector of ValuebucketInfo if the key is not found.
if (bucket.valueIndex.size() > 0) {
auto& bucketList = mPastBuckets[slice.first];
@@ -714,6 +717,58 @@
mSkippedBuckets.emplace_back(mCurrentBucketStartTimeNs, bucketEndTime);
}
+ if (!mCurrentBucketIsInvalid) {
+ appendToFullBucket(eventTimeNs, fullBucketEndTimeNs);
+ }
+ initCurrentSlicedBucket();
+ mCurrentBucketIsInvalid = false;
+}
+
+ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
+ const std::vector<Interval>& intervals) {
+ ValueBucket bucket;
+ bucket.mBucketStartNs = mCurrentBucketStartTimeNs;
+ bucket.mBucketEndNs = bucketEndTime;
+ for (const auto& interval : intervals) {
+ if (interval.hasValue) {
+ // skip the output if the diff is zero
+ if (mSkipZeroDiffOutput && mUseDiff && interval.value.isZero()) {
+ continue;
+ }
+ bucket.valueIndex.push_back(interval.valueIndex);
+ if (mAggregationType != ValueMetric::AVG) {
+ bucket.values.push_back(interval.value);
+ } else {
+ double sum = interval.value.type == LONG ? (double)interval.value.long_value
+ : interval.value.double_value;
+ bucket.values.push_back(Value((double)sum / interval.sampleSize));
+ }
+ }
+ }
+ return bucket;
+}
+
+void ValueMetricProducer::initCurrentSlicedBucket() {
+ for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
+ bool obsolete = true;
+ for (auto& interval : it->second) {
+ interval.hasValue = false;
+ interval.sampleSize = 0;
+ if (interval.seenNewData) {
+ obsolete = false;
+ }
+ interval.seenNewData = false;
+ }
+
+ if (obsolete) {
+ it = mCurrentSlicedBucket.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) {
if (eventTimeNs > fullBucketEndTimeNs) { // If full bucket, send to anomaly tracker.
// Accumulate partial buckets with current value and then send to anomaly tracker.
if (mCurrentFullBucket.size() > 0) {
@@ -751,24 +806,6 @@
mCurrentFullBucket[slice.first] += slice.second[0].value.long_value;
}
}
-
- for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
- bool obsolete = true;
- for (auto& interval : it->second) {
- interval.hasValue = false;
- interval.sampleSize = 0;
- if (interval.seenNewData) {
- obsolete = false;
- }
- interval.seenNewData = false;
- }
-
- if (obsolete) {
- it = mCurrentSlicedBucket.erase(it);
- } else {
- it++;
- }
- }
}
size_t ValueMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index a8dfc5b..40cf2e1 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -51,7 +51,8 @@
virtual ~ValueMetricProducer();
// Process data pulled on bucket boundary.
- void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
+ void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data,
+ bool pullSuccess) override;
// ValueMetric needs special logic if it's a pulled atom.
void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
@@ -102,6 +103,9 @@
// Calculate previous bucket end time based on current time.
int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs);
+ // Mark the data as invalid.
+ void invalidateCurrentBucket();
+
const int mWhatMatcherIndex;
sp<EventMatcherWizard> mEventMatcherWizard;
@@ -155,6 +159,11 @@
void pullAndMatchEventsLocked(const int64_t timestampNs);
+ ValueBucket buildPartialBucket(int64_t bucketEndTime,
+ const std::vector<Interval>& intervals);
+ void initCurrentSlicedBucket();
+ void appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs);
+
// Reset diff base and mHasGlobalBase
void resetBase();
@@ -186,6 +195,12 @@
// diff against.
bool mHasGlobalBase;
+ // Invalid bucket. There was a problem in collecting data in the current bucket so we cannot
+ // trust any of the data in this bucket.
+ //
+ // For instance, one pull failed.
+ bool mCurrentBucketIsInvalid;
+
const int64_t mMaxPullDelayNs;
const bool mSplitBucketForAppUpgrade;
@@ -216,8 +231,15 @@
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFail);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket);
FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
+ 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 cca09ac..73aab48 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -417,6 +417,8 @@
optional int64 skipped_forward_buckets = 4;
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 9c9985e..f76a9ad 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -78,6 +78,8 @@
const int FIELD_ID_SKIPPED_FORWARD_BUCKETS = 4;
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 {
@@ -494,6 +496,10 @@
(long long)pair.second.badValueType);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET,
(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/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 0ffbb54..1725160 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -133,7 +133,7 @@
event->init();
allData.push_back(event);
- gaugeProducer.onDataPulled(allData);
+ gaugeProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
EXPECT_EQ(INT, it->mValue.getType());
@@ -151,7 +151,7 @@
event2->write(25);
event2->init();
allData.push_back(event2);
- gaugeProducer.onDataPulled(allData);
+ gaugeProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
EXPECT_EQ(INT, it->mValue.getType());
@@ -305,7 +305,7 @@
event->write(1);
event->init();
allData.push_back(event);
- gaugeProducer.onDataPulled(allData);
+ gaugeProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
@@ -328,7 +328,7 @@
event->write(3);
event->init();
allData.push_back(event);
- gaugeProducer.onDataPulled(allData);
+ gaugeProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin()
@@ -371,7 +371,7 @@
event->write(1);
event->init();
allData.push_back(event);
- gaugeProducer.onDataPulled(allData);
+ gaugeProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
@@ -440,7 +440,7 @@
event->write(110);
event->init();
allData.push_back(event);
- gaugeProducer.onDataPulled(allData);
+ gaugeProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()
@@ -541,7 +541,7 @@
event->write(110);
event->init();
allData.push_back(event);
- gaugeProducer.onDataPulled(allData);
+ gaugeProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
@@ -590,7 +590,7 @@
event1->write(13);
event1->init();
- gaugeProducer.onDataPulled({event1});
+ gaugeProducer.onDataPulled({event1}, /** succeed */ true);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
@@ -604,7 +604,7 @@
event2->write(15);
event2->init();
- gaugeProducer.onDataPulled({event2});
+ gaugeProducer.onDataPulled({event2}, /** succeed */ true);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
@@ -619,7 +619,7 @@
event3->write(26);
event3->init();
- gaugeProducer.onDataPulled({event3});
+ gaugeProducer.onDataPulled({event3}, /** succeed */ true);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
@@ -633,7 +633,7 @@
std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10);
event4->write("some value");
event4->init();
- gaugeProducer.onDataPulled({event4});
+ gaugeProducer.onDataPulled({event4}, /** succeed */ true);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty());
}
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index c0648ee..2979346 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -160,7 +160,7 @@
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -177,7 +177,7 @@
event->write(23);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -196,7 +196,7 @@
event->write(36);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -256,7 +256,7 @@
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
@@ -274,7 +274,7 @@
event->write(23);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -292,7 +292,7 @@
event->write(36);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -341,7 +341,7 @@
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -357,7 +357,7 @@
event->write(10);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -373,7 +373,7 @@
event->write(36);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
@@ -420,7 +420,7 @@
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -436,7 +436,7 @@
event->write(10);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -451,7 +451,7 @@
event->write(36);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
@@ -525,7 +525,7 @@
event->write(110);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
@@ -635,7 +635,7 @@
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
@@ -650,7 +650,7 @@
event->write(150);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(20L,
@@ -689,7 +689,7 @@
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
@@ -993,7 +993,7 @@
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -1011,7 +1011,7 @@
event->write(23);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -1032,7 +1032,7 @@
event->write(36);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
// startUpdated:false sum:12
@@ -1120,7 +1120,7 @@
event->write(110);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(false, curInterval.hasBase);
@@ -1224,7 +1224,7 @@
event->write(110);
event->init();
allData.push_back(event);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
@@ -1677,7 +1677,7 @@
allData.push_back(event1);
allData.push_back(event2);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval1.hasBase);
EXPECT_EQ(11, interval1.base.long_value);
@@ -1762,7 +1762,7 @@
allData.push_back(event1);
allData.push_back(event2);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval1.hasBase);
EXPECT_EQ(11, interval1.base.long_value);
@@ -1791,7 +1791,7 @@
event1->write(5);
event1->init();
allData.push_back(event1);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval2.hasBase);
@@ -1813,7 +1813,7 @@
event2->write(5);
event2->init();
allData.push_back(event2);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval2.hasBase);
@@ -1888,7 +1888,7 @@
allData.push_back(event1);
allData.push_back(event2);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval1.hasBase);
EXPECT_EQ(11, interval1.base.long_value);
@@ -1918,7 +1918,7 @@
event1->write(5);
event1->init();
allData.push_back(event1);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
@@ -1941,7 +1941,7 @@
event1->write(13);
event1->init();
allData.push_back(event1);
- valueProducer.onDataPulled(allData);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
EXPECT_EQ(true, interval2.hasBase);
@@ -1951,7 +1951,60 @@
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
}
-TEST(ValueMetricProducerTest, TestResetBaseOnPullFail) {
+TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
+ 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());
+
+ // Used by onConditionChanged.
+ 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 + 8);
+ 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.onConditionChanged(true, bucketStartTimeNs + 8);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval& curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasBase);
+ EXPECT_EQ(100, curInterval.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+
+ vector<shared_ptr<LogEvent>> allData;
+ valueProducer.onDataPulled(allData, /** succeed */ false);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(false, curInterval.hasBase);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+}
+
+TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
ValueMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -2007,7 +2060,57 @@
EXPECT_EQ(false, valueProducer.mHasGlobalBase);
}
-TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
+TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
+ 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 + 8);
+ 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 = true;
+
+ vector<shared_ptr<LogEvent>> allData;
+ valueProducer.onDataPulled(allData, /** succeed */ false);
+ EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
+
+ valueProducer.onConditionChanged(false, bucketStartTimeNs + 1);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval& curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(false, curInterval.hasBase);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+}
+
+TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
ValueMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -2030,7 +2133,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();
@@ -2042,34 +2145,334 @@
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);
-
- // 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);
+TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) {
+ 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);
- // has one slice
+ 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, _))
+ // First onConditionChanged
+ .WillOnce(Return(false))
+ // Second onConditionChanged
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
+ event->write(tagId);
+ event->write(130);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ valueProducer.mCondition = true;
+
+ // Bucket start.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+ event->write(1);
+ event->write(110);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
+
+ // This will fail and should invalidate the whole bucket since we do not have all the data
+ // needed to compute the metric value when the screen was on.
+ valueProducer.onConditionChanged(false, bucketStartTimeNs + 2);
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 3);
+
+ // Bucket end.
+ allData.clear();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event2->write(1);
+ event2->write(140);
+ event2->init();
+ allData.push_back(event2);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
+
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1);
+
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ // Contains base from last pull which was successful.
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval& curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasBase);
+ EXPECT_EQ(140, curInterval.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+}
+
+TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) {
+ 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, _))
+ // First onConditionChanged
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
+ event->write(tagId);
+ event->write(120);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ // Second onConditionChanged
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
+ event->write(tagId);
+ event->write(130);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ valueProducer.mCondition = true;
+
+ // Bucket start.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+ event->write(1);
+ event->write(110);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData, /** succeed */ false);
+
+ valueProducer.onConditionChanged(false, bucketStartTimeNs + 2);
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 3);
+
+ // Bucket end.
+ allData.clear();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event2->write(1);
+ event2->write(140);
+ event2->init();
+ allData.push_back(event2);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
+
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1);
+
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ // Contains base from last pull which was successful.
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval& curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasBase);
+ EXPECT_EQ(140, curInterval.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+}
+
+TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) {
+ 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, _))
+ // First onConditionChanged
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
+ event->write(tagId);
+ event->write(120);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ // Second onConditionChanged
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
+ event->write(tagId);
+ event->write(130);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ valueProducer.mCondition = true;
+
+ // Bucket start.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+ event->write(1);
+ event->write(110);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData, /** succeed */ true);
+
+ // This will fail and should invalidate the whole bucket since we do not have all the data
+ // needed to compute the metric value when the screen was on.
+ valueProducer.onConditionChanged(false, bucketStartTimeNs + 2);
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 3);
+
+ // Bucket end.
+ allData.clear();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event2->write(1);
+ event2->write(140);
+ event2->init();
+ allData.push_back(event2);
+ valueProducer.onDataPulled(allData, /** succeed */ false);
+
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1);
+
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ // Last pull failed so based has been reset.
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval& curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(false, curInterval.hasBase);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(false, valueProducer.mHasGlobalBase);
}
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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ea145f0..64b94a9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -189,9 +189,19 @@
/**
* Special mode that means "allow only when app is in foreground." This is <b>not</b>
- * returned from {@link #checkOp}, {@link #noteOp}, {@link #startOp}; rather, when this
- * mode is set, these functions will return {@link #MODE_ALLOWED} when the app being
- * checked is currently in the foreground, otherwise {@link #MODE_IGNORED}.
+ * returned from {@link #unsafeCheckOp}, {@link #noteOp}, {@link #startOp}. Rather,
+ * {@link #unsafeCheckOp} will always return {@link #MODE_ALLOWED} (because it is always
+ * possible for it to be ultimately allowed, depending on the app's background state),
+ * and {@link #noteOp} and {@link #startOp} will return {@link #MODE_ALLOWED} when the app
+ * being checked is currently in the foreground, otherwise {@link #MODE_IGNORED}.
+ *
+ * <p>The only place you will this normally see this value is through
+ * {@link #unsafeCheckOpRaw}, which returns the actual raw mode of the op. Note that because
+ * you can't know the current state of the app being checked (and it can change at any
+ * point), you can only treat the result here as an indication that it will vary between
+ * {@link #MODE_ALLOWED} and {@link #MODE_IGNORED} depending on changes in the background
+ * state of the app. You thus must always use {@link #noteOp} or {@link #startOp} to do
+ * the actual check for access to the op.</p>
*/
public static final int MODE_FOREGROUND = 4;
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/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index a6f6d06..868fbfe 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -751,6 +751,47 @@
}
/**
+ * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
+ * serial number of the its ancestral work profile or {@code null} if there is none.
+ *
+ * <p> The ancestral serial number will have a corresponding {@link UserHandle} if the device
+ * has a work profile that was restored from another work profile with serial number
+ * {@code ancestralSerialNumber}.
+ *
+ * @see UserManager#getSerialNumberForUser(UserHandle)
+ */
+ @Nullable
+ public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
+ if (sService != null) {
+ try {
+ return sService.getUserForAncestralSerialNumber(ancestralSerialNumber);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getUserForAncestralSerialNumber() couldn't connect");
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the ancestral work profile for the calling user.
+ *
+ * <p> The ancestral work profile corresponds to the profile that was used to restore to the
+ * callers profile.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BACKUP)
+ public void setAncestralSerialNumber(long ancestralSerialNumber) {
+ if (sService != null) {
+ try {
+ sService.setAncestralSerialNumber(ancestralSerialNumber);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setAncestralSerialNumber() couldn't connect");
+ }
+ }
+ }
+
+ /**
* Returns an {@link Intent} for the specified transport's configuration UI.
* This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
* Intent, String)}.
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index eda8981..8386c72 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -22,6 +22,7 @@
import android.app.backup.IRestoreSession;
import android.app.backup.ISelectBackupTransportCallback;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
import android.content.Intent;
import android.content.ComponentName;
@@ -685,4 +686,24 @@
* {@link android.app.backup.IBackupManager.cancelBackups} for the calling user id.
*/
void cancelBackups();
+
+ /**
+ * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the serial
+ * number of the it's ancestral work profile.
+ *
+ * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
+ * and it corresponds to the profile that was used to restore to the callers profile.
+ */
+ UserHandle getUserForAncestralSerialNumber(in long ancestralSerialNumber);
+
+ /**
+ * Sets the ancestral work profile for the calling user.
+ *
+ * <p> The ancestral work profile corresponds to the profile that was used to restore to the
+ * callers profile.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ */
+ void setAncestralSerialNumber(in long ancestralSerialNumber);
+
}
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/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 47a4a2d..f1bfe86 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2748,6 +2748,19 @@
}
/**
+ * @see #setIsSyncable(Account, String, int)
+ * @hide
+ */
+ public static void setIsSyncableAsUser(Account account, String authority, int syncable,
+ int userId) {
+ try {
+ getContentService().setIsSyncableAsUser(account, authority, syncable, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the master auto-sync setting that applies to all the providers and accounts.
* If this is false then the per-provider auto-sync setting is ignored.
* <p>This method requires the caller to hold the permission
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 1d02375..9f6e236 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -126,6 +126,7 @@
* @param syncable, >0 denotes syncable, 0 means not syncable, <0 means unknown
*/
void setIsSyncable(in Account account, String providerName, int syncable);
+ void setIsSyncableAsUser(in Account account, String providerName, int syncable, int userId);
void setMasterSyncAutomatically(boolean flag);
void setMasterSyncAutomaticallyAsUser(boolean flag, int userId);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 6044914..81ed110 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -5260,6 +5260,10 @@
com.android.internal.R.styleable.AndroidManifestProvider_grantUriPermissions,
false);
+ p.info.forceUriPermissions = sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestProvider_forceUriPermissions,
+ false);
+
p.info.multiprocess = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestProvider_multiprocess,
false);
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index 379b783..f06a628 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -47,7 +47,13 @@
* grantUriPermissions} attribute.
*/
public boolean grantUriPermissions = false;
-
+
+ /** If true, always apply URI permission grants, as per the
+ * {@link android.R.styleable#AndroidManifestProvider_forceUriPermissions
+ * forceUriPermissions} attribute.
+ */
+ public boolean forceUriPermissions = false;
+
/**
* If non-null, these are the patterns that are allowed for granting URI
* permissions. Any URI that does not match one of these patterns will not
@@ -112,6 +118,7 @@
readPermission = orig.readPermission;
writePermission = orig.writePermission;
grantUriPermissions = orig.grantUriPermissions;
+ forceUriPermissions = orig.forceUriPermissions;
uriPermissionPatterns = orig.uriPermissionPatterns;
pathPermissions = orig.pathPermissions;
multiprocess = orig.multiprocess;
@@ -142,6 +149,7 @@
out.writeString(readPermission);
out.writeString(writePermission);
out.writeInt(grantUriPermissions ? 1 : 0);
+ out.writeInt(forceUriPermissions ? 1 : 0);
out.writeTypedArray(uriPermissionPatterns, parcelableFlags);
out.writeTypedArray(pathPermissions, parcelableFlags);
out.writeInt(multiprocess ? 1 : 0);
@@ -171,6 +179,7 @@
readPermission = in.readString();
writePermission = in.readString();
grantUriPermissions = in.readInt() != 0;
+ forceUriPermissions = in.readInt() != 0;
uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
pathPermissions = in.createTypedArray(PathPermission.CREATOR);
multiprocess = in.readInt() != 0;
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/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 866ea16..2aca55a 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -68,6 +68,7 @@
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -1889,7 +1890,8 @@
* @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
* changes. Must be extended by applications that use this API.
*
- * @return A {@link SocketKeepalive} object, which can be used to control this keepalive object.
+ * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+ * given socket.
**/
public SocketKeepalive createSocketKeepalive(@NonNull Network network,
@NonNull UdpEncapsulationSocket socket,
@@ -1918,6 +1920,8 @@
* @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
* changes. Must be extended by applications that use this API.
*
+ * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+ * given socket.
* @hide
*/
@SystemApi
@@ -1933,6 +1937,34 @@
}
/**
+ * Request that keepalives be started on a TCP socket.
+ * The socket must be established.
+ *
+ * @param network The {@link Network} the socket is on.
+ * @param socket The socket that needs to be kept alive.
+ * @param executor The executor on which callback will be invoked. This implementation assumes
+ * the provided {@link Executor} runs the callbacks in sequence with no
+ * concurrency. Failing this, no guarantee of correctness can be made. It is
+ * the responsibility of the caller to ensure the executor provides this
+ * guarantee. A simple way of creating such an executor is with the standard
+ * tool {@code Executors.newSingleThreadExecutor}.
+ * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
+ * changes. Must be extended by applications that use this API.
+ *
+ * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+ * given socket.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD)
+ public SocketKeepalive createSocketKeepalive(@NonNull Network network,
+ @NonNull Socket socket,
+ @NonNull Executor executor,
+ @NonNull Callback callback) {
+ return new TcpSocketKeepalive(mService, network, socket, executor, callback);
+ }
+
+ /**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface. An attempt to add a route that
* already exists is ignored, but treated as successful.
@@ -3891,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 1148ac1..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);
@@ -188,6 +190,9 @@
int intervalSeconds, in Messenger messenger, in IBinder binder, String srcAddr,
String dstAddr);
+ void startTcpKeepalive(in Network network, in FileDescriptor fd, int intervalSeconds,
+ in Messenger messenger, in IBinder binder);
+
void stopKeepalive(in Network network, int slot);
String getCaptivePortalServerUrl();
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index c3783783..273f8cd 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -178,6 +178,26 @@
*/
public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13;
+ // TODO: move the above 2 constants down so they are in order once merge conflicts are resolved
+ /**
+ * Sent by the KeepaliveTracker to NetworkAgent to add a packet filter.
+ *
+ * For TCP keepalive offloads, keepalive packets are sent by the firmware. However, because the
+ * remote site will send ACK packets in response to the keepalive packets, the firmware also
+ * needs to be configured to properly filter the ACKs to prevent the system from waking up.
+ * This does not happen with UDP, so this message is TCP-specific.
+ * arg1 = slot number of the keepalive to filter for.
+ * obj = the keepalive packet to send repeatedly.
+ */
+ public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16;
+
+ /**
+ * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
+ * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
+ * arg1 = slot number of the keepalive packet filter to remove.
+ */
+ public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
+
/**
* Sent by ConnectivityService to inform this network transport of signal strength thresholds
* that when crossed should trigger a system wakeup and a NetworkCapabilities update.
@@ -329,6 +349,14 @@
preventAutomaticReconnect();
break;
}
+ case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
+ addKeepalivePacketFilter(msg);
+ break;
+ }
+ case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
+ removeKeepalivePacketFilter(msg);
+ break;
+ }
}
}
@@ -478,6 +506,24 @@
}
/**
+ * Called by ConnectivityService to add specific packet filter to network hardware to block
+ * ACKs matching the sent keepalive packets. Implementations that support this feature must
+ * override this method.
+ */
+ protected void addKeepalivePacketFilter(Message msg) {
+ onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+ }
+
+ /**
+ * Called by ConnectivityService to remove a packet filter installed with
+ * {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature
+ * must override this method.
+ */
+ protected void removeKeepalivePacketFilter(Message msg) {
+ onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+ }
+
+ /**
* Called by ConnectivityService to inform this network transport of signal strength thresholds
* that when crossed should trigger a system wakeup and a NetworkCapabilities update.
*/
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index 7ea1bef..07728be 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -155,7 +156,7 @@
@NonNull private final SocketKeepalive.Callback mCallback;
@NonNull private final Looper mLooper;
@NonNull final Messenger mMessenger;
- @NonNull Integer mSlot;
+ @Nullable Integer mSlot;
SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
@NonNull Executor executor, @NonNull Callback callback) {
diff --git a/core/java/android/net/TcpSocketKeepalive.java b/core/java/android/net/TcpSocketKeepalive.java
new file mode 100644
index 0000000..8f6ee7b
--- /dev/null
+++ b/core/java/android/net/TcpSocketKeepalive.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.net.Socket;
+import java.util.concurrent.Executor;
+
+/** @hide */
+final class TcpSocketKeepalive extends SocketKeepalive {
+
+ private final Socket mSocket;
+
+ TcpSocketKeepalive(@NonNull IConnectivityManager service,
+ @NonNull Network network,
+ @NonNull Socket socket,
+ @NonNull Executor executor,
+ @NonNull Callback callback) {
+ super(service, network, executor, callback);
+ mSocket = socket;
+ }
+
+ /**
+ * Starts keepalives. {@code mSocket} must be a connected TCP socket.
+ *
+ * - The application must not write to or read from the socket after calling this method, until
+ * onDataReceived, onStopped, or onError are called. If it does, the keepalive will fail
+ * with {@link #ERROR_SOCKET_NOT_IDLE}, or {@code #ERROR_INVALID_SOCKET} if the socket
+ * experienced an error (as in poll(2) returned POLLERR); if this happens, the data received
+ * from the socket may be invalid, and the socket can't be recovered.
+ * - If the socket has data in the send or receive buffer, then this call will fail with
+ * {@link #ERROR_SOCKET_NOT_IDLE} and can be retried after the data has been processed.
+ * An app could ensure this by using an application-layer protocol where it can receive
+ * acknowledgement that it will go into keepalive mode. It could then go into keepalive
+ * mode after having read the acknowledgement, draining the socket.
+ */
+ @Override
+ void startImpl(int intervalSec) {
+ try {
+ final FileDescriptor fd = mSocket.getFileDescriptor$();
+ mService.startTcpKeepalive(mNetwork, fd, intervalSec, mMessenger, new Binder());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error starting packet keepalive: ", e);
+ stopLooper();
+ }
+ }
+
+ @Override
+ void stopImpl() {
+ try {
+ if (mSlot != null) {
+ mService.stopKeepalive(mNetwork, mSlot);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error stopping packet keepalive: ", e);
+ stopLooper();
+ }
+ }
+}
diff --git a/core/java/android/net/ip/IIpClient.aidl b/core/java/android/net/ip/IIpClient.aidl
index 7769ec2..a4a80e1 100644
--- a/core/java/android/net/ip/IIpClient.aidl
+++ b/core/java/android/net/ip/IIpClient.aidl
@@ -17,6 +17,7 @@
import android.net.ProxyInfoParcelable;
import android.net.ProvisioningConfigurationParcelable;
+import android.net.TcpKeepalivePacketDataParcelable;
/** @hide */
oneway interface IIpClient {
@@ -29,4 +30,6 @@
void setTcpBufferSizes(in String tcpBufferSizes);
void setHttpProxy(in ProxyInfoParcelable proxyInfo);
void setMulticastFilter(boolean enabled);
+ void addKeepalivePacketFilter(int slot, in TcpKeepalivePacketDataParcelable pkt);
+ void removeKeepalivePacketFilter(int slot);
}
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/Build.java b/core/java/android/os/Build.java
index 2d61a4e..83a7654 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1120,11 +1120,9 @@
/** The name identifying the system partition. */
public static final String PARTITION_NAME_SYSTEM = "system";
- private String mName;
- private String mFingerprint;
- private long mTimeMs;
-
- public Partition() {}
+ private final String mName;
+ private final String mFingerprint;
+ private final long mTimeMs;
private Partition(String name, String fingerprint, long timeMs) {
mName = name;
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/os/UserManager.java b/core/java/android/os/UserManager.java
index 0a60764..e2b5730 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -734,10 +734,13 @@
public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
/**
- * Specifies if what is copied in the clipboard of this profile can
- * be pasted in related profiles. Does not restrict if the clipboard of related profiles can be
- * pasted in this profile.
- * The default value is <code>false</code>.
+ * Specifies if the clipboard contents can be exported by pasting the data into other users or
+ * profiles. This restriction doesn't prevent import, such as someone pasting clipboard data
+ * from other profiles or users. The default value is {@code false}.
+ *
+ * <p><strong>Note</strong>: Because it's possible to extract data from screenshots using
+ * optical character recognition (OCR), we strongly recommend combining this user restriction
+ * with {@link DevicePolicyManager#setScreenCaptureDisabled(ComponentName, boolean)}.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
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/app/SmsAppService.java b/core/java/android/service/carrier/CarrierMessagingClientService.java
similarity index 67%
rename from core/java/android/app/SmsAppService.java
rename to core/java/android/service/carrier/CarrierMessagingClientService.java
index 3829d71..13f4fc4 100644
--- a/core/java/android/app/SmsAppService.java
+++ b/core/java/android/service/carrier/CarrierMessagingClientService.java
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app;
+package android.service.carrier;
+import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
@@ -24,10 +25,11 @@
* it so that the process is always running, which allows the app to have a persistent connection
* to the server.
*
- * <p>The service must have an {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE}
+ * <p>The service must have an
+ * {@link android.telephony.TelephonyManager#ACTION_CARRIER_MESSAGING_CLIENT_SERVICE}
* action in the intent handler, and be protected with
- * {@link android.Manifest.permission#BIND_SMS_APP_SERVICE}. However the service does not have to
- * be exported.
+ * {@link android.Manifest.permission#BIND_CARRIER_MESSAGING_CLIENT_SERVICE}.
+ * However the service does not have to be exported.
*
* <p>The service must be associated with a non-main process, meaning it must have an
* {@code android:process} tag in its manifest entry.
@@ -45,27 +47,27 @@
*
* <p>Example: First, define a subclass in the application:
* <pre>
- * public class MySmsAppService extends SmsAppService {
+ * public class MyCarrierMessagingClientService extends CarrierMessagingClientService {
* }
* </pre>
* Then, declare it in its {@code AndroidManifest.xml}:
* <pre>
* <service
- * android:name=".MySmsAppService"
+ * android:name=".MyCarrierMessagingClientService"
* android:exported="false"
* android:process=":persistent"
- * android:permission="android.permission.BIND_SMS_APP_SERVICE">
+ * android:permission="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE">
* <intent-filter>
- * <action android:name="android.telephony.action.SMS_APP_SERVICE" />
+ * <action android:name="android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE" />
* </intent-filter>
* </service>
* </pre>
*/
-public class SmsAppService extends Service {
- private final ISmsAppService mImpl;
+public class CarrierMessagingClientService extends Service {
+ private final ICarrierMessagingClientServiceImpl mImpl;
- public SmsAppService() {
- mImpl = new ISmsAppServiceImpl();
+ public CarrierMessagingClientService() {
+ mImpl = new ICarrierMessagingClientServiceImpl();
}
@Override
@@ -73,6 +75,6 @@
return mImpl.asBinder();
}
- private class ISmsAppServiceImpl extends ISmsAppService.Stub {
+ private class ICarrierMessagingClientServiceImpl extends ICarrierMessagingClientService.Stub {
}
}
diff --git a/core/java/android/app/ISmsAppService.aidl b/core/java/android/service/carrier/ICarrierMessagingClientService.aidl
similarity index 88%
rename from core/java/android/app/ISmsAppService.aidl
rename to core/java/android/service/carrier/ICarrierMessagingClientService.aidl
index 1ac2ec6..dbe7d12 100644
--- a/core/java/android/app/ISmsAppService.aidl
+++ b/core/java/android/service/carrier/ICarrierMessagingClientService.aidl
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.app;
+package android.service.carrier;
/**
* @hide
*/
-interface ISmsAppService {
+interface ICarrierMessagingClientService {
}
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/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 03d9955..3e749f4 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -37,7 +37,7 @@
}
/** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
public abstract void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top,
CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx,
CanvasProperty<Float> ry, CanvasProperty<Paint> paint);
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 90110e6..cb8f703 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -103,6 +103,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;
@@ -4258,47 +4259,51 @@
/**
* The offset, in pixels, by which the content of this view is scrolled
* horizontally.
+ * Please use {@link View#getScrollX()} and {@link View#setScrollX(int)} instead of
+ * accessing these directly.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "scrolling")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mScrollX;
/**
* The offset, in pixels, by which the content of this view is scrolled
* vertically.
+ * Please use {@link View#getScrollY()} and {@link View#setScrollY(int)} instead of
+ * accessing these directly.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "scrolling")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mScrollY;
/**
- * The left padding in pixels, that is the distance in pixels between the
- * left edge of this view and the left edge of its content.
+ * The final computed left padding in pixels that is used for drawing. This is the distance in
+ * pixels between the left edge of this view and the left edge of its content.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "padding")
@UnsupportedAppUsage
protected int mPaddingLeft = 0;
/**
- * The right padding in pixels, that is the distance in pixels between the
- * right edge of this view and the right edge of its content.
+ * The final computed right padding in pixels that is used for drawing. This is the distance in
+ * pixels between the right edge of this view and the right edge of its content.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "padding")
@UnsupportedAppUsage
protected int mPaddingRight = 0;
/**
- * The top padding in pixels, that is the distance in pixels between the
- * top edge of this view and the top edge of its content.
+ * The final computed top padding in pixels that is used for drawing. This is the distance in
+ * pixels between the top edge of this view and the top edge of its content.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "padding")
@UnsupportedAppUsage
protected int mPaddingTop;
/**
- * The bottom padding in pixels, that is the distance in pixels between the
- * bottom edge of this view and the bottom edge of its content.
+ * The final computed bottom padding in pixels that is used for drawing. This is the distance in
+ * pixels between the bottom edge of this view and the bottom edge of its content.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "padding")
@@ -4350,7 +4355,7 @@
private MatchIdPredicate mMatchIdPredicate;
/**
- * Cache the paddingRight set by the user to append to the scrollbar's size.
+ * The right padding after RTL resolution, but before taking account of scroll bars.
*
* @hide
*/
@@ -4358,7 +4363,7 @@
protected int mUserPaddingRight;
/**
- * Cache the paddingBottom set by the user to append to the scrollbar's size.
+ * The resolved bottom padding before taking account of scroll bars.
*
* @hide
*/
@@ -4366,7 +4371,7 @@
protected int mUserPaddingBottom;
/**
- * Cache the paddingLeft set by the user to append to the scrollbar's size.
+ * The left padding after RTL resolution, but before taking account of scroll bars.
*
* @hide
*/
@@ -4388,14 +4393,16 @@
int mUserPaddingEnd;
/**
- * Cache initial left padding.
+ * The left padding as set by a setter method, a background's padding, or via XML property
+ * resolution. This value is the padding before LTR resolution or taking account of scrollbars.
*
* @hide
*/
int mUserPaddingLeftInitial;
/**
- * Cache initial right padding.
+ * The right padding as set by a setter method, a background's padding, or via XML property
+ * resolution. This value is the padding before LTR resolution or taking account of scrollbars.
*
* @hide
*/
@@ -4407,12 +4414,14 @@
private static final int UNDEFINED_PADDING = Integer.MIN_VALUE;
/**
- * Cache if a left padding has been defined
+ * Cache if a left padding has been defined explicitly via padding, horizontal padding,
+ * or leftPadding in XML, or by setPadding(...) or setRelativePadding(...)
*/
private boolean mLeftPaddingDefined = false;
/**
- * Cache if a right padding has been defined
+ * Cache if a right padding has been defined explicitly via padding, horizontal padding,
+ * or rightPadding in XML, or by setPadding(...) or setRelativePadding(...)
*/
private boolean mRightPaddingDefined = false;
@@ -5321,7 +5330,7 @@
case com.android.internal.R.styleable.View_paddingVertical:
paddingVertical = a.getDimensionPixelSize(attr, -1);
break;
- case com.android.internal.R.styleable.View_paddingLeft:
+ case com.android.internal.R.styleable.View_paddingLeft:
leftPadding = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = leftPadding;
leftPaddingDefined = true;
@@ -5787,7 +5796,7 @@
setOverScrollMode(overScrollMode);
- // Cache start/end user padding as we cannot fully resolve padding here (we dont have yet
+ // Cache start/end user padding as we cannot fully resolve padding here (we don't have yet
// the resolved layout direction). Those cached values will be used later during padding
// resolution.
mUserPaddingStart = startPadding;
@@ -5802,6 +5811,8 @@
mLeftPaddingDefined = leftPaddingDefined;
mRightPaddingDefined = rightPaddingDefined;
+ // Valid paddingHorizontal/paddingVertical beats leftPadding, rightPadding, topPadding,
+ // bottomPadding, and padding set by background. Valid padding beats everything.
if (padding >= 0) {
leftPadding = padding;
topPadding = padding;
@@ -5854,6 +5865,8 @@
}
}
+ // mPaddingTop and mPaddingBottom may have been set by setBackground(Drawable) so must pass
+ // them on if topPadding or bottomPadding are not valid.
internalSetPadding(
mUserPaddingLeftInitial,
topPadding >= 0 ? topPadding : mPaddingTop,
@@ -16183,7 +16196,7 @@
* @return true if the View subclass handles alpha (the return value for onSetAlpha()) and
* the new value for the alpha property is different from the old value
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768435)
boolean setAlphaNoInvalidation(float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mAlpha != alpha) {
@@ -19029,6 +19042,7 @@
jumpDrawablesToCurrentState();
+ AccessibilityNodeIdManager.getInstance().registerViewWithId(this, getAccessibilityViewId());
resetSubtreeAccessibilityStateChanged();
// rebuild, since Outline not maintained while View is detached
@@ -19421,6 +19435,8 @@
if ((mViewFlags & TOOLTIP) == TOOLTIP) {
hideTooltip();
}
+
+ AccessibilityNodeIdManager.getInstance().unregisterViewWithId(getAccessibilityViewId());
}
private void cleanupDraw() {
@@ -23935,24 +23951,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/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index a0ab362..68c0d9e 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -1139,12 +1139,6 @@
boolean hardwareAccelerated = mView.isHardwareAccelerated();
- // alpha requires slightly different treatment than the other (transform) properties.
- // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
- // logic is dependent on how the view handles an internal call to onSetAlpha().
- // We track what kinds of properties are set, and how alpha is handled when it is
- // set, and perform the invalidation steps appropriately.
- boolean alphaHandled = false;
if (!hardwareAccelerated) {
mView.invalidateParentCaches();
}
@@ -1159,11 +1153,7 @@
for (int i = 0; i < count; ++i) {
NameValuesHolder values = valueList.get(i);
float value = values.mFromValue + fraction * values.mDeltaValue;
- if (values.mNameConstant == ALPHA) {
- alphaHandled = mView.setAlphaNoInvalidation(value);
- } else {
- setValue(values.mNameConstant, value);
- }
+ setValue(values.mNameConstant, value);
}
}
if ((propertyMask & TRANSFORM_MASK) != 0) {
@@ -1171,13 +1161,8 @@
mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
}
}
- // invalidate(false) in all cases except if alphaHandled gets set to true
- // via the call to setAlphaNoInvalidation(), above
- if (alphaHandled) {
- mView.invalidate(true);
- } else {
- mView.invalidateViewProperty(false, false);
- }
+
+ mView.invalidateViewProperty(false, false);
if (mUpdateListener != null) {
mUpdateListener.onAnimationUpdate(animation);
}
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/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index b6002681..6b48c65 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -20,6 +20,7 @@
import android.annotation.InterpolatorRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.Px;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -170,12 +171,24 @@
/** Duration of smooth progress animations. */
private static final int PROGRESS_ANIM_DURATION = 80;
- @UnsupportedAppUsage
+ /**
+ * Outside the framework, please use {@link ProgressBar#getMinWidth()} and
+ * {@link ProgressBar#setMinWidth(int)} instead of accessing these directly.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
int mMinWidth;
int mMaxWidth;
- @UnsupportedAppUsage
+ /**
+ * Outside the framework, please use {@link ProgressBar#getMinHeight()} and
+ * {@link ProgressBar#setMinHeight(int)} instead of accessing these directly.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
int mMinHeight;
- @UnsupportedAppUsage
+ /**
+ * Outside the framework, please use {@link ProgressBar#getMaxHeight()} ()} and
+ * {@link ProgressBar#setMaxHeight(int)} (int)} instead of accessing these directly.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
int mMaxHeight;
private int mProgress;
@@ -393,6 +406,74 @@
}
/**
+ * Sets the minimum width the progress bar can have.
+ * @param minWidth the minimum width to be set, in pixels
+ * @attr ref android.R.styleable#ProgressBar_minWidth
+ */
+ public void setMinWidth(@Px int minWidth) {
+ mMinWidth = minWidth;
+ requestLayout();
+ }
+
+ /**
+ * @return the minimum width the progress bar can have, in pixels
+ */
+ @Px public int getMinWidth() {
+ return mMinWidth;
+ }
+
+ /**
+ * Sets the maximum width the progress bar can have.
+ * @param maxWidth the maximum width to be set, in pixels
+ * @attr ref android.R.styleable#ProgressBar_maxWidth
+ */
+ public void setMaxWidth(@Px int maxWidth) {
+ mMaxWidth = maxWidth;
+ requestLayout();
+ }
+
+ /**
+ * @return the maximum width the progress bar can have, in pixels
+ */
+ @Px public int getMaxWidth() {
+ return mMaxWidth;
+ }
+
+ /**
+ * Sets the minimum height the progress bar can have.
+ * @param minHeight the minimum height to be set, in pixels
+ * @attr ref android.R.styleable#ProgressBar_minHeight
+ */
+ public void setMinHeight(@Px int minHeight) {
+ mMinHeight = minHeight;
+ requestLayout();
+ }
+
+ /**
+ * @return the minimum height the progress bar can have, in pixels
+ */
+ @Px public int getMinHeight() {
+ return mMinHeight;
+ }
+
+ /**
+ * Sets the maximum height the progress bar can have.
+ * @param maxHeight the maximum height to be set, in pixels
+ * @attr ref android.R.styleable#ProgressBar_maxHeight
+ */
+ public void setMaxHeight(@Px int maxHeight) {
+ mMaxHeight = maxHeight;
+ requestLayout();
+ }
+
+ /**
+ * @return the maximum height the progress bar can have, in pixels
+ */
+ @Px public int getMaxHeight() {
+ return mMaxHeight;
+ }
+
+ /**
* Returns {@code true} if the target drawable needs to be tileified.
*
* @param dr the drawable to check
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 8626c68..0f4e23d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -435,8 +435,13 @@
private ColorStateList mHintTextColor;
private ColorStateList mLinkTextColor;
@ViewDebug.ExportedProperty(category = "text")
- @UnsupportedAppUsage
+
+ /**
+ * {@link #setTextColor(int)} or {@link #getCurrentTextColor()} should be used instead.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mCurTextColor;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mCurHintTextColor;
private boolean mFreezesText;
@@ -707,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 ee96ae9..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));
}
}
@@ -1884,8 +1901,6 @@
}
if (startType == ChooserListAdapter.TARGET_SERVICE) {
- holder.row.setBackgroundColor(
- getColor(R.color.chooser_service_row_background_color));
int nextStartType = mChooserListAdapter.getPositionTargetType(
getFirstRowPosition(rowPosition + 1));
int serviceSpacing = holder.row.getContext().getResources()
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/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/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 397df56..b04ebec 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -691,6 +691,15 @@
return result;
}
+ public static boolean startsWith(byte[] cur, byte[] val) {
+ if (cur == null || val == null) return false;
+ if (cur.length < val.length) return false;
+ for (int i = 0; i < val.length; i++) {
+ if (cur[i] != val[i]) return false;
+ }
+ return true;
+ }
+
/**
* Returns the first element from the array for which
* condition {@code predicate} is true, or null if there is no such element
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/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
index 1b40492..b4610bd 100644
--- a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
+++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
@@ -26,6 +26,7 @@
import android.content.SyncAdapterType;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
import android.util.Log;
import org.json.JSONArray;
@@ -48,6 +49,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Helper for backing up account sync settings (whether or not a service should be synced). The
@@ -76,15 +78,17 @@
private static final String KEY_AUTHORITY_NAME = "name";
private static final String KEY_AUTHORITY_SYNC_STATE = "syncState";
private static final String KEY_AUTHORITY_SYNC_ENABLED = "syncEnabled";
- private static final String STASH_FILE = Environment.getDataDirectory()
- + "/backup/unadded_account_syncsettings.json";
+ private static final String STASH_FILE = "/backup/unadded_account_syncsettings.json";
private Context mContext;
private AccountManager mAccountManager;
+ private final int mUserId;
- public AccountSyncSettingsBackupHelper(Context context) {
+ public AccountSyncSettingsBackupHelper(Context context, int userId) {
mContext = context;
mAccountManager = AccountManager.get(mContext);
+
+ mUserId = userId;
}
/**
@@ -94,7 +98,7 @@
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput output,
ParcelFileDescriptor newState) {
try {
- JSONObject dataJSON = serializeAccountSyncSettingsToJSON();
+ JSONObject dataJSON = serializeAccountSyncSettingsToJSON(mUserId);
if (DEBUG) {
Log.d(TAG, "Account sync settings JSON: " + dataJSON);
@@ -123,10 +127,9 @@
/**
* Fetch and serialize Account and authority information as a JSON Array.
*/
- private JSONObject serializeAccountSyncSettingsToJSON() throws JSONException {
- Account[] accounts = mAccountManager.getAccounts();
- SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
- mContext.getUserId());
+ private JSONObject serializeAccountSyncSettingsToJSON(int userId) throws JSONException {
+ Account[] accounts = mAccountManager.getAccountsAsUser(userId);
+ SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(userId);
// Create a map of Account types to authorities. Later this will make it easier for us to
// generate our JSON.
@@ -146,7 +149,8 @@
// Generate JSON.
JSONObject backupJSON = new JSONObject();
backupJSON.put(KEY_VERSION, JSON_FORMAT_VERSION);
- backupJSON.put(KEY_MASTER_SYNC_ENABLED, ContentResolver.getMasterSyncAutomatically());
+ backupJSON.put(KEY_MASTER_SYNC_ENABLED, ContentResolver.getMasterSyncAutomaticallyAsUser(
+ userId));
JSONArray accountJSONArray = new JSONArray();
for (Account account : accounts) {
@@ -165,8 +169,9 @@
// Add authorities for this Account type and check whether or not sync is enabled.
JSONArray authoritiesJSONArray = new JSONArray();
for (String authority : authorities) {
- int syncState = ContentResolver.getIsSyncable(account, authority);
- boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority);
+ int syncState = ContentResolver.getIsSyncableAsUser(account, authority, userId);
+ boolean syncEnabled = ContentResolver.getSyncAutomaticallyAsUser(account, authority,
+ userId);
JSONObject authorityJSON = new JSONObject();
authorityJSON.put(KEY_AUTHORITY_NAME, authority);
@@ -254,17 +259,18 @@
boolean masterSyncEnabled = dataJSON.getBoolean(KEY_MASTER_SYNC_ENABLED);
JSONArray accountJSONArray = dataJSON.getJSONArray(KEY_ACCOUNTS);
- boolean currentMasterSyncEnabled = ContentResolver.getMasterSyncAutomatically();
+ boolean currentMasterSyncEnabled = ContentResolver.getMasterSyncAutomaticallyAsUser(
+ mUserId);
if (currentMasterSyncEnabled) {
// Disable master sync to prevent any syncs from running.
- ContentResolver.setMasterSyncAutomatically(false);
+ ContentResolver.setMasterSyncAutomaticallyAsUser(false, mUserId);
}
try {
- restoreFromJsonArray(accountJSONArray);
+ restoreFromJsonArray(accountJSONArray, mUserId);
} finally {
// Set the master sync preference to the value from the backup set.
- ContentResolver.setMasterSyncAutomatically(masterSyncEnabled);
+ ContentResolver.setMasterSyncAutomaticallyAsUser(masterSyncEnabled, mUserId);
}
Log.i(TAG, "Restore successful.");
} catch (IOException | JSONException e) {
@@ -272,9 +278,9 @@
}
}
- private void restoreFromJsonArray(JSONArray accountJSONArray)
+ private void restoreFromJsonArray(JSONArray accountJSONArray, int userId)
throws JSONException {
- HashSet<Account> currentAccounts = getAccounts();
+ Set<Account> currentAccounts = getAccounts(userId);
JSONArray unaddedAccountsJSONArray = new JSONArray();
for (int i = 0; i < accountJSONArray.length(); i++) {
JSONObject accountJSON = (JSONObject) accountJSONArray.get(i);
@@ -292,14 +298,14 @@
// yet won't be restored.
if (currentAccounts.contains(account)) {
if (DEBUG) Log.i(TAG, "Restoring Sync Settings for" + accountName);
- restoreExistingAccountSyncSettingsFromJSON(accountJSON);
+ restoreExistingAccountSyncSettingsFromJSON(accountJSON, userId);
} else {
unaddedAccountsJSONArray.put(accountJSON);
}
}
if (unaddedAccountsJSONArray.length() > 0) {
- try (FileOutputStream fOutput = new FileOutputStream(STASH_FILE)) {
+ try (FileOutputStream fOutput = new FileOutputStream(getStashFile(userId))) {
String jsonString = unaddedAccountsJSONArray.toString();
DataOutputStream out = new DataOutputStream(fOutput);
out.writeUTF(jsonString);
@@ -308,18 +314,20 @@
Log.e(TAG, "unable to write the sync settings to the stash file", ioe);
}
} else {
- File stashFile = new File(STASH_FILE);
- if (stashFile.exists()) stashFile.delete();
+ File stashFile = getStashFile(userId);
+ if (stashFile.exists()) {
+ stashFile.delete();
+ }
}
}
/**
* Restore SyncSettings for all existing accounts from a stashed backup-set
*/
- private void accountAddedInternal() {
+ private void accountAddedInternal(int userId) {
String jsonString;
- try (FileInputStream fIn = new FileInputStream(new File(STASH_FILE))) {
+ try (FileInputStream fIn = new FileInputStream(getStashFile(userId))) {
DataInputStream in = new DataInputStream(fIn);
jsonString = in.readUTF();
} catch (FileNotFoundException fnfe) {
@@ -333,7 +341,7 @@
try {
JSONArray unaddedAccountsJSONArray = new JSONArray(jsonString);
- restoreFromJsonArray(unaddedAccountsJSONArray);
+ restoreFromJsonArray(unaddedAccountsJSONArray, userId);
} catch (JSONException jse) {
// Malformed jsonString
Log.e(TAG, "there was an error with the stashed sync settings", jse);
@@ -343,9 +351,10 @@
/**
* Restore SyncSettings for all existing accounts from a stashed backup-set
*/
- public static void accountAdded(Context context) {
- AccountSyncSettingsBackupHelper helper = new AccountSyncSettingsBackupHelper(context);
- helper.accountAddedInternal();
+ public static void accountAdded(Context context, int userId) {
+ AccountSyncSettingsBackupHelper helper = new AccountSyncSettingsBackupHelper(context,
+ userId);
+ helper.accountAddedInternal(userId);
}
/**
@@ -353,9 +362,9 @@
*
* @return Accounts in a HashSet.
*/
- private HashSet<Account> getAccounts() {
- Account[] accounts = mAccountManager.getAccounts();
- HashSet<Account> accountHashSet = new HashSet<Account>();
+ private Set<Account> getAccounts(int userId) {
+ Account[] accounts = mAccountManager.getAccountsAsUser(userId);
+ Set<Account> accountHashSet = new HashSet<Account>();
for (Account account : accounts) {
accountHashSet.add(account);
}
@@ -391,7 +400,7 @@
* initialization sync, while an adapter that the user had off will be off until the user
* enables it on this device at which point it will get an initialization sync.
*/
- private void restoreExistingAccountSyncSettingsFromJSON(JSONObject accountJSON)
+ private void restoreExistingAccountSyncSettingsFromJSON(JSONObject accountJSON, int userId)
throws JSONException {
// Restore authorities.
JSONArray authorities = accountJSON.getJSONArray(KEY_ACCOUNT_AUTHORITIES);
@@ -406,14 +415,15 @@
int wasSyncable = authority.getInt(KEY_AUTHORITY_SYNC_STATE);
ContentResolver.setSyncAutomaticallyAsUser(
- account, authorityName, wasSyncEnabled, 0 /* user Id */);
+ account, authorityName, wasSyncEnabled, userId);
if (!wasSyncEnabled) {
- ContentResolver.setIsSyncable(
+ ContentResolver.setIsSyncableAsUser(
account,
authorityName,
wasSyncable == 0 ?
- 0 /* not syncable */ : 2 /* syncable but needs initialization */);
+ 0 /* not syncable */ : 2 /* syncable but needs initialization */,
+ userId);
}
}
}
@@ -422,4 +432,10 @@
public void writeNewStateDescription(ParcelFileDescriptor newState) {
}
-}
\ No newline at end of file
+
+ private static File getStashFile(int userId) {
+ File baseDir = userId == UserHandle.USER_SYSTEM ? Environment.getDataDirectory()
+ : Environment.getDataSystemCeDirectory(userId);
+ return new File(baseDir, STASH_FILE);
+ }
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 70798d0..35e8f56 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -81,7 +81,7 @@
private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
private static final Set<String> sEligibleForMultiUser = Sets.newArraySet(
- PERMISSION_HELPER, NOTIFICATION_HELPER);
+ PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER);
private int mUserId = UserHandle.USER_SYSTEM;
@@ -91,7 +91,7 @@
mUserId = user.getIdentifier();
- addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
+ addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this, mUserId));
addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(mUserId));
addHelper(PERMISSION_HELPER, new PermissionBackupHelper(mUserId));
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 c7b528c..60b04cf 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4411,8 +4411,8 @@
<permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE"
android:protectionLevel="signature|privileged" />
- <!-- A subclass of {@link android.app.SmsAppService} must be protected with this permission. -->
- <permission android:name="android.permission.BIND_SMS_APP_SERVICE"
+ <!-- A subclass of {@link android.service.carrier.CarrierMessagingClientService} must be protected with this permission. -->
+ <permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"
android:protectionLevel="signature" />
<!-- @hide Permission that allows configuring appops.
diff --git a/core/res/res/drawable/bottomsheet_background.xml b/core/res/res/drawable/bottomsheet_background.xml
new file mode 100644
index 0000000..bc32ba6
--- /dev/null
+++ b/core/res/res/drawable/bottomsheet_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners
+ android:topLeftRadius="?attr/dialogCornerRadius"
+ android:topRightRadius="?attr/dialogCornerRadius" />
+ <solid android:color="?attr/colorBackgroundFloating" />
+</shape>
diff --git a/core/res/res/drawable/ic_drag_handle.xml b/core/res/res/drawable/ic_drag_handle.xml
new file mode 100644
index 0000000..67ab84d
--- /dev/null
+++ b/core/res/res/drawable/ic_drag_handle.xml
@@ -0,0 +1,23 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M20.0,9.0L4.0,9.0l0.0,2.0l16.0,0.0L20.0,9.0zM4.0,15.0l16.0,0.0l0.0,-2.0L4.0,13.0l0.0,2.0z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index f784661..14a5310 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -25,11 +25,24 @@
android:maxCollapsedHeightSmall="56dp"
android:id="@id/contentPanel">
+
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
- android:background="?attr/colorBackgroundFloating">
+ android:background="@drawable/bottomsheet_background">
+
+ <ImageView
+ android:id="@+id/drag"
+ android:layout_width="48dp"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_drag_handle"
+ android:clickable="true"
+ android:paddingTop="@dimen/chooser_edge_margin_normal"
+ android:tint="?android:attr/textColorSecondary"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true" />
+
<TextView android:id="@+id/profile_button"
android:layout_width="wrap_content"
android:layout_height="48dp"
@@ -41,7 +54,7 @@
android:textAppearance="?attr/textAppearanceButton"
android:textColor="?attr/colorAccent"
android:gravity="center_vertical"
- android:layout_alignParentTop="true"
+ android:layout_below="@id/drag"
android:layout_alignParentRight="true"
android:singleLine="true"/>
@@ -207,7 +220,6 @@
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
android:listSelector="@color/transparent"
android:divider="@null"
android:scrollIndicators="top"
@@ -219,7 +231,7 @@
android:layout_alwaysShow="true"
android:background="?attr/colorBackgroundFloating"
android:text="@string/noApplications"
- android:padding="32dp"
+ android:padding="@dimen/chooser_edge_margin_normal"
android:gravity="center"
android:visibility="gone"/>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 759fc12..881688b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -164,6 +164,15 @@
provider.-->
<attr name="grantUriPermissions" format="boolean" />
+ <!-- If true, the system will always create URI permission grants
+ in the cases where {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}
+ or {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} would apply.
+ This is useful for a content provider that dynamically enforces permissions
+ on calls in to the provider, instead of through the manifest: the system
+ needs to know that it should always apply permission grants, even if it
+ looks like the target of the grant would already have access to the URI. -->
+ <attr name="forceUriPermissions" format="boolean" />
+
<!-- Characterizes the potential risk implied in a permission and
indicates the procedure the system should follow when determining
whether to grant the permission to an application requesting it. {@link
@@ -2199,6 +2208,7 @@
<attr name="readPermission" />
<attr name="writePermission" />
<attr name="grantUriPermissions" />
+ <attr name="forceUriPermissions" />
<attr name="permission" />
<attr name="multiprocess" />
<attr name="initOrder" />
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 16c0744..02fae4a 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -199,8 +199,6 @@
<color name="Red_700">#ffc53929</color>
<color name="Red_800">#ffb93221</color>
- <color name="chooser_service_row_background_color">#fff5f5f5</color>
-
<!-- Status bar color for semi transparent mode. -->
<color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9f4fece..76b589c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -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 e7d8102..f84f1f1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2938,6 +2938,7 @@
<public name="inheritShowWhenLocked" />
<public name="zygotePreloadName" />
<public name="useEmbeddedDex" />
+ <public name="forceUriPermissions" />
</public-group>
<public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6d4b04c..1da9149 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2744,7 +2744,6 @@
<java-symbol type="drawable" name="scroll_indicator_material" />
<java-symbol type="layout" name="chooser_row" />
- <java-symbol type="color" name="chooser_service_row_background_color" />
<java-symbol type="id" name="target_badge" />
<java-symbol type="bool" name="config_supportDoubleTapWake" />
<java-symbol type="drawable" name="ic_perm_device_info" />
@@ -3152,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" />
@@ -3596,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..76c56609 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/ExifInterface.java b/media/java/android/media/ExifInterface.java
index b9088d4..31d2232 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -31,6 +31,8 @@
import android.util.Log;
import android.util.Pair;
+import com.android.internal.util.ArrayUtils;
+
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -395,6 +397,12 @@
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
*/
public static final String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
+ /**
+ * Type is byte[]. See <a href=
+ * "https://en.wikipedia.org/wiki/Extensible_Metadata_Platform">Extensible
+ * Metadata Platform (XMP)</a> for details on contents.
+ */
+ public static final String TAG_XMP = "Xmp";
/**
* Private tags used for pointing the other IFD offsets.
@@ -1012,7 +1020,8 @@
new ExifTag(TAG_RW2_SENSOR_BOTTOM_BORDER, 6, IFD_FORMAT_ULONG),
new ExifTag(TAG_RW2_SENSOR_RIGHT_BORDER, 7, IFD_FORMAT_ULONG),
new ExifTag(TAG_RW2_ISO, 23, IFD_FORMAT_USHORT),
- new ExifTag(TAG_RW2_JPG_FROM_RAW, 46, IFD_FORMAT_UNDEFINED)
+ new ExifTag(TAG_RW2_JPG_FROM_RAW, 46, IFD_FORMAT_UNDEFINED),
+ new ExifTag(TAG_XMP, 700, IFD_FORMAT_BYTE),
};
// Primary image IFD Exif Private tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
@@ -1243,6 +1252,8 @@
private static final Charset ASCII = Charset.forName("US-ASCII");
// Identifier for EXIF APP1 segment in JPEG
private static final byte[] IDENTIFIER_EXIF_APP1 = "Exif\0\0".getBytes(ASCII);
+ // Identifier for XMP APP1 segment in JPEG
+ private static final byte[] IDENTIFIER_XMP_APP1 = "http://ns.adobe.com/xap/1.0/\0".getBytes(ASCII);
// JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with
// the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start
// of frame(baseline DCT) and the image size info exists in its beginning part.
@@ -2046,6 +2057,22 @@
}
/**
+ * Returns the raw bytes for the value of the requested tag inside the image
+ * file, or {@code null} if the tag is not contained.
+ *
+ * @return raw bytes for the value of the requested tag, or {@code null} if
+ * no tag was found.
+ */
+ public @Nullable byte[] getAttributeBytes(@NonNull String tag) {
+ final ExifAttribute attribute = getExifAttribute(tag);
+ if (attribute != null) {
+ return attribute.bytes;
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Stores the latitude and longitude value in a float array. The first element is
* the latitude, and the second element is the longitude. Returns false if the
* Exif tags are not available.
@@ -2432,40 +2459,32 @@
}
switch (marker) {
case MARKER_APP1: {
- if (DEBUG) {
- Log.d(TAG, "MARKER_APP1");
- }
- if (length < 6) {
- // Skip if it's not an EXIF APP1 segment.
- break;
- }
- byte[] identifier = new byte[6];
- if (in.read(identifier) != 6) {
- throw new IOException("Invalid exif");
- }
- bytesRead += 6;
- length -= 6;
- if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
- // Skip if it's not an EXIF APP1 segment.
- break;
- }
- if (length <= 0) {
- throw new IOException("Invalid exif");
- }
- if (DEBUG) {
- Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")");
- }
- // Save offset values for createJpegThumbnailBitmap() function
- mExifOffset = bytesRead;
-
- byte[] bytes = new byte[length];
- if (in.read(bytes) != length) {
- throw new IOException("Invalid exif");
- }
+ final int start = bytesRead;
+ final byte[] bytes = new byte[length];
+ in.readFully(bytes);
bytesRead += length;
length = 0;
- readExifSegment(bytes, imageType);
+ if (ArrayUtils.startsWith(bytes, IDENTIFIER_EXIF_APP1)) {
+ final long offset = start + IDENTIFIER_EXIF_APP1.length;
+ final byte[] value = Arrays.copyOfRange(bytes,
+ IDENTIFIER_EXIF_APP1.length, bytes.length);
+
+ readExifSegment(value, imageType);
+
+ // Save offset values for createJpegThumbnailBitmap() function
+ mExifOffset = (int) offset;
+ } else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) {
+ // See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6
+ final long offset = start + IDENTIFIER_XMP_APP1.length;
+ final byte[] value = Arrays.copyOfRange(bytes,
+ IDENTIFIER_XMP_APP1.length, bytes.length);
+
+ if (getAttribute(TAG_XMP) == null) {
+ mAttributes[IFD_TYPE_PRIMARY].put(TAG_XMP, new ExifAttribute(
+ IFD_FORMAT_BYTE, value.length, offset, value));
+ }
+ }
break;
}
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 4fa7d64..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;
@@ -38,6 +39,7 @@
import android.content.IntentFilter;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
import android.net.ip.IpClient.IpClientCallbacksWrapper;
@@ -55,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;
@@ -149,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.
@@ -285,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
@@ -305,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;
@@ -788,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;
}
@@ -847,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
@@ -980,6 +1124,8 @@
// drop
// if it's IPv4 broadcast:
// drop
+ // if keepalive ack
+ // drop
// pass
if (mMulticastFilter) {
@@ -1023,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);
@@ -1030,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
@@ -1037,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
@@ -1057,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);
@@ -1112,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);
+ }
}
/**
@@ -1489,6 +1655,36 @@
installNewProgramLocked();
}
+ /**
+ * 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 sentKeepalivePacket The attributes of the sent keepalive packet.
+ */
+ 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();
+ }
+
+ /**
+ * Remove keepalive packet filter.
+ *
+ * @param slot The index used to access the filter.
+ */
+ public synchronized void removeKeepalivePacketFilter(int slot) {
+ mKeepaliveAcks.remove(slot);
+ installNewProgramLocked();
+ }
+
static public long counterValue(byte[] data, Counter counter)
throws ArrayIndexOutOfBoundsException {
// Follow the same wrap-around addressing scheme of the interpreter.
@@ -1541,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/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
index 12fe8c5..9e59912 100644
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -23,6 +23,7 @@
import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
@@ -34,6 +35,7 @@
import android.net.ProxyInfo;
import android.net.ProxyInfoParcelable;
import android.net.RouteInfo;
+import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfCapabilities;
import android.net.apf.ApfFilter;
import android.net.dhcp.DhcpClient;
@@ -292,6 +294,8 @@
private static final int EVENT_PROVISIONING_TIMEOUT = 10;
private static final int EVENT_DHCPACTION_TIMEOUT = 11;
private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12;
+ private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13;
+ private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14;
// Internal commands to use instead of trying to call transitionTo() inside
// a given State's enter() method. Calling transitionTo() from enter/exit
@@ -522,6 +526,16 @@
checkNetworkStackCallingPermission();
IpClient.this.setMulticastFilter(enabled);
}
+ @Override
+ public void addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) {
+ checkNetworkStackCallingPermission();
+ IpClient.this.addKeepalivePacketFilter(slot, pkt);
+ }
+ @Override
+ public void removeKeepalivePacketFilter(int slot) {
+ checkNetworkStackCallingPermission();
+ IpClient.this.removeKeepalivePacketFilter(slot);
+ }
}
public String getInterfaceName() {
@@ -644,6 +658,22 @@
}
/**
+ * Called by WifiStateMachine to add keepalive packet filter before setting up
+ * keepalive offload.
+ */
+ public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) {
+ sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt);
+ }
+
+ /**
+ * Called by WifiStateMachine to remove keepalive packet filter after stopping keepalive
+ * offload.
+ */
+ public void removeKeepalivePacketFilter(int slot) {
+ sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, slot, 0 /* Unused */);
+ }
+
+ /**
* Dump logs of this IpClient.
*/
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -1512,6 +1542,23 @@
break;
}
+ case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: {
+ final int slot = msg.arg1;
+ if (mApfFilter != null) {
+ mApfFilter.addKeepalivePacketFilter(slot,
+ (TcpKeepalivePacketDataParcelable) msg.obj);
+ }
+ break;
+ }
+
+ case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: {
+ final int slot = msg.arg1;
+ if (mApfFilter != null) {
+ mApfFilter.removeKeepalivePacketFilter(slot);
+ }
+ break;
+ }
+
case EVENT_DHCPACTION_TIMEOUT:
stopDhcpAction();
break;
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/Android.bp b/packages/NetworkStack/tests/Android.bp
index 45fa2dc..4a09b3e 100644
--- a/packages/NetworkStack/tests/Android.bp
+++ b/packages/NetworkStack/tests/Android.bp
@@ -49,6 +49,7 @@
"libhidlbase",
"libhidltransport",
"libhwbinder",
+ "libjsoncpp",
"liblog",
"liblzma",
"libnativehelper",
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/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/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index 403d928..f664c05 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.systemui.bubbles.BubbleExpandedViewContainer
+<com.android.systemui.bubbles.BubbleExpandedView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -93,4 +93,4 @@
</FrameLayout>
-</com.android.systemui.bubbles.BubbleExpandedViewContainer>
+</com.android.systemui.bubbles.BubbleExpandedView>
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/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 eb95f2b..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;
@@ -70,7 +73,7 @@
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
@Singleton
-public class BubbleController implements BubbleExpandedViewContainer.OnBubbleBlockedListener {
+public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListener {
private static final int MAX_BUBBLES = 5; // TODO: actually enforce this
private static final String TAG = "BubbleController";
@@ -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/bubbles/BubbleExpandedViewContainer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
rename to packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index f08ba19..bf9d7ba 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -51,7 +51,7 @@
/**
* Container for the expanded bubble view, handles rendering the caret and header of the view.
*/
-public class BubbleExpandedViewContainer extends LinearLayout implements View.OnClickListener {
+public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
private static final String TAG = "BubbleExpandedView";
// The triangle pointing to the expanded view
@@ -81,19 +81,19 @@
private OnBubbleBlockedListener mOnBubbleBlockedListener;
- public BubbleExpandedViewContainer(Context context) {
+ public BubbleExpandedView(Context context) {
this(context, null);
}
- public BubbleExpandedViewContainer(Context context, AttributeSet attrs) {
+ public BubbleExpandedView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+ public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr,
+ public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mPm = context.getPackageManager();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index afa9f02..305f866 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -94,7 +94,7 @@
private StackAnimationController mStackAnimationController;
private ExpandedAnimationController mExpandedAnimationController;
- private BubbleExpandedViewContainer mExpandedViewContainer;
+ private BubbleExpandedView mExpandedViewContainer;
private int mBubbleSize;
private int mBubblePadding;
@@ -173,7 +173,7 @@
mBubbleContainer.setClipChildren(false);
addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- mExpandedViewContainer = (BubbleExpandedViewContainer)
+ mExpandedViewContainer = (BubbleExpandedView)
LayoutInflater.from(context).inflate(R.layout.bubble_expanded_view,
this /* parent */, false /* attachToRoot */);
mExpandedViewContainer.setElevation(elevation);
@@ -226,7 +226,7 @@
/**
* Sets the listener to notify when a bubble is blocked.
*/
- public void setOnBlockedListener(BubbleExpandedViewContainer.OnBubbleBlockedListener listener) {
+ public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) {
mExpandedViewContainer.setOnBlockedListener(listener);
}
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/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/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/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/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 14322ec..0dd1ded 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -41,6 +41,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Slog;
import android.util.SparseArray;
@@ -433,8 +434,46 @@
getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
return userBackupManagerService == null
- ? null
- : userBackupManagerService.getConfigurationIntent(transportName);
+ ? null
+ : userBackupManagerService.getConfigurationIntent(transportName);
+ }
+
+ /**
+ * Sets the ancestral work profile for the calling user.
+ *
+ * <p> The ancestral work profile corresponds to the profile that was used to restore to the
+ * callers profile.
+ */
+ public void setAncestralSerialNumber(long ancestralSerialNumber) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(
+ Binder.getCallingUserHandle().getIdentifier(),
+ "setAncestralSerialNumber()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.setAncestralSerialNumber(ancestralSerialNumber);
+ }
+ }
+
+ /**
+ * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
+ * serial number of the its ancestral work profile.
+ *
+ * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
+ * and it corresponds to the profile that was used to restore to the callers profile.
+ */
+ @Nullable
+ public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
+ for (UserHandle handle : mContext.getSystemService(UserManager.class).getUserProfiles()) {
+ UserBackupManagerService userBackupManagerService = getServiceUsers().get(
+ handle.getIdentifier());
+ if (userBackupManagerService != null) {
+ if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) {
+ return handle;
+ }
+ }
+ }
+ return null;
}
/**
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 87872e8..17e9b35 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -760,6 +760,21 @@
}
@Override
+ @Nullable public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
+ if (mService != null) {
+ return mService.getUserForAncestralSerialNumber(ancestralSerialNumber);
+ }
+ return null;
+ }
+
+ @Override
+ public void setAncestralSerialNumber(long ancestralSerialNumber) {
+ if (mService != null) {
+ mService.setAncestralSerialNumber(ancestralSerialNumber);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
int userId = binderGetCallingUserId();
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 8b2c1b9..b2afbc3 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -244,6 +244,8 @@
private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
+ private static final String SERIAL_ID_FILE = "serial_id";
+
private final @UserIdInt int mUserId;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final TransportManager mTransportManager;
@@ -360,6 +362,8 @@
private Set<String> mAncestralPackages = null;
private long mAncestralToken = 0;
private long mCurrentToken = 0;
+ @Nullable private File mAncestralSerialNumberFile;
+
/**
* Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
@@ -2308,6 +2312,55 @@
}
}
+ /**
+ * Sets the work profile serial number of the ancestral work profile.
+ */
+ public void setAncestralSerialNumber(long ancestralSerialNumber) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "setAncestralSerialNumber");
+ Slog.v(TAG, "Setting ancestral work profile id to " + ancestralSerialNumber);
+ try (RandomAccessFile af = getAncestralSerialNumberFile()) {
+ af.writeLong(ancestralSerialNumber);
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to write to work profile serial mapping file:", e);
+ }
+ }
+
+ /**
+ * Returns the work profile serial number of the ancestral device. This will be set by
+ * {@link #setAncestralSerialNumber(long)}. Will return {@code -1} if not set.
+ */
+ public long getAncestralSerialNumber() {
+ try (RandomAccessFile af = getAncestralSerialNumberFile()) {
+ return af.readLong();
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to write to work profile serial number file:", e);
+ return -1;
+ }
+ }
+
+ private RandomAccessFile getAncestralSerialNumberFile() throws FileNotFoundException {
+ if (mAncestralSerialNumberFile == null) {
+ mAncestralSerialNumberFile = new File(
+ UserBackupManagerFiles.getBaseStateDir(getUserId()),
+ SERIAL_ID_FILE);
+ if (!mAncestralSerialNumberFile.exists()) {
+ try {
+ mAncestralSerialNumberFile.createNewFile();
+ } catch (IOException e) {
+ Slog.w(TAG, "serial number mapping file creation failed", e);
+ }
+ }
+ }
+ return new RandomAccessFile(mAncestralSerialNumberFile, "rwd");
+ }
+
+ @VisibleForTesting
+ void setAncestralSerialNumberFile(File ancestralSerialNumberFile) {
+ mAncestralSerialNumberFile = ancestralSerialNumberFile;
+ }
+
+
/** Clear the given package's backup data from the current transport. */
public void clearBackupData(String transportName, String packageName) {
if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
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/core/java/android/app/ISmsAppService.aidl b/services/backup/java/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
similarity index 65%
copy from core/java/android/app/ISmsAppService.aidl
copy to services/backup/java/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
index 1ac2ec6..4aea601 100644
--- a/core/java/android/app/ISmsAppService.aidl
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package android.app;
+package com.android.server.backup.encryption.chunking;
-/**
- * @hide
- */
-interface ISmsAppService {
+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 1cb2c4a..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();
}
@@ -6377,6 +6401,14 @@
}
@Override
+ public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds,
+ Messenger messenger, IBinder binder) {
+ enforceKeepalivePermission();
+ mKeepaliveTracker.startTcpKeepalive(
+ getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, messenger, binder);
+ }
+
+ @Override
public void stopKeepalive(Network network, int slot) {
mHandler.sendMessage(mHandler.obtainMessage(
NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE, slot, SocketKeepalive.SUCCESS, network));
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 94fc552..5932f99 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3495,7 +3495,7 @@
if (!app.killedByAm) {
reportUidInfoMessageLocked(TAG,
"Process " + app.processName + " (pid " + pid + ") has died: "
- + ProcessList.makeOomAdjString(app.setAdj)
+ + ProcessList.makeOomAdjString(app.setAdj, true) + " "
+ ProcessList.makeProcStateString(app.setProcState), app.info.uid);
mAllowLowerMemLevel = true;
} else {
@@ -10029,7 +10029,7 @@
pw.print(" #");
pw.print(index);
pw.print(": ");
- pw.print(ProcessList.makeOomAdjString(proc.setAdj));
+ pw.print(ProcessList.makeOomAdjString(proc.setAdj, false));
pw.print(" ");
pw.print(ProcessList.makeProcStateString(proc.getCurProcState()));
pw.print(" ");
@@ -11334,7 +11334,7 @@
for (int i = list.size() - 1; i >= 0; i--) {
ProcessRecord r = list.get(i).first;
long token = proto.start(fieldId);
- String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
+ String oomAdj = ProcessList.makeOomAdjString(r.setAdj, true);
proto.write(ProcessOomProto.PERSISTENT, r.isPersistent());
proto.write(ProcessOomProto.NUM, (origList.size()-1)-list.get(i).second);
proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
@@ -11434,7 +11434,7 @@
for (int i=list.size()-1; i>=0; i--) {
ProcessRecord r = list.get(i).first;
- String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
+ String oomAdj = ProcessList.makeOomAdjString(r.setAdj, false);
char schedGroup;
switch (r.setSchedGroup) {
case ProcessList.SCHED_GROUP_BACKGROUND:
@@ -12871,7 +12871,7 @@
private void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss,
long memtrack, String name) {
sb.append(" ");
- sb.append(ProcessList.makeOomAdjString(oomAdj));
+ sb.append(ProcessList.makeOomAdjString(oomAdj, false));
sb.append(' ');
sb.append(ProcessList.makeProcStateString(procState));
sb.append(' ');
@@ -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/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f90c0ca..69cf54b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -679,47 +679,65 @@
return totalProcessLimit/2;
}
- private static String buildOomTag(String prefix, String space, int val, int base) {
+ private static String buildOomTag(String prefix, String compactPrefix, String space, int val,
+ int base, boolean compact) {
final int diff = val - base;
if (diff == 0) {
+ if (compact) {
+ return compactPrefix;
+ }
if (space == null) return prefix;
return prefix + space;
}
if (diff < 10) {
- return prefix + "+ " + Integer.toString(diff);
+ return prefix + (compact ? "+" : "+ ") + Integer.toString(diff);
}
return prefix + "+" + Integer.toString(diff);
}
- public static String makeOomAdjString(int setAdj) {
+ public static String makeOomAdjString(int setAdj, boolean compact) {
if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
- return buildOomTag("cch", " ", setAdj, ProcessList.CACHED_APP_MIN_ADJ);
+ return buildOomTag("cch", "cch", " ", setAdj,
+ ProcessList.CACHED_APP_MIN_ADJ, compact);
} else if (setAdj >= ProcessList.SERVICE_B_ADJ) {
- return buildOomTag("svcb ", null, setAdj, ProcessList.SERVICE_B_ADJ);
+ return buildOomTag("svcb ", "svcb", null, setAdj,
+ ProcessList.SERVICE_B_ADJ, compact);
} else if (setAdj >= ProcessList.PREVIOUS_APP_ADJ) {
- return buildOomTag("prev ", null, setAdj, ProcessList.PREVIOUS_APP_ADJ);
+ return buildOomTag("prev ", "prev", null, setAdj,
+ ProcessList.PREVIOUS_APP_ADJ, compact);
} else if (setAdj >= ProcessList.HOME_APP_ADJ) {
- return buildOomTag("home ", null, setAdj, ProcessList.HOME_APP_ADJ);
+ return buildOomTag("home ", "home", null, setAdj,
+ ProcessList.HOME_APP_ADJ, compact);
} else if (setAdj >= ProcessList.SERVICE_ADJ) {
- return buildOomTag("svc ", null, setAdj, ProcessList.SERVICE_ADJ);
+ return buildOomTag("svc ", "svc", null, setAdj,
+ ProcessList.SERVICE_ADJ, compact);
} else if (setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
- return buildOomTag("hvy ", null, setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ);
+ return buildOomTag("hvy ", "hvy", null, setAdj,
+ ProcessList.HEAVY_WEIGHT_APP_ADJ, compact);
} else if (setAdj >= ProcessList.BACKUP_APP_ADJ) {
- return buildOomTag("bkup ", null, setAdj, ProcessList.BACKUP_APP_ADJ);
+ return buildOomTag("bkup ", "bkup", null, setAdj,
+ ProcessList.BACKUP_APP_ADJ, compact);
} else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
- return buildOomTag("prcp ", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ);
+ return buildOomTag("prcp ", "prcp", null, setAdj,
+ ProcessList.PERCEPTIBLE_APP_ADJ, compact);
} else if (setAdj >= ProcessList.VISIBLE_APP_ADJ) {
- return buildOomTag("vis", " ", setAdj, ProcessList.VISIBLE_APP_ADJ);
+ return buildOomTag("vis", "vis", " ", setAdj,
+ ProcessList.VISIBLE_APP_ADJ, compact);
} else if (setAdj >= ProcessList.FOREGROUND_APP_ADJ) {
- return buildOomTag("fore ", null, setAdj, ProcessList.FOREGROUND_APP_ADJ);
+ return buildOomTag("fore ", "fore", null, setAdj,
+ ProcessList.FOREGROUND_APP_ADJ, compact);
} else if (setAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
- return buildOomTag("psvc ", null, setAdj, ProcessList.PERSISTENT_SERVICE_ADJ);
+ return buildOomTag("psvc ", "psvc", null, setAdj,
+ ProcessList.PERSISTENT_SERVICE_ADJ, compact);
} else if (setAdj >= ProcessList.PERSISTENT_PROC_ADJ) {
- return buildOomTag("pers ", null, setAdj, ProcessList.PERSISTENT_PROC_ADJ);
+ return buildOomTag("pers ", "pers", null, setAdj,
+ ProcessList.PERSISTENT_PROC_ADJ, compact);
} else if (setAdj >= ProcessList.SYSTEM_ADJ) {
- return buildOomTag("sys ", null, setAdj, ProcessList.SYSTEM_ADJ);
+ return buildOomTag("sys ", "sys", null, setAdj,
+ ProcessList.SYSTEM_ADJ, compact);
} else if (setAdj >= ProcessList.NATIVE_ADJ) {
- return buildOomTag("ntv ", null, setAdj, ProcessList.NATIVE_ADJ);
+ return buildOomTag("ntv ", "ntv", null, setAdj,
+ ProcessList.NATIVE_ADJ, compact);
} else {
return Integer.toString(setAdj);
}
diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java
index 3131255..0b6a432 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingService.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -47,7 +47,7 @@
import com.android.server.SystemService;
import com.android.server.am.PersistentConnection;
import com.android.server.appbinding.finders.AppServiceFinder;
-import com.android.server.appbinding.finders.SmsAppServiceFinder;
+import com.android.server.appbinding.finders.CarrierMessagingClientServiceFinder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -147,7 +147,7 @@
mIPackageManager = injector.getIPackageManager();
mHandler = BackgroundThread.getHandler();
- mApps.add(new SmsAppServiceFinder(context, this::onAppChanged, mHandler));
+ mApps.add(new CarrierMessagingClientServiceFinder(context, this::onAppChanged, mHandler));
// Initialize with the default value to make it non-null.
mConstants = AppBindingConstants.initializeFromString("");
diff --git a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
similarity index 79%
rename from services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
rename to services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
index fcc28f8..4c5f1a1 100644
--- a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
@@ -19,8 +19,6 @@
import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL;
import android.Manifest.permission;
-import android.app.ISmsAppService;
-import android.app.SmsAppService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -30,6 +28,8 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
+import android.service.carrier.CarrierMessagingClientService;
+import android.service.carrier.ICarrierMessagingClientService;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
@@ -41,10 +41,11 @@
import java.util.function.BiConsumer;
/**
- * Find the SmsAppService service within the default SMS app.
+ * Find the CarrierMessagingClientService service within the default SMS app.
*/
-public class SmsAppServiceFinder extends AppServiceFinder<SmsAppService, ISmsAppService> {
- public SmsAppServiceFinder(Context context,
+public class CarrierMessagingClientServiceFinder
+ extends AppServiceFinder<CarrierMessagingClientService, ICarrierMessagingClientService> {
+ public CarrierMessagingClientServiceFinder(Context context,
BiConsumer<AppServiceFinder, Integer> listener,
Handler callbackHandler) {
super(context, listener, callbackHandler);
@@ -62,23 +63,23 @@
}
@Override
- protected Class<SmsAppService> getServiceClass() {
- return SmsAppService.class;
+ protected Class<CarrierMessagingClientService> getServiceClass() {
+ return CarrierMessagingClientService.class;
}
@Override
- public ISmsAppService asInterface(IBinder obj) {
- return ISmsAppService.Stub.asInterface(obj);
+ public ICarrierMessagingClientService asInterface(IBinder obj) {
+ return ICarrierMessagingClientService.Stub.asInterface(obj);
}
@Override
protected String getServiceAction() {
- return TelephonyManager.ACTION_SMS_APP_SERVICE;
+ return TelephonyManager.ACTION_CARRIER_MESSAGING_CLIENT_SERVICE;
}
@Override
protected String getServicePermission() {
- return permission.BIND_SMS_APP_SERVICE;
+ return permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE;
}
@Override
@@ -121,7 +122,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) {
- mListener.accept(SmsAppServiceFinder.this, getSendingUserId());
+ mListener.accept(CarrierMessagingClientServiceFinder.this, getSendingUserId());
}
}
};
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/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index d872e4d..6cff57d 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -17,6 +17,8 @@
package com.android.server.connectivity;
import static android.net.NattSocketKeepalive.NATT_PORT;
+import static android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER;
+import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER;
import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE;
import static android.net.NetworkAgent.EVENT_SOCKET_KEEPALIVE;
@@ -37,6 +39,9 @@
import android.net.NetworkAgent;
import android.net.NetworkUtils;
import android.net.SocketKeepalive.InvalidPacketException;
+import android.net.SocketKeepalive.InvalidSocketException;
+import android.net.TcpKeepalivePacketData;
+import android.net.TcpKeepalivePacketData.TcpSocketInfo;
import android.net.util.IpUtils;
import android.os.Binder;
import android.os.Handler;
@@ -65,7 +70,7 @@
*
* Provides methods to stop and start keepalive requests, and keeps track of keepalives across all
* networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its
- * methods must be called only from the ConnectivityService handler thread.
+ * handle* methods must be called only from the ConnectivityService handler thread.
*/
public class KeepaliveTracker {
@@ -78,9 +83,12 @@
private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
new HashMap<> ();
private final Handler mConnectivityServiceHandler;
+ @NonNull
+ private final TcpKeepaliveController mTcpController;
public KeepaliveTracker(Handler handler) {
mConnectivityServiceHandler = handler;
+ mTcpController = new TcpKeepaliveController(handler);
}
/**
@@ -96,20 +104,33 @@
private final int mUid;
private final int mPid;
private final NetworkAgentInfo mNai;
+ private final int mType;
+ private final FileDescriptor mFd;
- /** Keepalive slot. A small integer that identifies this keepalive among the ones handled
- * by this network. */
+ public static final int TYPE_NATT = 1;
+ public static final int TYPE_TCP = 2;
+
+ // Keepalive slot. A small integer that identifies this keepalive among the ones handled
+ // by this network.
private int mSlot = NO_KEEPALIVE;
// Packet data.
private final KeepalivePacketData mPacket;
private final int mInterval;
- // Whether the keepalive is started or not.
- public boolean isStarted;
+ // Whether the keepalive is started or not. The initial state is NOT_STARTED.
+ private static final int NOT_STARTED = 1;
+ private static final int STARTING = 2;
+ private static final int STARTED = 3;
+ private int mStartedState = NOT_STARTED;
- public KeepaliveInfo(Messenger messenger, IBinder binder, NetworkAgentInfo nai,
- KeepalivePacketData packet, int interval) {
+ KeepaliveInfo(@NonNull Messenger messenger,
+ @NonNull IBinder binder,
+ @NonNull NetworkAgentInfo nai,
+ @NonNull KeepalivePacketData packet,
+ int interval,
+ int type,
+ @NonNull FileDescriptor fd) {
mMessenger = messenger;
mBinder = binder;
mPid = Binder.getCallingPid();
@@ -118,6 +139,8 @@
mNai = nai;
mPacket = packet;
mInterval = interval;
+ mType = type;
+ mFd = fd;
try {
mBinder.linkToDeath(this, 0);
@@ -130,32 +153,40 @@
return mNai;
}
+ private String startedStateString(final int state) {
+ switch (state) {
+ case NOT_STARTED : return "NOT_STARTED";
+ case STARTING : return "STARTING";
+ case STARTED : return "STARTED";
+ }
+ throw new IllegalArgumentException("Unknown state");
+ }
+
public String toString() {
- return new StringBuffer("KeepaliveInfo [")
- .append(" network=").append(mNai.network)
- .append(" isStarted=").append(isStarted)
- .append(" ")
- .append(IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort))
- .append("->")
- .append(IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort))
- .append(" interval=" + mInterval)
- .append(" packetData=" + HexDump.toHexString(mPacket.getPacket()))
- .append(" uid=").append(mUid).append(" pid=").append(mPid)
- .append(" ]")
- .toString();
+ return "KeepaliveInfo ["
+ + " network=" + mNai.network
+ + " startedState=" + startedStateString(mStartedState)
+ + " "
+ + IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort)
+ + "->"
+ + IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)
+ + " interval=" + mInterval
+ + " uid=" + mUid + " pid=" + mPid
+ + " packetData=" + HexDump.toHexString(mPacket.getPacket())
+ + " ]";
}
/** Sends a message back to the application via its SocketKeepalive.Callback. */
void notifyMessenger(int slot, int err) {
+ if (DBG) {
+ Log.d(TAG, "notify keepalive " + mSlot + " on " + mNai.network + " for " + err);
+ }
KeepaliveTracker.this.notifyMessenger(mMessenger, slot, err);
}
/** Called when the application process is killed. */
public void binderDied() {
- // Not called from ConnectivityService handler thread, so send it a message.
- mConnectivityServiceHandler.obtainMessage(
- NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE,
- mSlot, BINDER_DIED, mNai.network).sendToTarget();
+ stop(BINDER_DIED);
}
void unlinkDeathRecipient() {
@@ -202,7 +233,26 @@
int error = isValid();
if (error == SUCCESS) {
Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
- mNai.asyncChannel.sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
+ switch (mType) {
+ case TYPE_NATT:
+ mNai.asyncChannel
+ .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
+ break;
+ case TYPE_TCP:
+ mTcpController.startSocketMonitor(mFd, this, mSlot);
+ mNai.asyncChannel
+ .sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */,
+ mPacket);
+ // TODO: check result from apf and notify of failure as needed.
+ mNai.asyncChannel
+ .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
+ break;
+ default:
+ Log.wtf(TAG, "Starting keepalive with unknown type: " + mType);
+ handleStopKeepalive(mNai, mSlot, error);
+ return;
+ }
+ mStartedState = STARTING;
} else {
handleStopKeepalive(mNai, mSlot, error);
return;
@@ -216,15 +266,27 @@
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
}
}
- if (isStarted) {
+ if (NOT_STARTED != mStartedState) {
Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name());
- mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
+ if (mType == TYPE_NATT) {
+ mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
+ } else if (mType == TYPE_TCP) {
+ mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
+ mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, mSlot);
+ mTcpController.stopSocketMonitor(mSlot);
+ } else {
+ Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
+ }
}
// TODO: at the moment we unconditionally return failure here. In cases where the
// NetworkAgent is alive, should we ask it to reply, so it can return failure?
notifyMessenger(mSlot, reason);
unlinkDeathRecipient();
}
+
+ void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) {
+ handleStopKeepalive(mNai, mSlot, socketKeepaliveReason);
+ }
}
void notifyMessenger(Messenger messenger, int slot, int err) {
@@ -328,20 +390,38 @@
return;
}
- if (reason == SUCCESS && !ki.isStarted) {
+ // This can be called in a number of situations :
+ // - startedState is STARTING.
+ // - reason is SUCCESS => go to STARTED.
+ // - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive.
+ // - startedState is STARTED.
+ // - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive.
+ // - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive.
+ // The control is not supposed to ever come here if the state is NOT_STARTED. This is
+ // because in NOT_STARTED state, the code will switch to STARTING before sending messages
+ // to start, and the only way to NOT_STARTED is this function, through the edges outlined
+ // above : in all cases, keepalive gets stopped and can't restart without going into
+ // STARTING as messages are ordered. This also depends on the hardware processing the
+ // messages in order.
+ // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an
+ // option.
+ if (reason == SUCCESS && KeepaliveInfo.STARTING == ki.mStartedState) {
// Keepalive successfully started.
if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
- ki.isStarted = true;
+ ki.mStartedState = KeepaliveInfo.STARTED;
ki.notifyMessenger(slot, reason);
} else {
// Keepalive successfully stopped, or error.
- ki.isStarted = false;
+ ki.mStartedState = KeepaliveInfo.NOT_STARTED;
if (reason == SUCCESS) {
+ // The message indicated success stopping : don't call handleStopKeepalive.
if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name());
} else {
+ // The message indicated some error trying to start or during the course of
+ // keepalive : do call handleStopKeepalive.
+ handleStopKeepalive(nai, slot, reason);
if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason);
}
- handleStopKeepalive(nai, slot, reason);
}
}
@@ -379,7 +459,47 @@
notifyMessenger(messenger, NO_KEEPALIVE, e.error);
return;
}
- KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds);
+ KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds,
+ KeepaliveInfo.TYPE_NATT, null);
+ mConnectivityServiceHandler.obtainMessage(
+ NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
+ }
+
+ /**
+ * Called by ConnectivityService to start TCP keepalive on a file descriptor.
+ *
+ * In order to offload keepalive for application correctly, sequence number, ack number and
+ * other fields are needed to form the keepalive packet. Thus, this function synchronously
+ * puts the socket into repair mode to get the necessary information. After the socket has been
+ * put into repair mode, the application cannot access the socket until reverted to normal.
+ *
+ * See {@link android.net.SocketKeepalive}.
+ **/
+ public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
+ @NonNull FileDescriptor fd,
+ int intervalSeconds,
+ @NonNull Messenger messenger,
+ @NonNull IBinder binder) {
+ if (nai == null) {
+ notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK);
+ return;
+ }
+
+ TcpKeepalivePacketData packet = null;
+ try {
+ TcpSocketInfo tsi = TcpKeepaliveController.switchToRepairMode(fd);
+ packet = TcpKeepalivePacketData.tcpKeepalivePacket(tsi);
+ } catch (InvalidPacketException | InvalidSocketException e) {
+ try {
+ TcpKeepaliveController.switchOutOfRepairMode(fd);
+ } catch (ErrnoException e1) {
+ Log.e(TAG, "Couldn't move fd out of repair mode after failure to start keepalive");
+ }
+ notifyMessenger(messenger, NO_KEEPALIVE, e.error);
+ return;
+ }
+ KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds,
+ KeepaliveInfo.TYPE_TCP, fd);
Log.d(TAG, "Created keepalive: " + ki.toString());
mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
}
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
index 640504f..8a9ac23 100644
--- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
@@ -15,7 +15,6 @@
*/
package com.android.server.connectivity;
-import static android.net.NetworkAgent.EVENT_SOCKET_KEEPALIVE;
import static android.net.SocketKeepalive.DATA_RECEIVED;
import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
import static android.net.SocketKeepalive.ERROR_SOCKET_NOT_IDLE;
@@ -31,10 +30,8 @@
import android.net.TcpKeepalivePacketData.TcpSocketInfo;
import android.net.TcpRepairWindow;
import android.os.Handler;
-import android.os.Message;
import android.os.MessageQueue;
import android.os.Messenger;
-import android.os.RemoteException;
import android.system.ErrnoException;
import android.system.Int32Ref;
import android.system.Os;
@@ -42,6 +39,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo;
import java.io.FileDescriptor;
import java.net.InetAddress;
@@ -111,7 +109,7 @@
* tcp/ip information.
*/
// TODO : make this private. It's far too confusing that this gets called from outside
- // at a time that nobody can understand, but the switch out is in this class only.
+ // at a time that nobody can understand.
public static TcpSocketInfo switchToRepairMode(FileDescriptor fd)
throws InvalidSocketException {
if (DBG) Log.i(TAG, "switchToRepairMode to start tcp keepalive : " + fd);
@@ -199,7 +197,13 @@
trw.rcvWndScale);
}
- private static void switchOutOfRepairMode(@NonNull final FileDescriptor fd)
+ /**
+ * Switch the tcp socket out of repair mode.
+ *
+ * @param fd the fd of socket to switch back to normal.
+ */
+ // TODO : make this private.
+ public static void switchOutOfRepairMode(@NonNull final FileDescriptor fd)
throws ErrnoException {
Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_OFF);
}
@@ -212,7 +216,7 @@
* @param slot keepalive slot.
*/
public void startSocketMonitor(@NonNull final FileDescriptor fd,
- @NonNull final Messenger messenger, final int slot) {
+ @NonNull final KeepaliveInfo ki, final int slot) {
synchronized (mListeners) {
if (null != mListeners.get(slot)) {
throw new IllegalArgumentException("This slot is already taken");
@@ -226,31 +230,13 @@
// This can't be called twice because the queue guarantees that once the listener
// is unregistered it can't be called again, even for a message that arrived
// before it was unregistered.
- int result;
- try {
- // First move the socket out of repair mode.
- if (DBG) Log.d(TAG, "Moving socket out of repair mode for event : " + readyFd);
- switchOutOfRepairMode(readyFd);
- result = (0 != (events & EVENT_ERROR)) ? ERROR_INVALID_SOCKET : DATA_RECEIVED;
- } catch (ErrnoException e) {
- // Could not move the socket out of repair mode. Still continue with notifying
- // the client
- Log.e(TAG, "Cannot switch socket out of repair mode", e);
- result = ERROR_INVALID_SOCKET;
+ final int reason;
+ if (0 != (events & EVENT_ERROR)) {
+ reason = ERROR_INVALID_SOCKET;
+ } else {
+ reason = DATA_RECEIVED;
}
- // Prepare and send the message to the receiver.
- final Message message = Message.obtain();
- message.what = EVENT_SOCKET_KEEPALIVE;
- message.arg1 = slot;
- message.arg2 = result;
- try {
- messenger.send(message);
- } catch (RemoteException e) {
- // Remote process died
- }
- synchronized (mListeners) {
- mListeners.remove(slot);
- }
+ ki.onFileDescriptorInitiatedStop(reason);
// The listener returns the new set of events to listen to. Because 0 means no
// event, the listener gets unregistered.
return 0;
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index e268e44..bfa7f9d 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -899,9 +899,20 @@
@Override
public void setIsSyncable(Account account, String providerName, int syncable) {
+ setIsSyncableAsUser(account, providerName, syncable, UserHandle.getCallingUserId());
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void setIsSyncableAsUser(Account account, String providerName, int syncable,
+ int userId) {
if (TextUtils.isEmpty(providerName)) {
throw new IllegalArgumentException("Authority must not be empty");
}
+ enforceCrossUserPermission(userId,
+ "no permission to set the sync settings for user " + userId);
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
@@ -909,7 +920,6 @@
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
- int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 7096477..99e0707 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -3261,7 +3261,7 @@
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Account " + aau.account + " added, checking sync restore data");
}
- AccountSyncSettingsBackupHelper.accountAdded(mContext);
+ AccountSyncSettingsBackupHelper.accountAdded(mContext, syncTargets.userId);
break;
}
}
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index b3a1a06..6a76087 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -115,6 +115,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 +324,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;
}
@@ -871,7 +871,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);
}
}
@@ -1543,7 +1543,7 @@
mDisplayWhiteBalanceTintController.setMatrix(cct);
if (mDisplayWhiteBalanceTintController.isActivated()) {
- applyTint(mDisplayWhiteBalanceTintController, false);
+ mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
return true;
}
return false;
@@ -1603,6 +1603,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 e18da7f..32dc988 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -14613,6 +14613,13 @@
PACKAGE_MIME_TYPE);
enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ // Allow the broadcast to be sent before boot complete.
+ // This is needed when committing the apk part of a staged
+ // session in early boot. The rollback manager registers
+ // its receiver early enough during the boot process that
+ // it will not miss the broadcast.
+ enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, getUser(),
android.Manifest.permission.PACKAGE_ROLLBACK_AGENT,
new BroadcastReceiver() {
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/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/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 1163d39..057b53e 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -128,6 +128,8 @@
private final WatchLogHandler mWatchLogHandler;
+ private IBinder.DeathRecipient mDeathRecipient;
+
public TvInputManagerService(Context context) {
super(context);
@@ -674,6 +676,7 @@
if (sessionToken == userState.mainSessionToken) {
setMainLocked(sessionToken, false, callingUid, userId);
}
+ sessionState.session.asBinder().unlinkToDeath(sessionState, 0);
sessionState.session.release();
}
} catch (RemoteException | SessionNotFoundException e) {
@@ -709,6 +712,7 @@
clientState.sessionTokens.remove(sessionToken);
if (clientState.isEmpty()) {
userState.clientStateMap.remove(sessionState.client.asBinder());
+ sessionState.client.asBinder().unlinkToDeath(clientState, 0);
}
}
@@ -1002,17 +1006,19 @@
synchronized (mLock) {
final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
userState.callbackSet.add(callback);
- try {
- callback.asBinder().linkToDeath(new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- synchronized (mLock) {
- if (userState.callbackSet != null) {
- userState.callbackSet.remove(callback);
- }
+ mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ if (userState.callbackSet != null) {
+ userState.callbackSet.remove(callback);
}
}
- }, 0);
+ }
+ };
+
+ try {
+ callback.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
Slog.e(TAG, "client process has already died", e);
}
@@ -1031,6 +1037,7 @@
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
userState.callbackSet.remove(callback);
+ callback.asBinder().unlinkToDeath(mDeathRecipient, 0);
}
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 744efab..332df95 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -1067,8 +1067,9 @@
// Figure out the value returned when access is allowed
final int allowedResult;
- if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0) {
- // If we're extending a persistable grant, then we need to return
+ if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0
+ || pi.forceUriPermissions) {
+ // If we're extending a persistable grant or need to force, then we need to return
// "targetUid" so that we always create a grant data structure to
// support take/release APIs
allowedResult = targetUid;
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/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index b8db3f3..37909c3 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -62,6 +62,7 @@
import java.io.File;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
@@ -1516,8 +1517,7 @@
public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
- File testFile = new File(mContext.getFilesDir(), "test");
- testFile.createNewFile();
+ File testFile = createTestFile();
FileDescriptor fileDescriptor = new FileDescriptor();
PrintWriter printWriter = new PrintWriter(testFile);
String[] args = {"1", "2"};
@@ -1531,8 +1531,7 @@
@Test
public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
BackupManagerService backupManagerService = createService();
- File testFile = new File(mContext.getFilesDir(), "test");
- testFile.createNewFile();
+ File testFile = createTestFile();
FileDescriptor fileDescriptor = new FileDescriptor();
PrintWriter printWriter = new PrintWriter(testFile);
String[] args = {"1", "2"};
@@ -1542,6 +1541,12 @@
verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args);
}
+ private File createTestFile() throws IOException {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ return testFile;
+ }
+
private BackupManagerService createService() {
mShadowContext.grantPermissions(BACKUP);
return new BackupManagerService(
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 427aed7..c8d1eb4 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -76,6 +76,7 @@
import org.robolectric.shadows.ShadowPackageManager;
import java.io.File;
+import java.io.IOException;
import java.util.List;
/**
@@ -1158,6 +1159,58 @@
}
/**
+ * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns {@code -1}
+ * when value not set.
+ */
+ @Test
+ public void testGetAncestralSerialNumber_notSet_returnsMinusOne() {
+ UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();
+
+ assertThat(service.getAncestralSerialNumber()).isEqualTo(-1L);
+ }
+
+ /**
+ * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns correct value
+ * when value set.
+ */
+ @Test
+ public void testGetAncestralSerialNumber_set_returnsCorrectValue() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();
+ service.setAncestralSerialNumberFile(createTestFile());
+
+ long testSerialNumber = 20L;
+ service.setAncestralSerialNumber(testSerialNumber);
+
+ assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber);
+ }
+
+ /**
+ * Test that {@link UserBackupManagerService#getAncestralSerialNumber()} returns correct value
+ * when value set.
+ */
+ @Test
+ public void testGetAncestralSerialNumber_setTwice_returnsCorrectValue() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();
+ service.setAncestralSerialNumberFile(createTestFile());
+
+ long testSerialNumber = 20L;
+ long testSerialNumber2 = 21L;
+ service.setAncestralSerialNumber(testSerialNumber);
+ service.setAncestralSerialNumber(testSerialNumber2);
+
+ assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber2);
+ }
+
+ private File createTestFile() throws IOException {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ return testFile;
+ }
+
+
+ /**
* We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
* extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
*/
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/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/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 3f3b996..bfda2ea 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -37,8 +37,10 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
+ <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
- android:testOnly="true">
+ android:testOnly="true"
+ android:largeHeap="true">
<uses-library android:name="android.test.mock" android:required="true" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
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..be47153 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -48,6 +48,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;
@@ -263,12 +264,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 +280,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 +301,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 +436,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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7395894..eab536f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1326,13 +1326,15 @@
"android.intent.action.DATA_STALL_DETECTED";
/**
- * A service action that identifies a {@link android.app.SmsAppService} subclass in the
+ * A service action that identifies
+ * a {@link android.service.carrier.CarrierMessagingClientService} subclass in the
* AndroidManifest.xml.
*
- * <p>See {@link android.app.SmsAppService} for the details.
+ * <p>See {@link android.service.carrier.CarrierMessagingClientService} for the details.
*/
@SdkConstant(SdkConstantType.SERVICE_ACTION)
- public static final String ACTION_SMS_APP_SERVICE = "android.telephony.action.SMS_APP_SERVICE";
+ public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE =
+ "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE";
/**
* An int extra used with {@link #ACTION_DATA_STALL_DETECTED} to indicate the
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/RcsPart.java b/telephony/java/android/telephony/ims/RcsPart.java
deleted file mode 100644
index da50173..0000000
--- a/telephony/java/android/telephony/ims/RcsPart.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;
-
-/**
- * A part of a composite {@link RcsMessage}.
- * @hide - TODO(sahinc) make this public
- */
-public abstract class RcsPart implements Parcelable {
-}
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));