diff options
161 files changed, 5067 insertions, 4015 deletions
diff --git a/api/current.txt b/api/current.txt index b4c84b16a069..b9f5637ec411 100644 --- a/api/current.txt +++ b/api/current.txt @@ -73,7 +73,6 @@ package android { field public static final java.lang.String DUMP = "android.permission.DUMP"; field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST"; - field public static final java.lang.String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE"; field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; @@ -6706,6 +6705,7 @@ package android.app.admin { field public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; // 0x3 field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1 field public static final int USER_OPERATION_SUCCESS = 0; // 0x0 + field public static final int WIPE_EUICC = 4; // 0x4 field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1 field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2 } @@ -6874,6 +6874,7 @@ package android.app.assist { method public java.lang.String getIdEntry(); method public java.lang.String getIdPackage(); method public java.lang.String getIdType(); + method public int getImportantForAutofill(); method public int getInputType(); method public int getLeft(); method public android.os.LocaleList getLocaleList(); @@ -9441,6 +9442,7 @@ package android.content { field public static final java.lang.String DISPLAY_SERVICE = "display"; field public static final java.lang.String DOWNLOAD_SERVICE = "download"; field public static final java.lang.String DROPBOX_SERVICE = "dropbox"; + field public static final java.lang.String EUICC_SERVICE = "euicc"; field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint"; field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; @@ -11266,6 +11268,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore"; field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony"; field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma"; + field public static final java.lang.String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc"; field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm"; field public static final java.lang.String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms"; field public static final deprecated java.lang.String FEATURE_TELEVISION = "android.hardware.type.television"; @@ -23371,6 +23374,7 @@ package android.media { method public android.media.MediaDrm.CryptoSession getCryptoSession(byte[], java.lang.String, java.lang.String); method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>) throws android.media.NotProvisionedException; method public int getMaxHdcpLevel(); + method public static int getMaxSecurityLevel(); method public int getMaxSessionCount(); method public android.os.PersistableBundle getMetrics(); method public int getOpenSessionCount(); @@ -23384,6 +23388,7 @@ package android.media { method public static boolean isCryptoSchemeSupported(java.util.UUID); method public static boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String); method public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException; + method public byte[] openSession(int) throws android.media.NotProvisionedException, android.media.ResourceBusyException; method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException; method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException; method public java.util.HashMap<java.lang.String, java.lang.String> queryKeyStatus(byte[]); @@ -23399,7 +23404,6 @@ package android.media { method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler); method public void setPropertyByteArray(java.lang.String, byte[]); method public void setPropertyString(java.lang.String, java.lang.String); - method public void setSecurityLevel(byte[], int); field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3 field public static final int EVENT_KEY_REQUIRED = 2; // 0x2 field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1 @@ -42205,13 +42209,16 @@ package android.telephony { method public java.lang.String getNumber(); method public int getSimSlotIndex(); method public int getSubscriptionId(); + method public boolean isEmbedded(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.SubscriptionInfo> CREATOR; } public class SubscriptionManager { method public void addOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); + method public boolean canManageSubscription(android.telephony.SubscriptionInfo); method public static deprecated android.telephony.SubscriptionManager from(android.content.Context); + method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList(); method public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int); method public int getActiveSubscriptionInfoCount(); method public int getActiveSubscriptionInfoCountMax(); @@ -42596,6 +42603,44 @@ package android.telephony.data { } +package android.telephony.euicc { + + public final class DownloadableSubscription implements android.os.Parcelable { + method public int describeContents(); + method public static android.telephony.euicc.DownloadableSubscription forActivationCode(java.lang.String); + method public java.lang.String getConfirmationCode(); + method public java.lang.String getEncodedActivationCode(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.euicc.DownloadableSubscription> CREATOR; + } + + public final class EuiccInfo implements android.os.Parcelable { + ctor public EuiccInfo(java.lang.String); + method public int describeContents(); + method public java.lang.String getOsVersion(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccInfo> CREATOR; + } + + public class EuiccManager { + method public void deleteSubscription(int, android.app.PendingIntent); + method public void downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent); + method public java.lang.String getEid(); + method public android.telephony.euicc.EuiccInfo getEuiccInfo(); + method public boolean isEnabled(); + method public void startResolutionActivity(android.app.Activity, int, android.content.Intent, android.app.PendingIntent) throws android.content.IntentSender.SendIntentException; + method public void switchToSubscription(int, android.app.PendingIntent); + field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS"; + field public static final java.lang.String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE = "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE"; + field public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2; // 0x2 + field public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0; // 0x0 + field public static final int EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR = 1; // 0x1 + field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE"; + field public static final java.lang.String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon"; + } + +} + package android.telephony.gsm { public class GsmCellLocation extends android.telephony.CellLocation { @@ -48420,6 +48465,7 @@ package android.view { method public abstract void setHint(java.lang.CharSequence); method public abstract void setHtmlInfo(android.view.ViewStructure.HtmlInfo); method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String); + method public void setImportantForAutofill(int); method public abstract void setInputType(int); method public abstract void setLocaleList(android.os.LocaleList); method public abstract void setLongClickable(boolean); diff --git a/api/system-current.txt b/api/system-current.txt index 3dbb333ef789..3707c910c481 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -20,6 +20,7 @@ package android { field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE"; field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH"; + field public static final java.lang.String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE"; field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE"; field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET"; field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"; @@ -186,6 +187,7 @@ package android { field public static final java.lang.String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; field public static final java.lang.String WRITE_APN_SETTINGS = "android.permission.WRITE_APN_SETTINGS"; field public static final java.lang.String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE"; + field public static final java.lang.String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"; field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES"; field public static final java.lang.String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE"; field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS"; @@ -791,6 +793,7 @@ package android.content { method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); field public static final java.lang.String BACKUP_SERVICE = "backup"; field public static final java.lang.String CONTEXTHUB_SERVICE = "contexthub"; + field public static final java.lang.String EUICC_CARD_SERVICE = "euicc_card"; field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control"; field public static final java.lang.String NETWORK_SCORE_SERVICE = "network_score"; field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock"; @@ -3806,6 +3809,7 @@ package android.os { method public void resume(); method public void suspend(); method public boolean unbind(); + method public boolean verifyPayloadMetadata(java.lang.String); } public static final class UpdateEngine.ErrorCodeConstants { @@ -4133,6 +4137,7 @@ package android.provider { method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean); method public static void resetToDefaults(android.content.ContentResolver, java.lang.String); field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages"; + field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus"; field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update"; field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on"; field public static final java.lang.String WEBVIEW_MULTIPROCESS = "webview_multiprocess"; @@ -4306,6 +4311,125 @@ package android.service.autofill { } +package android.service.euicc { + + public final class EuiccProfileInfo implements android.os.Parcelable { + method public int describeContents(); + method public android.service.carrier.CarrierIdentifier getCarrierIdentifier(); + method public java.lang.String getIccid(); + method public java.lang.String getNickname(); + method public int getPolicyRules(); + method public int getProfileClass(); + method public java.lang.String getProfileName(); + method public java.lang.String getServiceProviderName(); + method public int getState(); + method public java.util.List<android.telephony.UiccAccessRule> getUiccAccessRules(); + method public boolean hasPolicyRule(int); + method public boolean hasPolicyRules(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.euicc.EuiccProfileInfo> CREATOR; + field public static final int POLICY_RULE_DELETE_AFTER_DISABLING = 4; // 0x4 + field public static final int POLICY_RULE_DO_NOT_DELETE = 2; // 0x2 + field public static final int POLICY_RULE_DO_NOT_DISABLE = 1; // 0x1 + field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2 + field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1 + field public static final int PROFILE_CLASS_TESTING = 0; // 0x0 + field public static final int PROFILE_STATE_DISABLED = 0; // 0x0 + field public static final int PROFILE_STATE_ENABLED = 1; // 0x1 + } + + public static final class EuiccProfileInfo.Builder { + ctor public EuiccProfileInfo.Builder(java.lang.String); + ctor public EuiccProfileInfo.Builder(android.service.euicc.EuiccProfileInfo); + method public android.service.euicc.EuiccProfileInfo build(); + method public android.service.euicc.EuiccProfileInfo.Builder setCarrierIdentifier(android.service.carrier.CarrierIdentifier); + method public android.service.euicc.EuiccProfileInfo.Builder setIccid(java.lang.String); + method public android.service.euicc.EuiccProfileInfo.Builder setNickname(java.lang.String); + method public android.service.euicc.EuiccProfileInfo.Builder setPolicyRules(int); + method public android.service.euicc.EuiccProfileInfo.Builder setProfileClass(int); + method public android.service.euicc.EuiccProfileInfo.Builder setProfileName(java.lang.String); + method public android.service.euicc.EuiccProfileInfo.Builder setServiceProviderName(java.lang.String); + method public android.service.euicc.EuiccProfileInfo.Builder setState(int); + method public android.service.euicc.EuiccProfileInfo.Builder setUiccAccessRule(java.util.List<android.telephony.UiccAccessRule>); + } + + public static abstract class EuiccProfileInfo.PolicyRule implements java.lang.annotation.Annotation { + } + + public static abstract class EuiccProfileInfo.ProfileClass implements java.lang.annotation.Annotation { + } + + public static abstract class EuiccProfileInfo.ProfileState implements java.lang.annotation.Annotation { + } + + public abstract class EuiccService extends android.app.Service { + ctor public EuiccService(); + method public android.os.IBinder onBind(android.content.Intent); + method public abstract int onDeleteSubscription(int, java.lang.String); + method public abstract int onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean); + method public abstract int onEraseSubscriptions(int); + method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean); + method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean); + method public abstract java.lang.String onGetEid(int); + method public abstract android.telephony.euicc.EuiccInfo onGetEuiccInfo(int); + method public abstract android.service.euicc.GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int); + method public abstract int onGetOtaStatus(int); + method public abstract int onRetainSubscriptionsForFactoryReset(int); + method public abstract void onStartOtaIfNecessary(int, android.service.euicc.EuiccService.OtaStatusChangedCallback); + method public abstract int onSwitchToSubscription(int, java.lang.String, boolean); + method public abstract int onUpdateSubscriptionNickname(int, java.lang.String, java.lang.String); + field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS"; + field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION"; + field public static final java.lang.String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE"; + field public static final java.lang.String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM"; + field public static final java.lang.String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES"; + field public static final java.lang.String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI"; + field public static final java.lang.String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService"; + field public static final java.lang.String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE"; + field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE"; + field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED"; + field public static final java.lang.String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT"; + field public static final int RESULT_FIRST_USER = 1; // 0x1 + field public static final int RESULT_MUST_DEACTIVATE_SIM = -1; // 0xffffffff + field public static final int RESULT_NEED_CONFIRMATION_CODE = -2; // 0xfffffffe + field public static final int RESULT_OK = 0; // 0x0 + } + + public static abstract class EuiccService.OtaStatusChangedCallback { + ctor public EuiccService.OtaStatusChangedCallback(); + method public abstract void onOtaStatusChanged(int); + } + + public final class GetDefaultDownloadableSubscriptionListResult implements android.os.Parcelable { + ctor public GetDefaultDownloadableSubscriptionListResult(int, android.telephony.euicc.DownloadableSubscription[]); + method public int describeContents(); + method public java.util.List<android.telephony.euicc.DownloadableSubscription> getDownloadableSubscriptions(); + method public int getResult(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.euicc.GetDefaultDownloadableSubscriptionListResult> CREATOR; + } + + public final class GetDownloadableSubscriptionMetadataResult implements android.os.Parcelable { + ctor public GetDownloadableSubscriptionMetadataResult(int, android.telephony.euicc.DownloadableSubscription); + method public int describeContents(); + method public android.telephony.euicc.DownloadableSubscription getDownloadableSubscription(); + method public int getResult(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.euicc.GetDownloadableSubscriptionMetadataResult> CREATOR; + } + + public final class GetEuiccProfileInfoListResult implements android.os.Parcelable { + ctor public GetEuiccProfileInfoListResult(int, android.service.euicc.EuiccProfileInfo[], boolean); + method public int describeContents(); + method public boolean getIsRemovable(); + method public java.util.List<android.service.euicc.EuiccProfileInfo> getProfiles(); + method public int getResult(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.euicc.GetEuiccProfileInfoListResult> CREATOR; + } + +} + package android.service.notification { public final class Adjustment implements android.os.Parcelable { @@ -4906,8 +5030,14 @@ package android.telephony { field public static final int RESULT_SYSTEM_ERROR = 15; // 0xf } + public class SubscriptionInfo implements android.os.Parcelable { + method public java.util.List<android.telephony.UiccAccessRule> getAccessRules(); + } + public class SubscriptionManager { + method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList(); method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int); + method public void requestEmbeddedSubscriptionInfoListRefresh(); method public void setSubscriptionOverrideCongested(int, boolean, long); method public void setSubscriptionOverrideUnmetered(int, boolean, long); method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>); @@ -5043,6 +5173,16 @@ package android.telephony { field public static final int SIM_STATE_PRESENT = 11; // 0xb } + public final class UiccAccessRule implements android.os.Parcelable { + ctor public UiccAccessRule(byte[], java.lang.String, long); + method public int describeContents(); + method public int getCarrierPrivilegeStatus(android.content.pm.PackageInfo); + method public int getCarrierPrivilegeStatus(android.content.pm.Signature, java.lang.String); + method public java.lang.String getPackageName(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.UiccAccessRule> CREATOR; + } + public class UiccSlotInfo implements android.os.Parcelable { ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int); method public int describeContents(); @@ -5153,6 +5293,125 @@ package android.telephony.data { } +package android.telephony.euicc { + + public final class DownloadableSubscription implements android.os.Parcelable { + method public java.util.List<android.telephony.UiccAccessRule> getAccessRules(); + method public java.lang.String getCarrierName(); + } + + public static final class DownloadableSubscription.Builder { + ctor public DownloadableSubscription.Builder(); + ctor public DownloadableSubscription.Builder(android.telephony.euicc.DownloadableSubscription); + method public android.telephony.euicc.DownloadableSubscription build(); + method public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(java.util.List<android.telephony.UiccAccessRule>); + method public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(java.lang.String); + method public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(java.lang.String); + method public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(java.lang.String); + } + + public class EuiccCardManager { + method public void authenticateServer(java.lang.String, java.lang.String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); + method public void cancelSession(java.lang.String, byte[], int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); + method public void deleteProfile(java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); + method public void disableProfile(java.lang.String, java.lang.String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); + method public void listNotifications(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>); + method public void loadBoundProfilePackage(java.lang.String, byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); + method public void prepareDownload(java.lang.String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); + method public void removeNotificationFromList(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); + method public void requestAllProfiles(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo[]>); + method public void requestDefaultSmdpAddress(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>); + method public void requestEuiccChallenge(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); + method public void requestEuiccInfo1(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); + method public void requestEuiccInfo2(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>); + method public void requestProfile(java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>); + method public void requestRulesAuthTable(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccRulesAuthTable>); + method public void requestSmdsAddress(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>); + method public void resetMemory(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); + method public void retrieveNotification(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification>); + method public void retrieveNotificationList(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>); + method public void setDefaultSmdpAddress(java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); + method public void setNickname(java.lang.String, java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>); + method public void switchToProfile(java.lang.String, java.lang.String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>); + field public static final int CANCEL_REASON_END_USER_REJECTED = 0; // 0x0 + field public static final int CANCEL_REASON_POSTPONED = 1; // 0x1 + field public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3; // 0x3 + field public static final int CANCEL_REASON_TIMEOUT = 2; // 0x2 + field public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 2; // 0x2 + field public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1; // 0x1 + field public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 4; // 0x4 + field public static final int RESULT_OK = 0; // 0x0 + field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff + } + + public static abstract class EuiccCardManager.CancelReason implements java.lang.annotation.Annotation { + } + + public static abstract class EuiccCardManager.ResetOption implements java.lang.annotation.Annotation { + } + + public static abstract interface EuiccCardManager.ResultCallback<T> { + method public abstract void onComplete(int, T); + } + + public class EuiccManager { + method public void continueOperation(android.content.Intent, android.os.Bundle); + method public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent); + method public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent); + method public int getOtaStatus(); + field public static final java.lang.String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED"; + field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION"; + field public static final int EUICC_OTA_FAILED = 2; // 0x2 + field public static final int EUICC_OTA_IN_PROGRESS = 1; // 0x1 + field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4 + field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5 + field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3 + field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION"; + field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS"; + } + + public static abstract class EuiccManager.OtaStatus implements java.lang.annotation.Annotation { + } + + public final class EuiccNotification implements android.os.Parcelable { + ctor public EuiccNotification(int, java.lang.String, int, byte[]); + method public int describeContents(); + method public byte[] getData(); + method public int getEvent(); + method public int getSeq(); + method public java.lang.String getTargetAddr(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int ALL_EVENTS = 15; // 0xf + field public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccNotification> CREATOR; + field public static final int EVENT_DELETE = 8; // 0x8 + field public static final int EVENT_DISABLE = 4; // 0x4 + field public static final int EVENT_ENABLE = 2; // 0x2 + field public static final int EVENT_INSTALL = 1; // 0x1 + } + + public static abstract class EuiccNotification.Event implements java.lang.annotation.Annotation { + } + + public final class EuiccRulesAuthTable implements android.os.Parcelable { + method public int describeContents(); + method public int findIndex(int, android.service.carrier.CarrierIdentifier); + method public boolean hasPolicyRuleFlag(int, int); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccRulesAuthTable> CREATOR; + field public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1; // 0x1 + } + + public static final class EuiccRulesAuthTable.Builder { + ctor public EuiccRulesAuthTable.Builder(int); + method public android.telephony.euicc.EuiccRulesAuthTable.Builder add(int, java.util.List<android.service.carrier.CarrierIdentifier>, int); + method public android.telephony.euicc.EuiccRulesAuthTable build(); + } + + public static abstract class EuiccRulesAuthTable.PolicyRuleFlag implements java.lang.annotation.Annotation { + } + +} + package android.telephony.ims { public final class ImsCallForwardInfo implements android.os.Parcelable { diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 8eea944ed588..67b9089c8315 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -19,11 +19,9 @@ statsd_common_src := \ ../../core/java/android/os/IStatsManager.aidl \ src/stats_log.proto \ src/statsd_config.proto \ - src/statsd_internal.proto \ src/atoms.proto \ - src/field_util.cpp \ + src/FieldValue.cpp \ src/stats_log_util.cpp \ - src/dimension.cpp \ src/anomaly/AnomalyMonitor.cpp \ src/anomaly/AnomalyTracker.cpp \ src/anomaly/DurationAnomalyTracker.cpp \ @@ -172,7 +170,6 @@ LOCAL_CFLAGS += \ LOCAL_SRC_FILES := \ $(statsd_common_src) \ - tests/dimension_test.cpp \ tests/AnomalyMonitor_test.cpp \ tests/anomaly/AnomalyTracker_test.cpp \ tests/ConfigManager_test.cpp \ @@ -184,6 +181,7 @@ LOCAL_SRC_FILES := \ tests/MetricsManager_test.cpp \ tests/StatsLogProcessor_test.cpp \ tests/UidMap_test.cpp \ + tests/FieldValue_test.cpp \ tests/condition/CombinationConditionTracker_test.cpp \ tests/condition/SimpleConditionTracker_test.cpp \ tests/metrics/OringDurationTracker_test.cpp \ diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp new file mode 100644 index 000000000000..7b0b69a4bcae --- /dev/null +++ b/cmds/statsd/src/FieldValue.cpp @@ -0,0 +1,186 @@ +/* + * 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. + */ + +#define DEBUG false +#include "Log.h" +#include "FieldValue.h" +#include "HashableDimensionKey.h" + +namespace android { +namespace os { +namespace statsd { + +bool Field::matches(const Matcher& matcher) const { + if (mTag != matcher.mMatcher.getTag()) { + return false; + } + if ((mField & matcher.mMask) == matcher.mMatcher.getField()) { + return true; + } + + return false; +}; + +void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* pos, int* mask, + std::vector<Matcher>* output) { + if (depth > kMaxLogDepth) { + ALOGE("depth > 2"); + return; + } + + pos[depth] = matcher.field(); + mask[depth] = 0x7f; + + if (matcher.has_position()) { + depth++; + if (depth > 2) { + return; + } + switch (matcher.position()) { + case Position::ANY: + pos[depth] = 0; + mask[depth] = 0; + break; + case Position::FIRST: + pos[depth] = 1; + mask[depth] = 0x7f; + break; + case Position::LAST: + pos[depth] = 0x80; + mask[depth] = 0x80; + break; + case Position::POSITION_UNKNOWN: + pos[depth] = 0; + mask[depth] = 0; + break; + } + } + + if (matcher.child_size() == 0) { + output->push_back(Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth))); + Matcher matcher = Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth)); + } else { + for (const auto& child : matcher.child()) { + translateFieldMatcher(tag, child, depth + 1, pos, mask, output); + } + } +} + +void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output) { + int pos[] = {1, 1, 1}; + int mask[] = {0x7f, 0x7f, 0x7f}; + int tag = matcher.field(); + for (const auto& child : matcher.child()) { + translateFieldMatcher(tag, child, 0, pos, mask, output); + } +} + +bool isAttributionUidField(const FieldValue& value) { + int field = value.mField.getField() & 0xff007f; + if (field == 0x10001 && value.mValue.getType() == INT) { + return true; + } + return false; +} + +bool isAttributionUidField(const Field& field, const Value& value) { + int f = field.getField() & 0xff007f; + if (f == 0x10001 && value.getType() == INT) { + return true; + } + return false; +} + +Value::Value(const Value& from) { + type = from.getType(); + switch (type) { + case INT: + int_value = from.int_value; + break; + case LONG: + long_value = from.long_value; + break; + case FLOAT: + float_value = from.float_value; + break; + case STRING: + str_value = from.str_value; + break; + } +} + +std::string Value::toString() const { + switch (type) { + case INT: + return std::to_string(int_value) + "[I]"; + case LONG: + return std::to_string(long_value) + "[L]"; + case FLOAT: + return std::to_string(float_value) + "[F]"; + case STRING: + return str_value + "[S]"; + } +} + +bool Value::operator==(const Value& that) const { + if (type != that.getType()) return false; + + switch (type) { + case INT: + return int_value == that.int_value; + case LONG: + return long_value == that.long_value; + case FLOAT: + return float_value == that.float_value; + case STRING: + return str_value == that.str_value; + } +} + +bool Value::operator!=(const Value& that) const { + if (type != that.getType()) return true; + switch (type) { + case INT: + return int_value != that.int_value; + case LONG: + return long_value != that.long_value; + case FLOAT: + return float_value != that.float_value; + case STRING: + return str_value != that.str_value; + } +} + +bool Value::operator<(const Value& that) const { + if (type != that.getType()) return type < that.getType(); + + switch (type) { + case INT: + return int_value < that.int_value; + case LONG: + return long_value < that.long_value; + case FLOAT: + return float_value < that.float_value; + case STRING: + return str_value < that.str_value; + default: + return false; + } +} + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h new file mode 100644 index 000000000000..7484108d9e1a --- /dev/null +++ b/cmds/statsd/src/FieldValue.h @@ -0,0 +1,338 @@ +/* + * 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. + */ +#pragma once + +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +namespace android { +namespace os { +namespace statsd { + +class HashableDimensionKey; +struct Matcher; +struct Field; +struct FieldValue; + +const int32_t kAttributionField = 1; +const int32_t kMaxLogDepth = 2; +const int32_t kLastBitMask = 0x80; +const int32_t kClearLastBitDeco = 0x7f; + +enum Type { INT, LONG, FLOAT, STRING }; + + +static int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth) { + int32_t field = 0; + for (int32_t i = 0; i <= depth; i++) { + int32_t shiftBits = 8 * (kMaxLogDepth - i); + field |= (pos[i] << shiftBits); + } + + if (includeDepth) { + field |= (depth << 24); + } + return field; +} + +static int32_t encodeMatcherMask(int32_t mask[], int32_t depth) { + return getEncodedField(mask, depth, false) | 0xff000000; +} + +// Get the encoded field for a leaf with a [field] number at depth 0; +static int32_t getSimpleField(size_t field) { + return ((int32_t)field << 8 * 2); +} + +/** + * Field is a wrapper class for 2 integers that represents the field of a log element in its Atom + * proto. + * [mTag]: the atom id. + * [mField]: encoded path from the root (atom) to leaf. + * + * For example: + * WakeLockStateChanged { + * repeated AttributionNode = 1; + * int state = 2; + * string tag = 3; + * } + * Read from logd, the items are structured as below: + * [[[1000, "tag"], [2000, "tag2"],], 2,"hello"] + * + * When we read through the list, we will encode each field in a 32bit integer. + * 8bit segments |--------|--------|--------|--------| + * Depth field0 [L]field1 [L]field1 + * + * The first 8 bits are the depth of the field. for example, the uid 1000 has depth 2. + * The following 3 8-bit are for the item's position at each level. + * The first bit of each 8bits field is reserved to mark if the item is the last item at that level + * this is to make matching easier later. + * + * The above wakelock event is translated into FieldValue pairs. + * 0x02010101->1000 + * 0x02010182->tag + * 0x02018201->2000 + * 0x02018282->tag2 + * 0x00020000->2 + * 0x00030000->"hello" + * + * This encoding is the building block for the later operations. + * Please see the definition for Matcher below to see how the matching is done. + */ +struct Field { +private: + int32_t mTag; + int32_t mField; + +public: + Field(int32_t tag, int32_t pos[], int32_t depth) : mTag(tag) { + mField = getEncodedField(pos, depth, true); + } + + Field(const Field& from) : mTag(from.getTag()), mField(from.getField()) { + } + + Field(int32_t tag, int32_t field) : mTag(tag), mField(field){}; + + inline void setField(int32_t field) { + mField = field; + } + + inline void setTag(int32_t tag) { + mTag = tag; + } + + inline void decorateLastPos(int32_t depth) { + int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth); + mField |= mask; + } + + inline int32_t getTag() const { + return mTag; + } + + inline int32_t getDepth() const { + return (mField >> 24); + } + + inline int32_t getPath(int32_t depth) const { + if (depth > 2 || depth < 0) return 0; + + int32_t field = (mField & 0x00ffffff); + int32_t mask = 0xffffffff; + return (field & (mask << 8 * (kMaxLogDepth - depth))); + } + + inline int32_t getPrefix(int32_t depth) const { + if (depth == 0) return 0; + return getPath(depth - 1); + } + + inline int32_t getField() const { + return mField; + } + + inline int32_t getRawPosAtDepth(int32_t depth) const { + int32_t field = (mField & 0x00ffffff); + int32_t shift = 8 * (kMaxLogDepth - depth); + int32_t mask = 0xff << shift; + + return (field & mask) >> shift; + } + + inline int32_t getPosAtDepth(int32_t depth) const { + return getRawPosAtDepth(depth) & kClearLastBitDeco; + } + + // Check if the first bit of the 8-bit segment for depth is 1 + inline bool isLastPos(int32_t depth) const { + int32_t field = (mField & 0x00ffffff); + int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth); + return (field & mask) != 0; + } + + // if the 8-bit segment is all 0's + inline bool isAnyPosMatcher(int32_t depth) const { + return getDepth() >= depth && getRawPosAtDepth(depth) == 0; + } + // if the 8bit is 0x80 (1000 0000) + inline bool isLastPosMatcher(int32_t depth) const { + return getDepth() >= depth && getRawPosAtDepth(depth) == kLastBitMask; + } + + inline bool operator==(const Field& that) const { + return mTag == that.getTag() && mField == that.getField(); + }; + + inline bool operator!=(const Field& that) const { + return mTag != that.getTag() || mField != that.getField(); + }; + + bool operator<(const Field& that) const { + if (mTag != that.getTag()) { + return mTag < that.getTag(); + } + + if (mField != that.getField()) { + return mField < that.getField(); + } + + return false; + } + bool matches(const Matcher& that) const; +}; + +/** + * Matcher represents a leaf matcher in the FieldMatcher in statsd_config. + * + * It contains all information needed to match one or more leaf node. + * All information is encoded in a Field(2 ints) and a bit mask(1 int). + * + * For example, to match the first/any/last uid field in attribution chain in Atom 10, + * we have the following FieldMatcher in statsd_config + * FieldMatcher { + * field:10 + * FieldMatcher { + * field:1 + * position: any/last/first + * FieldMatcher { + * field:1 + * } + * } + * } + * + * We translate the FieldMatcher into a Field, and mask + * First: [Matcher Field] 0x02010101 [Mask]0xffff7fff + * Last: [Matcher Field] 0x02018001 [Mask]0xffff80ff + * Any: [Matcher Field] 0x02010001 [Mask]0xffff00ff + * + * [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if + * the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are + * equal. Nothing can beat the performance of this matching algorithm. + * + * TODO: ADD EXAMPLE HERE. + */ +struct Matcher { + Matcher(const Field& matcher, int32_t mask) : mMatcher(matcher), mMask(mask){}; + + const Field mMatcher; + const int32_t mMask; + + bool hasAnyPositionMatcher(int* prefix) const { + if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(2) == 0) { + (*prefix) = mMatcher.getPrefix(2); + return true; + } + return false; + } +}; + +/** + * A wrapper for a union type to contain multiple types of values. + * + */ +struct Value { + Value(int32_t v) { + int_value = v; + type = INT; + } + + Value(int64_t v) { + long_value = v; + type = LONG; + } + + Value(float v) { + float_value = v; + type = FLOAT; + } + + Value(const std::string& v) { + str_value = v; + type = STRING; + } + + void setInt(int32_t v) { + int_value = v; + type = INT; + } + + void setLong(int64_t v) { + long_value = v; + type = LONG; + } + + union { + int32_t int_value; + int64_t long_value; + float float_value; + }; + std::string str_value; + + Type type; + + std::string toString() const; + + Type getType() const { + return type; + } + + Value(const Value& from); + + bool operator==(const Value& that) const; + bool operator!=(const Value& that) const; + + bool operator<(const Value& that) const; + +private: + Value(){}; +}; + +/** + * Represents a log item, or a dimension item (They are essentially the same). + */ +struct FieldValue { + FieldValue(const Field& field, const Value& value) : mField(field), mValue(value) { + } + bool operator==(const FieldValue& that) const { + return mField == that.mField && mValue == that.mValue; + } + bool operator!=(const FieldValue& that) const { + return mField != that.mField || mValue != that.mValue; + } + bool operator<(const FieldValue& that) const { + if (mField != that.mField) { + return mField < that.mField; + } + + if (mValue != that.mValue) { + return mValue < that.mValue; + } + + return false; + } + + Field mField; + Value mValue; +}; + +bool isAttributionUidField(const FieldValue& value); + +void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output); + +bool isAttributionUidField(const Field& field, const Value& value); +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index 8483b024aa55..68e2176c2e6d 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -13,177 +13,253 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#define DEBUG false // STOPSHIP if true +#include "Log.h" #include "HashableDimensionKey.h" -#include "dimension.h" +#include "FieldValue.h" namespace android { namespace os { namespace statsd { +using std::vector; -android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value) { - android::hash_t hash = seed; - hash = android::JenkinsHashMix(hash, android::hash_type(value.field())); - - hash = android::JenkinsHashMix(hash, android::hash_type((int)value.value_case())); - switch (value.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - hash = android::JenkinsHashMix( - hash, - static_cast<uint32_t>(std::hash<std::string>()(value.value_str()))); - break; - case DimensionsValue::ValueCase::kValueInt: - hash = android::JenkinsHashMix(hash, android::hash_type(value.value_int())); - break; - case DimensionsValue::ValueCase::kValueLong: - hash = android::JenkinsHashMix( - hash, android::hash_type(static_cast<int64_t>(value.value_long()))); - break; - case DimensionsValue::ValueCase::kValueBool: - hash = android::JenkinsHashMix(hash, android::hash_type(value.value_bool())); - break; - case DimensionsValue::ValueCase::kValueFloat: { - float floatVal = value.value_float(); - hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float)); - break; - } - case DimensionsValue::ValueCase::kValueTuple: { - hash = android::JenkinsHashMix(hash, android::hash_type( - value.value_tuple().dimensions_value_size())); - for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) { - hash = android::JenkinsHashMix( - hash, - hashDimensionsValue(value.value_tuple().dimensions_value(i))); +android::hash_t hashDimension(const HashableDimensionKey& value) { + android::hash_t hash = 0; + for (const auto& fieldValue : value.getValues()) { + hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getField())); + hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getTag())); + hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mValue.getType())); + switch (fieldValue.mValue.getType()) { + case INT: + hash = android::JenkinsHashMix(hash, + android::hash_type(fieldValue.mValue.int_value)); + break; + case LONG: + hash = android::JenkinsHashMix(hash, + android::hash_type(fieldValue.mValue.long_value)); + break; + case STRING: + hash = android::JenkinsHashMix(hash, static_cast<uint32_t>(std::hash<std::string>()( + fieldValue.mValue.str_value))); + break; + case FLOAT: { + float floatVal = fieldValue.mValue.float_value; + hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float)); + break; } - break; } - case DimensionsValue::ValueCase::VALUE_NOT_SET: - break; } return JenkinsHashWhiten(hash); } -android::hash_t hashDimensionsValue(const DimensionsValue& value) { - return hashDimensionsValue(0, value); -} +// Filter fields using the matchers and output the results as a HashableDimensionKey. +// Note: HashableDimensionKey is just a wrapper for vector<FieldValue> +bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values, + vector<HashableDimensionKey>* output) { + output->push_back(HashableDimensionKey()); + // Top level is only tag id. Now take the real child matchers + int prevAnyMatcherPrefix = 0; + size_t prevPrevFanout = 0; + size_t prevFanout = 0; + // For each matcher get matched results. + for (const auto& matcher : matcherFields) { + vector<FieldValue> matchedResults; + for (const auto& value : values) { + // TODO: potential optimization here to break early because all fields are naturally + // sorted. + int32_t filteredField; + if (value.mField.matches(matcher)) { + matchedResults.push_back(FieldValue( + Field(value.mField.getTag(), (value.mField.getField() & matcher.mMask)), + value.mValue)); + } + } -android::hash_t hashMetricDimensionKey(int64_t seed, const MetricDimensionKey& dimensionKey) { - android::hash_t hash = seed; - hash = android::JenkinsHashMix(hash, std::hash<MetricDimensionKey>{}(dimensionKey)); - return JenkinsHashWhiten(hash); -} + if (matchedResults.size() == 0) { + VLOG("We can't find a dimension value for matcher (%d)%#x.", matcher.mMatcher.getTag(), + matcher.mMatcher.getField()); + continue; + } -using std::string; + if (matchedResults.size() == 1) { + for (auto& dimension : *output) { + dimension.addValue(matchedResults[0]); + } + prevAnyMatcherPrefix = 0; + prevFanout = 0; + continue; + } -string HashableDimensionKey::toString() const { - return DimensionsValueToString(getDimensionsValue()); -} + // All the complexity below is because we support ANY in dimension. + bool createFanout = true; + // createFanout is true when the matcher doesn't need to follow the prev matcher's + // order. + // e.g., get (uid, tag) from any position in attribution. because we have translated + // it as 2 matchers, they need to follow the same ordering, we can't create a cross + // product of all uid and tags. + // However, if the 2 matchers have different prefix, they will create a cross product + // e.g., [any uid] [any some other repeated field], we will create a cross product for them + if (prevAnyMatcherPrefix != 0) { + int anyMatcherPrefix = 0; + bool isAnyMatcher = matcher.hasAnyPositionMatcher(&anyMatcherPrefix); + if (isAnyMatcher && anyMatcherPrefix == prevAnyMatcherPrefix) { + createFanout = false; + } else { + prevAnyMatcherPrefix = anyMatcherPrefix; + } + } -bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) { - if (s1.field() != s2.field()) { - return false; - } - if (s1.value_case() != s2.value_case()) { - return false; + // Each matcher should match exact one field, unless position is ANY + // When x number of fields matches a matcher, the returned dimension + // size is multiplied by x. + int oldSize; + if (createFanout) { + // First create fanout (fanout size is matchedResults.Size which could be one, + // which means we do nothing here) + oldSize = output->size(); + for (size_t i = 1; i < matchedResults.size(); i++) { + output->insert(output->end(), output->begin(), output->begin() + oldSize); + } + prevPrevFanout = oldSize; + prevFanout = matchedResults.size(); + } else { + // If we should not create fanout, e.g., uid tag from same position should be remain + // together. + oldSize = prevPrevFanout; + if (prevFanout != matchedResults.size()) { + // sanity check. + ALOGE("2 Any matcher result in different output"); + return false; + } + } + // now add the matched field value to output + for (size_t i = 0; i < matchedResults.size(); i++) { + for (int j = 0; j < oldSize; j++) { + (*output)[i * oldSize + j].addValue(matchedResults[i]); + } + } } - switch (s1.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return (s1.value_str() == s2.value_str()); - case DimensionsValue::ValueCase::kValueInt: - return s1.value_int() == s2.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return s1.value_long() == s2.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return s1.value_bool() == s2.value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return s1.value_float() == s2.value_float(); - case DimensionsValue::ValueCase::kValueTuple: - { - if (s1.value_tuple().dimensions_value_size() != - s2.value_tuple().dimensions_value_size()) { - return false; - } - bool allMatched = true; - for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) { - allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i)); - } - return allMatched; + + return output->size() > 0 && (*output)[0].getValues().size() > 0; +} + +void filterGaugeValues(const std::vector<Matcher>& matcherFields, + const std::vector<FieldValue>& values, std::vector<FieldValue>* output) { + for (const auto& field : matcherFields) { + for (const auto& value : values) { + int filteredField; + if (value.mField.matches(field)) { + output->push_back(value); } - case DimensionsValue::ValueCase::VALUE_NOT_SET: - default: - return true; + } } } -bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) { - if (s1.field() != s2.field()) { - return s1.field() < s2.field(); +void getDimensionForCondition(const LogEvent& event, Metric2Condition links, + vector<HashableDimensionKey>* conditionDimension) { + // Get the dimension first by using dimension from what. + filterValues(links.metricFields, event.getValues(), conditionDimension); + + // Then replace the field with the dimension from condition. + for (auto& dim : *conditionDimension) { + size_t count = dim.getValues().size(); + if (count != links.conditionFields.size()) { + // ALOGE("WTF condition link is bad"); + return; + } + + for (size_t i = 0; i < count; i++) { + dim.mutableValue(i)->mField.setField(links.conditionFields[i].mMatcher.getField()); + dim.mutableValue(i)->mField.setTag(links.conditionFields[i].mMatcher.getTag()); + } } - if (s1.value_case() != s2.value_case()) { - return s1.value_case() < s2.value_case(); +} + +bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) { + if (s1.size() != s2.size()) { + return s1.size() < s2.size(); } - switch (s1.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return s1.value_str() < s2.value_str(); - case DimensionsValue::ValueCase::kValueInt: - return s1.value_int() < s2.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return s1.value_long() < s2.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return (int)s1.value_bool() < (int)s2.value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return s1.value_float() < s2.value_float(); - case DimensionsValue::ValueCase::kValueTuple: - { - if (s1.value_tuple().dimensions_value_size() != - s2.value_tuple().dimensions_value_size()) { - return s1.value_tuple().dimensions_value_size() < - s2.value_tuple().dimensions_value_size(); - } - for (int i = 0; i < s1.value_tuple().dimensions_value_size(); ++i) { - if (EqualsTo(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i))) { - continue; - } else { - return LessThan(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i)); - } - } - return false; - } - case DimensionsValue::ValueCase::VALUE_NOT_SET: - default: - return false; + + size_t count = s1.size(); + for (size_t i = 0; i < count; i++) { + if (s1[i] != s2[i]) { + return s1[i] < s2[i]; + } } + return false; } bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const { - return EqualsTo(getDimensionsValue(), that.getDimensionsValue()); + if (mValues.size() != that.getValues().size()) { + return false; + } + size_t count = mValues.size(); + for (size_t i = 0; i < count; i++) { + if (mValues[i] != (that.getValues())[i]) { + return false; + } + } + return true; }; bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const { - return LessThan(getDimensionsValue(), that.getDimensionsValue()); + return LessThan(getValues(), that.getValues()); }; -string MetricDimensionKey::toString() const { - string flattened = mDimensionKeyInWhat.toString(); - flattened += mDimensionKeyInCondition.toString(); - return flattened; +bool HashableDimensionKey::contains(const HashableDimensionKey& that) const { + if (mValues.size() < that.getValues().size()) { + return false; + } + + if (mValues.size() == that.getValues().size()) { + return (*this) == that; + } + + for (const auto& value : that.getValues()) { + bool found = false; + for (const auto& myValue : mValues) { + if (value.mField == myValue.mField && value.mValue == myValue.mValue) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + + return true; +} + +string HashableDimensionKey::toString() const { + std::string output; + for (const auto& value : mValues) { + output += StringPrintf("(%d)%#x->%s ", value.mField.getTag(), value.mField.getField(), + value.mValue.toString().c_str()); + } + return output; } bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const { return mDimensionKeyInWhat == that.getDimensionKeyInWhat() && - mDimensionKeyInCondition == that.getDimensionKeyInCondition(); + mDimensionKeyInCondition == that.getDimensionKeyInCondition(); }; +string MetricDimensionKey::toString() const { + return mDimensionKeyInWhat.toString() + mDimensionKeyInCondition.toString(); +} + bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const { - return toString().compare(that.toString()) < 0; -}; + if (mDimensionKeyInWhat < that.getDimensionKeyInWhat()) { + return true; + } else if (that.getDimensionKeyInWhat() < mDimensionKeyInWhat) { + return false; + } -bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) { - return EqualsTo(s1, s2); + return mDimensionKeyInCondition < that.getDimensionKeyInCondition(); } + } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index a31d7a6d85c6..89fe317834d8 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -17,44 +17,66 @@ #pragma once #include <utils/JenkinsHash.h> -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include <vector> +#include "FieldValue.h" +#include "android-base/stringprintf.h" +#include "logd/LogEvent.h" namespace android { namespace os { namespace statsd { +using android::base::StringPrintf; + +struct Metric2Condition { + int64_t conditionId; + std::vector<Matcher> metricFields; + std::vector<Matcher> conditionFields; +}; + class HashableDimensionKey { public: - explicit HashableDimensionKey(const DimensionsValue& dimensionsValue) - : mDimensionsValue(dimensionsValue){}; + explicit HashableDimensionKey(const std::vector<FieldValue>& values) { + mValues = values; + } HashableDimensionKey(){}; - HashableDimensionKey(const HashableDimensionKey& that) - : mDimensionsValue(that.getDimensionsValue()){}; - - HashableDimensionKey& operator=(const HashableDimensionKey& from) = default; + HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){}; - std::string toString() const; + inline void addValue(const FieldValue& value) { + mValues.push_back(value); + } - inline const DimensionsValue& getDimensionsValue() const { - return mDimensionsValue; + inline const std::vector<FieldValue>& getValues() const { + return mValues; } - inline DimensionsValue* getMutableDimensionsValue() { - return &mDimensionsValue; + inline std::vector<FieldValue>* mutableValues() { + return &mValues; } - bool operator==(const HashableDimensionKey& that) const; + inline FieldValue* mutableValue(size_t i) { + if (i >= 0 && i < mValues.size()) { + return &(mValues[i]); + } + return nullptr; + } - bool operator<(const HashableDimensionKey& that) const; + std::string toString() const; inline const char* c_str() const { return toString().c_str(); } + bool operator==(const HashableDimensionKey& that) const; + + bool operator<(const HashableDimensionKey& that) const; + + bool contains(const HashableDimensionKey& that) const; + private: - DimensionsValue mDimensionsValue; + std::vector<FieldValue> mValues; }; class MetricDimensionKey { @@ -83,7 +105,7 @@ class MetricDimensionKey { } bool hasDimensionKeyInCondition() const { - return mDimensionKeyInCondition.getDimensionsValue().has_field(); + return mDimensionKeyInCondition.getValues().size() > 0; } bool operator==(const MetricDimensionKey& that) const; @@ -98,11 +120,32 @@ class MetricDimensionKey { HashableDimensionKey mDimensionKeyInCondition; }; -bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2); +android::hash_t hashDimension(const HashableDimensionKey& key); + +/** + * Creating HashableDimensionKeys from FieldValues using matcher. + * + * This function may make modifications to the Field if the matcher has Position=LAST or ANY in + * it. This is because: for example, when we create dimension from last uid in attribution chain, + * In one event, uid 1000 is at position 5 and it's the last + * In another event, uid 1000 is at position 6, and it's the last + * these 2 events should be mapped to the same dimension. So we will remove the original position + * from the dimension key for the uid field (by applying 0x80 bit mask). + */ +bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& values, + std::vector<HashableDimensionKey>* output); + +/** + * Filter the values from FieldValues using the matchers. + * + * In contrast to the above function, this function will not do any modification to the original + * data. Considering it as taking a snapshot on the atom event. + */ +void filterGaugeValues(const std::vector<Matcher>& matchers, const std::vector<FieldValue>& values, + std::vector<FieldValue>* output); -android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value); -android::hash_t hashDimensionsValue(const DimensionsValue& value); -android::hash_t hashMetricDimensionKey(int64_t see, const MetricDimensionKey& dimensionKey); +void getDimensionForCondition(const LogEvent& event, Metric2Condition links, + std::vector<HashableDimensionKey>* conditionDimension); } // namespace statsd } // namespace os @@ -116,17 +159,15 @@ using android::os::statsd::MetricDimensionKey; template <> struct hash<HashableDimensionKey> { std::size_t operator()(const HashableDimensionKey& key) const { - return hashDimensionsValue(key.getDimensionsValue()); + return hashDimension(key); } }; template <> struct hash<MetricDimensionKey> { std::size_t operator()(const MetricDimensionKey& key) const { - android::hash_t hash = hashDimensionsValue( - key.getDimensionKeyInWhat().getDimensionsValue()); - hash = android::JenkinsHashMix(hash, - hashDimensionsValue(key.getDimensionKeyInCondition().getDimensionsValue())); + android::hash_t hash = hashDimension(key.getDimensionKeyInWhat()); + hash = android::JenkinsHashMix(hash, hashDimension(key.getDimensionKeyInCondition())); return android::JenkinsHashWhiten(hash); } }; diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 7662c4025c42..4fac5aa7c8e7 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG false // STOPSHIP if true +#define DEBUG true // STOPSHIP if true #include "Log.h" #include "statslog.h" @@ -25,8 +25,6 @@ #include "guardrail/StatsdStats.h" #include "metrics/CountMetricProducer.h" #include "external/StatsPullerManager.h" -#include "dimension.h" -#include "field_util.h" #include "stats_util.h" #include "storage/StorageManager.h" @@ -93,27 +91,31 @@ void StatsLogProcessor::onAnomalyAlarmFired( } } +void updateUid(Value* value, int hostUid) { + int uid = value->int_value; + if (uid != hostUid) { + value->setInt(hostUid); + } +} + void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const { - std::set<Field, FieldCmp> uidFields; if (android::util::kAtomsWithAttributionChain.find(event->GetTagId()) != android::util::kAtomsWithAttributionChain.end()) { - FieldMatcher matcher; - buildAttributionUidFieldMatcher(event->GetTagId(), Position::ANY, &matcher); - findFields(event->getFieldValueMap(), matcher, &uidFields); - } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) != - android::util::kAtomsWithUidField.end()) { - FieldMatcher matcher; - buildSimpleAtomFieldMatcher( - event->GetTagId(), 1 /* uid is always the 1st field. */, &matcher); - findFields(event->getFieldValueMap(), matcher, &uidFields); - } - - for (const auto& uidField : uidFields) { - DimensionsValue* value = event->findFieldValueOrNull(uidField); - if (value != nullptr && value->value_case() == DimensionsValue::ValueCase::kValueInt) { - const int uid = mUidMap->getHostUidOrSelf(value->value_int()); - value->set_value_int(uid); + for (auto& value : *(event->getMutableValues())) { + if (value.mField.getPosAtDepth(0) > kAttributionField) { + break; + } + if (isAttributionUidField(value)) { + const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value); + updateUid(&value.mValue, hostUid); + } } + } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) != + android::util::kAtomsWithUidField.end() && + event->getValues().size() > 0 && (event->getValues())[0].mValue.getType() == INT) { + Value& value = (*event->getMutableValues())[0].mValue; + const int hostUid = mUidMap->getHostUidOrSelf(value.int_value); + updateUid(&value, hostUid); } } @@ -212,27 +214,14 @@ void StatsLogProcessor::dumpStates(FILE* out, bool verbose) { } } -void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, - ConfigMetricsReportList* report) { +void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t dumpTimeStampNs, + vector<uint8_t>* outData) { std::lock_guard<std::mutex> lock(mMetricsMutex); - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - ALOGW("Config source %s does not exist", key.ToString().c_str()); - return; - } - report->mutable_config_key()->set_uid(key.GetUid()); - report->mutable_config_key()->set_id(key.GetId()); - ConfigMetricsReport* configMetricsReport = report->add_reports(); - it->second->onDumpReport(dumpTimeStampNs, configMetricsReport); - // TODO: dump uid mapping. + onDumpReportLocked(key, dumpTimeStampNs, outData); } -void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) { - std::lock_guard<std::mutex> lock(mMetricsMutex); - onDumpReportLocked(key, outData); -} - -void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, vector<uint8_t>* outData) { +void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeStampNs, + vector<uint8_t>* outData) { auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end()) { ALOGW("Config source %s does not exist", key.ToString().c_str()); @@ -258,7 +247,7 @@ void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, vector<uint8_t> // First, fill in ConfigMetricsReport using current data on memory, which // starts from filling in StatsLogReport's. - it->second->onDumpReport(&proto); + it->second->onDumpReport(dumpTimeStampNs, &proto); // Fill in UidMap. auto uidMap = mUidMap->getOutput(key); @@ -292,6 +281,7 @@ void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, vector<uint8_t> iter.rp()->move(toRead); } } + StatsdStats::getInstance().noteMetricsReportSent(key); } @@ -327,7 +317,7 @@ void StatsLogProcessor::flushIfNecessaryLocked( StatsdStats::kMaxMetricsBytesPerConfig) { // Too late. We need to start clearing data. // TODO(b/70571383): By 12/15/2017 add API to drop data directly ProtoOutputStream proto; - metricsManager.onDumpReport(&proto); + metricsManager.onDumpReport(time(nullptr) * NS_PER_SEC, &proto); StatsdStats::getInstance().noteDataDropped(key); VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str()); } else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) { @@ -351,7 +341,7 @@ void StatsLogProcessor::WriteDataToDisk() { for (auto& pair : mMetricsManagers) { const ConfigKey& key = pair.first; vector<uint8_t> data; - onDumpReportLocked(key, &data); + onDumpReportLocked(key, time(nullptr) * NS_PER_SEC, &data); // TODO: Add a guardrail to prevent accumulation of file on disk. string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, time(nullptr), key.GetUid(), (long long)key.GetId()); diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 8bbcd751252a..144430639d9f 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -46,9 +46,7 @@ public: size_t GetMetricsSize(const ConfigKey& key) const; - void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData); - void onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, - ConfigMetricsReportList* report); + void onDumpReport(const ConfigKey& key, const uint64_t dumpTimeNs, vector<uint8_t>* outData); /* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */ void onAnomalyAlarmFired( @@ -80,7 +78,8 @@ private: sp<AnomalyMonitor> mAnomalyMonitor; - void onDumpReportLocked(const ConfigKey& key, vector<uint8_t>* outData); + void onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeNs, + vector<uint8_t>* outData); /* Check if we should send a broadcast if approaching memory limits and if we're over, we * actually delete the data. */ @@ -105,9 +104,14 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration); - FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3); + FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1); + FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice); FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index c24efec4c4fc..e111f5854a34 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -492,7 +492,8 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String } if (good) { vector<uint8_t> data; - mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), &data); + mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), time(nullptr) * NS_PER_SEC, + &data); // TODO: print the returned StatsLogReport to file instead of printing to logcat. if (proto) { for (size_t i = 0; i < data.size(); i ++) { @@ -780,7 +781,7 @@ Status StatsService::getData(int64_t key, vector<uint8_t>* output) { VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid()); if (checkCallingPermission(String16(kPermissionDump))) { ConfigKey configKey(ipc->getCallingUid(), key); - mProcessor->onDumpReport(configKey, output); + mProcessor->onDumpReport(configKey, time(nullptr) * NS_PER_SEC, output); return Status::ok(); } else { return Status::fromExceptionCode(binder::Status::EX_SECURITY); diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index ba16ec83a5c5..443d33d39915 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -92,6 +92,9 @@ void AnomalyTracker::flushPastBuckets(const int64_t& latestPastBucketNum) { void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue, const int64_t& bucketNum) { + if (mNumOfPastBuckets == 0) { + return; + } flushPastBuckets(bucketNum); auto& bucket = mPastBuckets[index(bucketNum)]; @@ -106,6 +109,9 @@ void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t& void AnomalyTracker::addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum) { VLOG("addPastBucket() called."); + if (mNumOfPastBuckets == 0) { + return; + } flushPastBuckets(bucketNum); // Replace the oldest bucket with the new bucket we are adding. mPastBuckets[index(bucketNum)] = bucketValues; diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index 4c20ccb61afe..13a2b7be9152 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -106,11 +106,9 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf } void CombinationConditionTracker::isConditionMet( - const ConditionKey& conditionParameters, - const vector<sp<ConditionTracker>>& allConditions, - const FieldMatcher& dimensionFields, - vector<ConditionState>& conditionCache, - std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const { + const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, + const std::vector<Matcher>& dimensionFields, vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const { // So far, this is fine as there is at most one child having sliced output. for (const int childIndex : mChildren) { if (conditionCache[childIndex] == ConditionState::kNotEvaluated) { @@ -169,8 +167,8 @@ void CombinationConditionTracker::evaluateCondition( ConditionState CombinationConditionTracker::getMetConditionDimension( const std::vector<sp<ConditionTracker>>& allConditions, - const FieldMatcher& dimensionFields, - std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const { + const std::vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const { vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated); // So far, this is fine as there is at most one child having sliced output. for (const int childIndex : mChildren) { diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index 0b7f9492d628..ba185f6661ec 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -41,17 +41,17 @@ public: std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache) override; - void isConditionMet( - const ConditionKey& conditionParameters, - const std::vector<sp<ConditionTracker>>& allConditions, - const FieldMatcher& dimensionFields, - std::vector<ConditionState>& conditionCache, - std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override; + void isConditionMet(const ConditionKey& conditionParameters, + const std::vector<sp<ConditionTracker>>& allConditions, + const vector<Matcher>& dimensionFields, + std::vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override; ConditionState getMetConditionDimension( const std::vector<sp<ConditionTracker>>& allConditions, - const FieldMatcher& dimensionFields, - std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override; + const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override; + private: LogicalOperation mLogicalOperation; diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 81abbdb36ee4..2612a9a06da1 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -90,14 +90,13 @@ public: virtual void isConditionMet( const ConditionKey& conditionParameters, const std::vector<sp<ConditionTracker>>& allConditions, - const FieldMatcher& dimensionFields, - std::vector<ConditionState>& conditionCache, - std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0; + const vector<Matcher>& dimensionFields, std::vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0; virtual ConditionState getMetConditionDimension( const std::vector<sp<ConditionTracker>>& allConditions, - const FieldMatcher& dimensionFields, - std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0; + const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0; // return the list of LogMatchingTracker index that this ConditionTracker uses. virtual const std::set<int>& getLogTrackerIndex() const { diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp index 0427700fec91..c8722c362fe6 100644 --- a/cmds/statsd/src/condition/ConditionWizard.cpp +++ b/cmds/statsd/src/condition/ConditionWizard.cpp @@ -24,11 +24,9 @@ using std::map; using std::string; using std::vector; -ConditionState ConditionWizard::query( - const int index, const ConditionKey& parameters, - const FieldMatcher& dimensionFields, - std::unordered_set<HashableDimensionKey> *dimensionKeySet) { - +ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters, + const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>* dimensionKeySet) { vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated); mAllConditions[index]->isConditionMet( @@ -37,9 +35,8 @@ ConditionState ConditionWizard::query( } ConditionState ConditionWizard::getMetConditionDimension( - const int index, const FieldMatcher& dimensionFields, - std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const { - + const int index, const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const { return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields, *dimensionsKeySet); } diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h index b38b59ff4cd0..4831d5622a3d 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -39,16 +39,13 @@ public: // condition. // The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case, // the conditionParameters contains the parameters for it's children SimpleConditionTrackers. - virtual ConditionState query( - const int conditionIndex, - const ConditionKey& conditionParameters, - const FieldMatcher& dimensionFields, - std::unordered_set<HashableDimensionKey> *dimensionKeySet); + virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters, + const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>* dimensionKeySet); virtual ConditionState getMetConditionDimension( - const int index, - const FieldMatcher& dimensionFields, - std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const; + const int index, const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const; private: std::vector<sp<ConditionTracker>> mAllConditions; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index 25265d5dabd7..624119f3ad04 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -19,7 +19,6 @@ #include "SimpleConditionTracker.h" #include "guardrail/StatsdStats.h" -#include "dimension.h" #include <log/logprint.h> @@ -77,10 +76,12 @@ SimpleConditionTracker::SimpleConditionTracker( mStopAllLogMatcherIndex = -1; } - mOutputDimensions = simplePredicate.dimensions(); - - if (mOutputDimensions.child_size() > 0) { - mSliced = true; + if (simplePredicate.has_dimensions()) { + translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions); + if (mOutputDimensions.size() > 0) { + mSliced = true; + mDimensionTag = mOutputDimensions[0].mMatcher.getTag(); + } } if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) { @@ -104,13 +105,10 @@ bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, vector<bool>& stack) { // SimpleConditionTracker does not have dependency on other conditions, thus we just return // if the initialization was successful. - if (mOutputDimensions.has_field() || mOutputDimensions.child_size() > 0) { - setSliced(true); - } return mInitialized; } -void print(map<HashableDimensionKey, int>& conditions, const int64_t& id) { +void print(const map<HashableDimensionKey, int>& conditions, const int64_t& id) { VLOG("%lld DUMP:", (long long)id); for (const auto& pair : conditions) { VLOG("\t%s : %d", pair.first.c_str(), pair.second); @@ -151,24 +149,15 @@ bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) { } void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey, - bool matchStart, - std::vector<ConditionState>& conditionCache, - std::vector<bool>& conditionChangedCache) { - if ((int)conditionChangedCache.size() <= mIndex) { - ALOGE("handleConditionEvent: param conditionChangedCache not initialized."); - return; - } - if ((int)conditionCache.size() <= mIndex) { - ALOGE("handleConditionEvent: param conditionCache not initialized."); - return; - } + bool matchStart, ConditionState* conditionCache, + bool* conditionChangedCache) { bool changed = false; auto outputIt = mSlicedConditionState.find(outputKey); ConditionState newCondition; if (hitGuardRail(outputKey)) { - conditionChangedCache[mIndex] = false; + (*conditionChangedCache) = false; // Tells the caller it's evaluated. - conditionCache[mIndex] = ConditionState::kUnknown; + (*conditionCache) = ConditionState::kUnknown; return; } if (outputIt == mSlicedConditionState.end()) { @@ -230,9 +219,8 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou print(mSlicedConditionState, mConditionId); } - conditionChangedCache[mIndex] = changed; - conditionCache[mIndex] = newCondition; - + (*conditionChangedCache) = changed; + (*conditionCache) = newCondition; VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId, conditionChangedCache[mIndex] == true); } @@ -292,42 +280,42 @@ void SimpleConditionTracker::evaluateCondition( return; } - // outputKey is the output values. e.g, uid:1234 - std::vector<DimensionsValue> outputValues; - getDimensionKeys(event, mOutputDimensions, &outputValues); - if (outputValues.size() == 0) { - // The original implementation would generate an empty string dimension hash when condition - // is not sliced. - handleConditionEvent( - DEFAULT_DIMENSION_KEY, matchedState == 1, conditionCache, conditionChangedCache); - } else if (outputValues.size() == 1) { - handleConditionEvent(HashableDimensionKey(outputValues[0]), matchedState == 1, - conditionCache, conditionChangedCache); + ConditionState overallState = mInitialValue; + bool overallChanged = false; + + if (mOutputDimensions.size() == 0) { + handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState, + &overallChanged); } else { + std::vector<HashableDimensionKey> outputValues; + filterValues(mOutputDimensions, event.getValues(), &outputValues); + // If this event has multiple nodes in the attribution chain, this log event probably will // generate multiple dimensions. If so, we will find if the condition changes for any // dimension and ask the corresponding metric producer to verify whether the actual sliced // condition has changed or not. // A high level assumption is that a predicate is either sliced or unsliced. We will never // have both sliced and unsliced version of a predicate. - for (const DimensionsValue& outputValue : outputValues) { - vector<ConditionState> dimensionalConditionCache(conditionCache.size(), - ConditionState::kNotEvaluated); - vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false); - handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1, - dimensionalConditionCache, dimensionalConditionChangedCache); - OrConditionState(dimensionalConditionCache, &conditionCache); - OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache); + for (const HashableDimensionKey& outputValue : outputValues) { + // For sliced conditions, the value in the cache is not used. We don't need to update + // the overall condition state. + ConditionState tempState = ConditionState::kUnknown; + bool tempChanged = false; + handleConditionEvent(outputValue, matchedState == 1, &tempState, &tempChanged); + if (tempChanged) { + overallChanged = true; + } } } + conditionCache[mIndex] = overallState; + conditionChangedCache[mIndex] = overallChanged; } void SimpleConditionTracker::isConditionMet( - const ConditionKey& conditionParameters, - const vector<sp<ConditionTracker>>& allConditions, - const FieldMatcher& dimensionFields, - vector<ConditionState>& conditionCache, - std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const { + const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, + const vector<Matcher>& dimensionFields, vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const { + if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { // it has been evaluated. VLOG("Yes, already evaluated, %lld %d", @@ -338,8 +326,7 @@ void SimpleConditionTracker::isConditionMet( if (pair == conditionParameters.end()) { ConditionState conditionState = ConditionState::kNotEvaluated; - if (dimensionFields.has_field() && dimensionFields.child_size() > 0 && - dimensionFields.field() == mOutputDimensions.field()) { + if (dimensionFields.size() > 0 && dimensionFields[0].mMatcher.getTag() == mDimensionTag) { conditionState = conditionState | getMetConditionDimension( allConditions, dimensionFields, dimensionsKeySet); } else { @@ -368,12 +355,10 @@ void SimpleConditionTracker::isConditionMet( ConditionState sliceState = startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; conditionState = conditionState | sliceState; - if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) { - HashableDimensionKey dimensionKey; - if (getSubDimension(startedCountIt->first.getDimensionsValue(), dimensionFields, - dimensionKey.getMutableDimensionsValue())) { - dimensionsKeySet.insert(dimensionKey); - } + if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) { + vector<HashableDimensionKey> dimensionKeys; + filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKeys); + dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end()); } } else { // For unseen key, check whether the require dimensions are subset of sliced condition @@ -382,31 +367,29 @@ void SimpleConditionTracker::isConditionMet( for (const auto& slice : mSlicedConditionState) { ConditionState sliceState = slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - if (IsSubDimension(slice.first.getDimensionsValue(), key.getDimensionsValue())) { + if (slice.first.contains(key)) { conditionState = conditionState | sliceState; - if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) { - HashableDimensionKey dimensionKey; - if (getSubDimension(slice.first.getDimensionsValue(), - dimensionFields, dimensionKey.getMutableDimensionsValue())) { - dimensionsKeySet.insert(dimensionKey); - } + if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) { + vector<HashableDimensionKey> dimensionKeys; + filterValues(dimensionFields, slice.first.getValues(), &dimensionKeys); + + dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end()); + } } } } } - } conditionCache[mIndex] = conditionState; VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]); } ConditionState SimpleConditionTracker::getMetConditionDimension( const std::vector<sp<ConditionTracker>>& allConditions, - const FieldMatcher& dimensionFields, - std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const { + const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const { ConditionState conditionState = mInitialValue; - if (!dimensionFields.has_field() || - !mOutputDimensions.has_field() || - dimensionFields.field() != mOutputDimensions.field()) { + if (dimensionFields.size() == 0 || mOutputDimensions.size() == 0 || + dimensionFields[0].mMatcher.getTag() != mOutputDimensions[0].mMatcher.getTag()) { const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY); if (itr != mSlicedConditionState.end()) { ConditionState sliceState = @@ -419,13 +402,13 @@ ConditionState SimpleConditionTracker::getMetConditionDimension( for (const auto& slice : mSlicedConditionState) { ConditionState sliceState = slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - DimensionsValue dimensionsValue; conditionState = conditionState | sliceState; - HashableDimensionKey dimensionKey; - if (sliceState == ConditionState::kTrue && - getSubDimension(slice.first.getDimensionsValue(), dimensionFields, - dimensionKey.getMutableDimensionsValue())) { - dimensionsKeySet.insert(dimensionKey); + + if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) { + vector<HashableDimensionKey> dimensionKeys; + filterValues(dimensionFields, slice.first.getValues(), &dimensionKeys); + + dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end()); } } return conditionState; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index ce9a02d4a795..c56512935297 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -48,14 +48,14 @@ public: void isConditionMet(const ConditionKey& conditionParameters, const std::vector<sp<ConditionTracker>>& allConditions, - const FieldMatcher& dimensionFields, + const vector<Matcher>& dimensionFields, std::vector<ConditionState>& conditionCache, - std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override; + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override; ConditionState getMetConditionDimension( const std::vector<sp<ConditionTracker>>& allConditions, - const FieldMatcher& dimensionFields, - std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override; + const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override; private: const ConfigKey mConfigKey; @@ -73,17 +73,17 @@ private: ConditionState mInitialValue; - FieldMatcher mOutputDimensions; + std::vector<Matcher> mOutputDimensions; + + int mDimensionTag; std::map<HashableDimensionKey, int> mSlicedConditionState; void handleStopAll(std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache); - void handleConditionEvent(const HashableDimensionKey& outputKey, - bool matchStart, - std::vector<ConditionState>& conditionCache, - std::vector<bool>& changedCache); + void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart, + ConditionState* conditionCache, bool* changedCache); bool hitGuardRail(const HashableDimensionKey& newKey); diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp index 0ab33cfbaea1..691356b5edc6 100644 --- a/cmds/statsd/src/condition/condition_util.cpp +++ b/cmds/statsd/src/condition/condition_util.cpp @@ -27,7 +27,6 @@ #include "ConditionTracker.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" -#include "dimension.h" namespace android { namespace os { @@ -97,109 +96,6 @@ ConditionState evaluateCombinationCondition(const std::vector<int>& children, ConditionState operator|(ConditionState l, ConditionState r) { return l >= r ? l : r; } - -void OrConditionState(const std::vector<ConditionState>& ref, vector<ConditionState> * ored) { - if (ref.size() != ored->size()) { - return; - } - for (size_t i = 0; i < ored->size(); ++i) { - ored->at(i) = ored->at(i) | ref.at(i); - } -} - -void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored) { - if (ref.size() != ored->size()) { - return; - } - for (size_t i = 0; i < ored->size(); ++i) { - ored->at(i) = ored->at(i) | ref.at(i); - } -} - -void getFieldsFromFieldMatcher(const FieldMatcher& matcher, Field* rootField, Field* leafField, - std::vector<Field> *allFields) { - if (matcher.has_position()) { - leafField->set_position_index(0); - } - if (matcher.child_size() == 0) { - allFields->push_back(*rootField); - return; - } - for (int i = 0; i < matcher.child_size(); ++i) { - Field* newLeafField = leafField->add_child(); - newLeafField->set_field(matcher.child(i).field()); - getFieldsFromFieldMatcher(matcher.child(i), rootField, newLeafField, allFields); - } -} - -void getFieldsFromFieldMatcher(const FieldMatcher& matcher, std::vector<Field> *allFields) { - if (!matcher.has_field()) { - return; - } - Field rootField; - rootField.set_field(matcher.field()); - getFieldsFromFieldMatcher(matcher, &rootField, &rootField, allFields); -} - -void flattenValueLeaves(const DimensionsValue& value, - std::vector<const DimensionsValue*> *allLaves) { - switch (value.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::kValueInt: - case DimensionsValue::ValueCase::kValueLong: - case DimensionsValue::ValueCase::kValueBool: - case DimensionsValue::ValueCase::kValueFloat: - case DimensionsValue::ValueCase::VALUE_NOT_SET: - allLaves->push_back(&value); - break; - case DimensionsValue::ValueCase::kValueTuple: - for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) { - flattenValueLeaves(value.value_tuple().dimensions_value(i), allLaves); - } - break; - } -} - -void getDimensionKeysForCondition( - const LogEvent& event, const MetricConditionLink& link, - std::vector<HashableDimensionKey> *hashableDimensionKeys) { - std::vector<Field> whatFields; - getFieldsFromFieldMatcher(link.fields_in_what(), &whatFields); - std::vector<Field> conditionFields; - getFieldsFromFieldMatcher(link.fields_in_condition(), &conditionFields); - - // TODO(yanglu): here we could simplify the logic to get the leaf value node in what and - // directly construct the full condition value tree. - std::vector<DimensionsValue> whatValues; - getDimensionKeys(event, link.fields_in_what(), &whatValues); - - for (size_t i = 0; i < whatValues.size(); ++i) { - std::vector<const DimensionsValue*> whatLeaves; - flattenValueLeaves(whatValues[i], &whatLeaves); - if (whatLeaves.size() != whatFields.size() || - whatLeaves.size() != conditionFields.size()) { - ALOGE("Dimensions between what and condition not equal."); - return; - } - FieldValueMap conditionValueMap; - for (size_t j = 0; j < whatLeaves.size(); ++j) { - DimensionsValue* conditionValue = &conditionValueMap[conditionFields[j]]; - *conditionValue = *whatLeaves[i]; - if (!setFieldInLeafValueProto(conditionFields[j], conditionValue)) { - ALOGE("Not able to reset the field for condition leaf value."); - return; - } - } - std::vector<DimensionsValue> conditionValueTrees; - findDimensionsValues(conditionValueMap, link.fields_in_condition(), &conditionValueTrees); - if (conditionValueTrees.size() != 1) { - ALOGE("Not able to find unambiguous field value in condition atom."); - continue; - } - hashableDimensionKeys->push_back(HashableDimensionKey(conditionValueTrees[0])); - } -} - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h index a7288beb69ca..fed90ec3da37 100644 --- a/cmds/statsd/src/condition/condition_util.h +++ b/cmds/statsd/src/condition/condition_util.h @@ -33,16 +33,10 @@ enum ConditionState { }; ConditionState operator|(ConditionState l, ConditionState r); -void OrConditionState(const std::vector<ConditionState>& ref, vector<ConditionState> * ored); -void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored); ConditionState evaluateCombinationCondition(const std::vector<int>& children, const LogicalOperation& operation, const std::vector<ConditionState>& conditionCache); - -void getDimensionKeysForCondition( - const LogEvent& event, const MetricConditionLink& link, - std::vector<HashableDimensionKey> *dimensionKeys); } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp deleted file mode 100644 index 8a2e87128319..000000000000 --- a/cmds/statsd/src/dimension.cpp +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Log.h" - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_internal.pb.h" -#include "dimension.h" - - -namespace android { -namespace os { -namespace statsd { - -const DimensionsValue* getSingleLeafValue(const DimensionsValue* value) { - if (value->value_case() == DimensionsValue::ValueCase::kValueTuple) { - return getSingleLeafValue(&value->value_tuple().dimensions_value(0)); - } else { - return value; - } -} - -DimensionsValue getSingleLeafValue(const DimensionsValue& value) { - const DimensionsValue* leafValue = getSingleLeafValue(&value); - return *leafValue; -} - -void appendLeafNodeToTree(const Field& field, - const DimensionsValue& value, - DimensionsValue* parentValue) { - if (field.child_size() <= 0) { - *parentValue = value; - parentValue->set_field(field.field()); - return; - } - parentValue->set_field(field.field()); - int idx = -1; - for (int i = 0; i < parentValue->mutable_value_tuple()->dimensions_value_size(); ++i) { - if (parentValue->mutable_value_tuple()->dimensions_value(i).field() == - field.child(0).field()) { - idx = i; - } - } - if (idx < 0) { - parentValue->mutable_value_tuple()->add_dimensions_value(); - idx = parentValue->mutable_value_tuple()->dimensions_value_size() - 1; - } - appendLeafNodeToTree( - field.child(0), value, - parentValue->mutable_value_tuple()->mutable_dimensions_value(idx)); -} - -void appendLeafNodeToTrees(const Field& field, - const DimensionsValue& node, - std::vector<DimensionsValue>* rootTrees) { - if (rootTrees == nullptr) { - return; - } - if (rootTrees->empty()) { - DimensionsValue tree; - appendLeafNodeToTree(field, node, &tree); - rootTrees->push_back(tree); - } else { - for (size_t i = 0; i < rootTrees->size(); ++i) { - appendLeafNodeToTree(field, node, &rootTrees->at(i)); - } - } -} - -namespace { - -void findDimensionsValues( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::vector<DimensionsValue>* rootDimensionsValues); - -void findNonRepeatedDimensionsValues( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::vector<DimensionsValue>* rootValues) { - if (matcher.child_size() > 0) { - Field* newLeafField = leafField->add_child(); - for (const auto& childMatcher : matcher.child()) { - newLeafField->set_field(childMatcher.field()); - findDimensionsValues(fieldValueMap, childMatcher, rootField, newLeafField, rootValues); - } - leafField->clear_child(); - } else { - auto ret = fieldValueMap.equal_range(*rootField); - int found = 0; - for (auto it = ret.first; it != ret.second; ++it) { - found++; - } - // Not found. - if (found <= 0) { - return; - } - if (found > 1) { - ALOGE("Found multiple values for optional field."); - return; - } - appendLeafNodeToTrees(*rootField, ret.first->second, rootValues); - } -} - -void findRepeatedDimensionsValues(const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::vector<DimensionsValue>* rootValues) { - if (matcher.position() == Position::FIRST) { - leafField->set_position_index(0); - findNonRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField, rootValues); - leafField->clear_position_index(); - } else { - auto itLower = fieldValueMap.lower_bound(*rootField); - if (itLower == fieldValueMap.end()) { - return; - } - const int leafFieldNum = leafField->field(); - leafField->set_field(leafFieldNum + 1); - auto itUpper = fieldValueMap.lower_bound(*rootField); - // Resets the field number. - leafField->set_field(leafFieldNum); - - switch (matcher.position()) { - case Position::LAST: - { - itUpper--; - if (itUpper != fieldValueMap.end()) { - int last_index = getPositionByReferenceField(*rootField, itUpper->first); - if (last_index < 0) { - return; - } - leafField->set_position_index(last_index); - findNonRepeatedDimensionsValues( - fieldValueMap, matcher, rootField, leafField, rootValues); - leafField->clear_position_index(); - } - } - break; - case Position::ANY: - { - std::set<int> indexes; - for (auto it = itLower; it != itUpper; ++it) { - int index = getPositionByReferenceField(*rootField, it->first); - if (index >= 0) { - indexes.insert(index); - } - } - if (!indexes.empty()) { - std::vector<DimensionsValue> allValues; - for (const int index : indexes) { - leafField->set_position_index(index); - std::vector<DimensionsValue> newValues = *rootValues; - findNonRepeatedDimensionsValues( - fieldValueMap, matcher, rootField, leafField, &newValues); - allValues.insert(allValues.end(), newValues.begin(), newValues.end()); - leafField->clear_position_index(); - } - rootValues->clear(); - rootValues->insert(rootValues->end(), allValues.begin(), allValues.end()); - } - } - break; - default: - break; - } - } -} - -void findDimensionsValues( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::vector<DimensionsValue>* rootDimensionsValues) { - if (!matcher.has_position()) { - findNonRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField, - rootDimensionsValues); - } else { - findRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField, - rootDimensionsValues); - } -} - -} // namespace - -void findDimensionsValues( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - std::vector<DimensionsValue>* rootDimensionsValues) { - Field rootField; - buildSimpleAtomField(matcher.field(), &rootField); - findDimensionsValues(fieldValueMap, matcher, &rootField, &rootField, rootDimensionsValues); -} - -void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) { - matcher->set_field(tagId); -} - -void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatcher* matcher) { - matcher->set_field(tagId); - matcher->add_child()->set_field(fieldNum); -} - -constexpr int ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO = 1; -constexpr int UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 1; -constexpr int TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 2; - -void buildAttributionUidFieldMatcher(const int tagId, const Position position, - FieldMatcher* matcher) { - matcher->set_field(tagId); - auto child = matcher->add_child(); - child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO); - child->set_position(position); - child->add_child()->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); -} - -void buildAttributionTagFieldMatcher(const int tagId, const Position position, - FieldMatcher* matcher) { - matcher->set_field(tagId); - FieldMatcher* child = matcher->add_child(); - child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO); - child->set_position(position); - child->add_child()->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); -} - -void buildAttributionFieldMatcher(const int tagId, const Position position, FieldMatcher* matcher) { - matcher->set_field(tagId); - FieldMatcher* child = matcher->add_child(); - child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO); - child->set_position(position); - child->add_child()->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); - child->add_child()->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); -} - -void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) { - if (!value.has_field()) { - return; - } - *flattened += std::to_string(value.field()); - *flattened += ":"; - switch (value.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - *flattened += value.value_str(); - break; - case DimensionsValue::ValueCase::kValueInt: - *flattened += std::to_string(value.value_int()); - break; - case DimensionsValue::ValueCase::kValueLong: - *flattened += std::to_string(value.value_long()); - break; - case DimensionsValue::ValueCase::kValueBool: - *flattened += std::to_string(value.value_bool()); - break; - case DimensionsValue::ValueCase::kValueFloat: - *flattened += std::to_string(value.value_float()); - break; - case DimensionsValue::ValueCase::kValueTuple: - { - *flattened += "{"; - for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) { - DimensionsValueToString(value.value_tuple().dimensions_value(i), flattened); - *flattened += "|"; - } - *flattened += "}"; - } - break; - case DimensionsValue::ValueCase::VALUE_NOT_SET: - break; - } -} - -std::string DimensionsValueToString(const DimensionsValue& value) { - std::string flatten; - DimensionsValueToString(value, &flatten); - return flatten; -} - -bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub) { - if (dimension.field() != sub.field()) { - return false; - } - if (dimension.value_case() != sub.value_case()) { - return false; - } - switch (dimension.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return dimension.value_str() == sub.value_str(); - case DimensionsValue::ValueCase::kValueInt: - return dimension.value_int() == sub.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return dimension.value_long() == sub.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return dimension.value_bool() == sub.value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return dimension.value_float() == sub.value_float(); - case DimensionsValue::ValueCase::kValueTuple: { - if (dimension.value_tuple().dimensions_value_size() < - sub.value_tuple().dimensions_value_size()) { - return false; - } - bool allSub = true; - for (int i = 0; allSub && i < sub.value_tuple().dimensions_value_size(); ++i) { - bool isSub = false; - for (int j = 0; !isSub && - j < dimension.value_tuple().dimensions_value_size(); ++j) { - isSub |= IsSubDimension(dimension.value_tuple().dimensions_value(j), - sub.value_tuple().dimensions_value(i)); - } - allSub &= isSub; - } - return allSub; - } - break; - case DimensionsValue::ValueCase::VALUE_NOT_SET: - return false; - default: - return false; - } -} - -long getLongFromDimenValue(const DimensionsValue& dimensionValue) { - switch (dimensionValue.value_case()) { - case DimensionsValue::ValueCase::kValueInt: - return dimensionValue.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return dimensionValue.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return dimensionValue.value_bool() ? 1 : 0; - case DimensionsValue::ValueCase::kValueFloat: - return (int64_t)dimensionValue.value_float(); - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::VALUE_NOT_SET: - return 0; - } -} - -bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher, - DimensionsValue* subDimension) { - if (!matcher.has_field()) { - return false; - } - if (matcher.field() != dimension.field()) { - return false; - } - if (matcher.child_size() <= 0) { - if (dimension.value_case() == DimensionsValue::ValueCase::kValueTuple || - dimension.value_case() == DimensionsValue::ValueCase::VALUE_NOT_SET) { - return false; - } - *subDimension = dimension; - return true; - } else { - if (dimension.value_case() != DimensionsValue::ValueCase::kValueTuple) { - return false; - } - bool found_value = true; - auto value_tuple = dimension.value_tuple(); - subDimension->set_field(dimension.field()); - for (int i = 0; found_value && i < matcher.child_size(); ++i) { - int j = 0; - for (; j < value_tuple.dimensions_value_size(); ++j) { - if (value_tuple.dimensions_value(j).field() == matcher.child(i).field()) { - break; - } - } - if (j < value_tuple.dimensions_value_size()) { - found_value &= getSubDimension(value_tuple.dimensions_value(j), matcher.child(i), - subDimension->mutable_value_tuple()->add_dimensions_value()); - } else { - found_value = false; - } - } - return found_value; - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h deleted file mode 100644 index 138c6e9b0160..000000000000 --- a/cmds/statsd/src/dimension.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <log/logprint.h> -#include <set> -#include <vector> -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "field_util.h" - -namespace android { -namespace os { -namespace statsd { - -// Returns the leaf node from the DimensionsValue proto. It assume that the input has only one -// leaf node at most. -const DimensionsValue* getSingleLeafValue(const DimensionsValue* value); -DimensionsValue getSingleLeafValue(const DimensionsValue& value); - -// Appends the leaf node to the parent tree. -void appendLeafNodeToTree(const Field& field, const DimensionsValue& value, DimensionsValue* tree); - -// Constructs the DimensionsValue protos from the FieldMatcher. Each DimensionsValue proto -// represents a tree. When the input proto has repeated fields and the input "dimensions" wants -// "ANY" locations, it will return multiple trees. -void findDimensionsValues( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - std::vector<DimensionsValue>* rootDimensionsValues); - -// Utils to build FieldMatcher proto for simple one-depth atoms. -void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher); -void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher); - -// Utils to build FieldMatcher proto for attribution nodes. -void buildAttributionUidFieldMatcher(const int tagId, const Position position, - FieldMatcher* matcher); -void buildAttributionTagFieldMatcher(const int tagId, const Position position, - FieldMatcher* matcher); -void buildAttributionFieldMatcher(const int tagId, const Position position, - FieldMatcher* matcher); - -// Utils to print pretty string for DimensionsValue proto. -std::string DimensionsValueToString(const DimensionsValue& value); -void DimensionsValueToString(const DimensionsValue& value, std::string *flattened); - -bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub); - -// Helper function to get long value from the DimensionsValue proto. -long getLongFromDimenValue(const DimensionsValue& dimensionValue); - -bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher, - DimensionsValue* subDimension); -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp index 7cfc1d487dfb..0b0c5c41bf9d 100644 --- a/cmds/statsd/src/external/puller_util.cpp +++ b/cmds/statsd/src/external/puller_util.cpp @@ -18,7 +18,6 @@ #include "Log.h" #include "StatsPullerManagerImpl.h" -#include "field_util.h" #include "puller_util.h" #include "statslog.h" @@ -30,125 +29,136 @@ using std::map; using std::shared_ptr; using std::vector; -DimensionsValue* getFieldValue(shared_ptr<LogEvent> event, int tagId, int fieldNum) { - Field field; - buildSimpleAtomField(tagId, fieldNum, &field); - return event->findFieldValueOrNull(field); -} - +namespace { bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs, - const vector<int>& nonAdditiveFields, int tagId) { - for (int f : nonAdditiveFields) { - DimensionsValue* lValue = getFieldValue(lhs, tagId, f); - DimensionsValue* rValue = getFieldValue(rhs, tagId, f); - if (!compareDimensionsValue(*lValue, *rValue)) { - return false; + const vector<int>& nonAdditiveFields) { + const auto& l_values = lhs->getValues(); + const auto& r_values = rhs->getValues(); + + for (size_t i : nonAdditiveFields) { + // We store everything starting from index 0, so we need to use i-1 + if (!(l_values.size() > i - 1 && r_values.size() > i - 1 && + l_values[i - 1].mValue == r_values[i - 1].mValue)) { + return false; + } } - } - return true; + return true; } // merge rhs to lhs -void mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs, - const vector<int>& additiveFields, int tagId) { - for (int f : additiveFields) { - DimensionsValue* lValue = getFieldValue(lhs, tagId, f); - DimensionsValue* rValue = getFieldValue(rhs, tagId, f); - if (lValue->has_value_int()) { - lValue->set_value_int(lValue->value_int() + rValue->value_int()); - } else if (lValue->has_value_long()) { - lValue->set_value_long(lValue->value_long() + rValue->value_long()); +// when calling this function, all sanity check should be done already. +// e.g., index boundary, nonAdditiveFields matching etc. +bool mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs, + const vector<int>& additiveFields) { + vector<FieldValue>* host_values = lhs->getMutableValues(); + const auto& child_values = rhs->getValues(); + for (int i : additiveFields) { + Value& host = (*host_values)[i - 1].mValue; + const Value& child = (child_values[i - 1]).mValue; + if (child.getType() != host.getType()) { + return false; + } + switch (child.getType()) { + case INT: + host.setInt(host.int_value + child.int_value); + break; + case LONG: + host.setLong(host.long_value + child.long_value); + break; + default: + ALOGE("Tried to merge 2 fields with unsupported type"); + return false; + } } - } + return true; } -// process all data and merge isolated with host if necessary -void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, - const sp<UidMap>& uidMap, int tagId) { - if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) == - StatsPullerManagerImpl::kAllPullAtomInfo.end()) { - VLOG("Unknown pull atom id %d", tagId); - return; - } - if (android::util::kAtomsWithUidField.find(tagId) == - android::util::kAtomsWithUidField.end()) { - VLOG("No uid to merge for atom %d", tagId); - return; - } - const vector<int>& additiveFields = - StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) - ->second.additiveFields; - const vector<int>& nonAdditiveFields = - StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) - ->second.nonAdditiveFields; - - // map of host uid to isolated uid data index in the original vector. - // because of non additive fields, there could be multiple of them that can't - // be merged into one - map<int, vector<int>> hostToIsolated; - // map of host uid to their position in the original vector - map<int, vector<int>> hostPosition; - vector<int> isolatedUidPos; - // all uids in the original vector - vector<int> allUids; - for (size_t i = 0; i < data.size(); i++) { - // uid field is always first primitive filed, if present - DimensionsValue* uidField = getFieldValue(data[i], tagId, 1); - if (!uidField) { - VLOG("Bad data for %d, %s", tagId, data[i]->ToString().c_str()); - return; - } - int uid = uidField->value_int(); - allUids.push_back(uid); - const int hostUid = uidMap->getHostUidOrSelf(uid); - if (hostUid != uid) { - uidField->set_value_int(hostUid); - hostToIsolated[hostUid].push_back(i); - isolatedUidPos.push_back(i); +bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<int>& host_pos, + const vector<int>& nonAdditiveFields, const vector<int>& additiveFields) { + for (const auto& pos : host_pos) { + if (shouldMerge(data[pos], data[child_pos], nonAdditiveFields) && + mergeEvent(data[pos], data[child_pos], additiveFields)) { + return true; + } } - } - vector<shared_ptr<LogEvent>> mergedData; - for (size_t i = 0; i < allUids.size(); i++) { - if (hostToIsolated.find(allUids[i]) != hostToIsolated.end()) { - hostPosition[allUids[i]].push_back(i); - } else if (std::find(isolatedUidPos.begin(), isolatedUidPos.end(), i) != isolatedUidPos.end()) { - continue; - } else { - mergedData.push_back(data[i]); + return false; +} + +} // namespace + +/** + * Process all data and merge isolated with host if necessary. + * For example: + * NetworkBytesAtom { + * int uid = 1; + * State process_state = 2; + * int byte_send = 3; + * int byte_recv = 4; + * } + * additive fields are {3, 4}, non-additive field is {2} + * If we pulled the following events (uid1_child is an isolated uid which maps to uid1): + * [uid1, fg, 100, 200] + * [uid1_child, fg, 100, 200] + * [uid1, bg, 100, 200] + * + * We want to merge them and results should be: + * [uid1, fg, 200, 400] + * [uid1, bg, 100, 200] + */ +void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap, + int tagId) { + if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) == + StatsPullerManagerImpl::kAllPullAtomInfo.end()) { + VLOG("Unknown pull atom id %d", tagId); + return; } - } - for (auto iter = hostToIsolated.begin(); iter != hostToIsolated.end(); - iter++) { - int uid = iter->first; - vector<int>& isolated = hostToIsolated[uid]; - vector<int> toBeMerged; - toBeMerged.insert(toBeMerged.begin(), isolated.begin(), isolated.end()); - if (hostPosition.find(uid) != hostPosition.end()) { - vector<int>& host = hostPosition[uid]; - toBeMerged.insert(toBeMerged.end(), host.begin(), host.end()); + if (android::util::kAtomsWithUidField.find(tagId) == android::util::kAtomsWithUidField.end()) { + VLOG("No uid to merge for atom %d", tagId); + return; } - vector<bool> used(toBeMerged.size()); - for (size_t i = 0; i < toBeMerged.size(); i++) { - if (used[i] == true) { - continue; - } - for (size_t j = i + 1; j < toBeMerged.size(); j++) { - shared_ptr<LogEvent>& lhs = data[toBeMerged[i]]; - shared_ptr<LogEvent>& rhs = data[toBeMerged[j]]; - if (shouldMerge(lhs, rhs, nonAdditiveFields, tagId)) { - mergeEvent(lhs, rhs, additiveFields, tagId); - used[j] = true; + const vector<int>& additiveFields = + StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.additiveFields; + const vector<int>& nonAdditiveFields = + StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields; + + // map of host uid to their position in the original vector + map<int, vector<int>> hostPosition; + vector<bool> toRemove = vector<bool>(data.size(), false); + + for (size_t i = 0; i < data.size(); i++) { + vector<FieldValue>* valueList = data[i]->getMutableValues(); + + int err = 0; + int uid = data[i]->GetInt(1, &err); + if (err != 0) { + VLOG("Bad uid field for %s", data[i]->ToString().c_str()); + return; + } + + const int hostUid = uidMap->getHostUidOrSelf(uid); + + if (hostUid != uid) { + (*valueList)[0].mValue.setInt(hostUid); + } + if (hostPosition.find(hostUid) == hostPosition.end()) { + hostPosition[hostUid].push_back(i); + } else { + if (tryMerge(data, i, hostPosition[hostUid], nonAdditiveFields, additiveFields)) { + toRemove[i] = true; + } else { + hostPosition[hostUid].push_back(i); + } } - } - } - for (size_t i = 0; i < toBeMerged.size(); i++) { - if (used[i] == false) { - mergedData.push_back(data[i]); } + + vector<shared_ptr<LogEvent>> mergedData; + for (size_t i = 0; i < toRemove.size(); i++) { + if (!toRemove[i]) { + mergedData.push_back(data[i]); + } } - } - data.clear(); - data = mergedData; + data.clear(); + data = mergedData; } } // namespace statsd diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h index 70d5321ce29a..fd4a4a2a5755 100644 --- a/cmds/statsd/src/external/puller_util.h +++ b/cmds/statsd/src/external/puller_util.h @@ -17,7 +17,6 @@ #pragma once #include <vector> -#include "HashableDimensionKey.h" #include "StatsPuller.h" #include "logd/LogEvent.h" #include "packages/UidMap.h" diff --git a/cmds/statsd/src/field_util.cpp b/cmds/statsd/src/field_util.cpp deleted file mode 100644 index acf64fe12e6d..000000000000 --- a/cmds/statsd/src/field_util.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Log.h" -#include "field_util.h" - -#include <set> -#include <vector> - -namespace android { -namespace os { -namespace statsd { - -// This function is to compare two Field trees where each node has at most one child. -bool CompareField(const Field& a, const Field& b) { - if (a.field() < b.field()) { - return true; - } - if (a.field() > b.field()) { - return false; - } - if (a.position_index() < b.position_index()) { - return true; - } - if (a.position_index() > b.position_index()) { - return false; - } - if (a.child_size() < b.child_size()) { - return true; - } - if (a.child_size() > b.child_size()) { - return false; - } - if (a.child_size() == 0 && b.child_size() == 0) { - return false; - } - return CompareField(a.child(0), b.child(0)); -} - -const Field* getSingleLeaf(const Field* field) { - if (field->child_size() <= 0) { - return field; - } else { - return getSingleLeaf(&field->child(0)); - } -} - -Field* getSingleLeaf(Field* field) { - if (field->child_size() <= 0) { - return field; - } else { - return getSingleLeaf(field->mutable_child(0)); - } -} - -void FieldToString(const Field& field, std::string *flattened) { - *flattened += std::to_string(field.field()); - if (field.has_position_index()) { - *flattened += "["; - *flattened += std::to_string(field.position_index()); - *flattened += "]"; - } - if (field.child_size() <= 0) { - return; - } - *flattened += "."; - *flattened += "{"; - for (int i = 0 ; i < field.child_size(); ++i) { - *flattened += FieldToString(field.child(i)); - } - *flattened += "},"; -} - -std::string FieldToString(const Field& field) { - std::string flatten; - FieldToString(field, &flatten); - return flatten; -} - -bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue) { - if (field.child_size() <= 0) { - leafValue->set_field(field.field()); - return true; - } else if (field.child_size() == 1) { - return setFieldInLeafValueProto(field.child(0), leafValue); - } else { - ALOGE("Not able to set the 'field' in leaf value for multiple children."); - return false; - } -} - -void buildSimpleAtomField(const int tagId, const int atomFieldNum, Field *field) { - field->set_field(tagId); - field->add_child()->set_field(atomFieldNum); -} - -void buildSimpleAtomField(const int tagId, Field *field) { - field->set_field(tagId); -} - -void appendLeaf(Field *parent, int node_field_num) { - if (!parent->has_field()) { - parent->set_field(node_field_num); - } else if (parent->child_size() <= 0) { - parent->add_child()->set_field(node_field_num); - } else { - appendLeaf(parent->mutable_child(0), node_field_num); - } -} - -void appendLeaf(Field *parent, int node_field_num, int position) { - if (!parent->has_field()) { - parent->set_field(node_field_num); - parent->set_position_index(position); - } else if (parent->child_size() <= 0) { - auto child = parent->add_child(); - child->set_field(node_field_num); - child->set_position_index(position); - } else { - appendLeaf(parent->mutable_child(0), node_field_num, position); - } -} - -void increasePosition(Field *field) { - if (!field->has_position_index()) { - field->set_position_index(0); - } else { - field->set_position_index(field->position_index() + 1); - } -} - -int getPositionByReferenceField(const Field& ref, const Field& field_with_index) { - if (ref.child_size() <= 0) { - return field_with_index.position_index(); - } - if (ref.child_size() != 1 || - field_with_index.child_size() != 1) { - return -1; - } - return getPositionByReferenceField(ref.child(0), field_with_index.child(0)); -} - -namespace { - -void findFields( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::set<Field, FieldCmp>* rootFields); - -void findNonRepeatedFields( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::set<Field, FieldCmp>* rootFields) { - if (matcher.child_size() > 0) { - Field* newLeafField = leafField->add_child(); - for (const auto& childMatcher : matcher.child()) { - newLeafField->set_field(childMatcher.field()); - findFields(fieldValueMap, childMatcher, rootField, newLeafField, rootFields); - } - leafField->clear_child(); - } else { - auto ret = fieldValueMap.equal_range(*rootField); - int found = 0; - for (auto it = ret.first; it != ret.second; ++it) { - found++; - } - // Not found. - if (found <= 0) { - return; - } - if (found > 1) { - ALOGE("Found multiple values for optional field."); - return; - } - rootFields->insert(ret.first->first); - } -} - -void findRepeatedFields(const FieldValueMap& fieldValueMap, const FieldMatcher& matcher, - Field* rootField, Field* leafField, - std::set<Field, FieldCmp>* rootFields) { - if (matcher.position() == Position::FIRST) { - leafField->set_position_index(0); - findNonRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields); - leafField->clear_position_index(); - } else { - auto itLower = fieldValueMap.lower_bound(*rootField); - if (itLower == fieldValueMap.end()) { - return; - } - - const int leafFieldNum = leafField->field(); - leafField->set_field(leafFieldNum + 1); - auto itUpper = fieldValueMap.lower_bound(*rootField); - // Resets the field number. - leafField->set_field(leafFieldNum); - - switch (matcher.position()) { - case Position::LAST: - { - itUpper--; - if (itUpper != fieldValueMap.end()) { - int last_index = getPositionByReferenceField(*rootField, itUpper->first); - if (last_index < 0) { - return; - } - leafField->set_position_index(last_index); - findNonRepeatedFields( - fieldValueMap, matcher, rootField, leafField, rootFields); - leafField->clear_position_index(); - } - } - break; - case Position::ANY: - { - std::set<int> indexes; - for (auto it = itLower; it != itUpper; ++it) { - int index = getPositionByReferenceField(*rootField, it->first); - if (index >= 0) { - indexes.insert(index); - } - } - if (!indexes.empty()) { - for (const int index : indexes) { - leafField->set_position_index(index); - findNonRepeatedFields( - fieldValueMap, matcher, rootField, leafField, rootFields); - leafField->clear_position_index(); - } - } - } - break; - default: - break; - } - } -} - -void findFields( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::set<Field, FieldCmp>* rootFields) { - if (!matcher.has_position()) { - findNonRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields); - } else { - findRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields); - } -} - -} // namespace - -void findFields( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - std::set<Field, FieldCmp>* rootFields) { - if (!matcher.has_field() || fieldValueMap.empty()) { - return; - } - Field rootField; - buildSimpleAtomField(matcher.field(), &rootField); - return findFields(fieldValueMap, matcher, &rootField, &rootField, rootFields); -} - -void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap) { - if (!matcher.has_field()) { - return; - } - std::set<Field, FieldCmp> rootFields; - findFields(*fieldValueMap, matcher, &rootFields); - auto it = fieldValueMap->begin(); - while (it != fieldValueMap->end()) { - if (rootFields.find(it->first) == rootFields.end()) { - it = fieldValueMap->erase(it); - } else { - it++; - } - } -} - -bool hasLeafNode(const FieldMatcher& matcher) { - if (!matcher.has_field()) { - return false; - } - for (int i = 0; i < matcher.child_size(); ++i) { - if (hasLeafNode(matcher.child(i))) { - return true; - } - } - return true; -} - -bool IsAttributionUidField(const Field& field) { - return field.child_size() == 1 && field.child(0).field() == 1 - && field.child(0).child_size() == 1 && field.child(0).child(0).field() == 1; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/field_util.h b/cmds/statsd/src/field_util.h deleted file mode 100644 index b04465dc9862..000000000000 --- a/cmds/statsd/src/field_util.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_internal.pb.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" - -#include <map> -#include <set> - -namespace android { -namespace os { -namespace statsd { - -// Function to sort the Field protos. -bool CompareField(const Field& a, const Field& b); -struct FieldCmp { - bool operator()(const Field& a, const Field& b) const { - return CompareField(a, b); - } -}; - -// Flattened dimensions value map. To save space, usually the key contains the tree structure info -// and value field is only leaf node. -typedef std::map<Field, DimensionsValue, FieldCmp> FieldValueMap; - -// Util function to print the Field proto. -std::string FieldToString(const Field& field); - -// Util function to find the leaf node from the input Field proto and set it in the corresponding -// value proto. -bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue); - -// Returns the leaf node from the Field proto. It assume that the input has only one -// leaf node at most. -const Field* getSingleLeaf(const Field* field); -Field* getSingleLeaf(Field* field); - -// Append a node to the current leaf. It assumes that the input "parent" has one leaf node at most. -void appendLeaf(Field *parent, int node_field_num); -void appendLeaf(Field *parent, int node_field_num, int position); - -// Increase the position index for the node. If the "position_index" is not set, set it as 0. -void increasePosition(Field *field); - -// Returns true if the matcher has specified at least one leaf node. -bool hasLeafNode(const FieldMatcher& matcher); - -// The two input Field proto are describing the same tree structure. Both contain one leaf node at -// most. This is find the position index info for the leaf node at "reference" stored in the -// "field_with_index" tree. -int getPositionByReferenceField(const Field& reference, const Field& field_with_index); - -// Utils to build the Field proto for simple atom fields. -void buildSimpleAtomField(const int tagId, const int atomFieldNum, Field* field); -void buildSimpleAtomField(const int tagId, Field* field); - -// Find out all the fields specified by the matcher. -void findFields( - const FieldValueMap& fieldValueMap, const FieldMatcher& matcher, - std::set<Field, FieldCmp>* rootFields); - -// Filter out the fields not in the field matcher. -void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap); - -// Returns if the field is attribution node uid field. -bool IsAttributionUidField(const Field& field); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index e1ab5d529370..909b74f30508 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -17,13 +17,8 @@ #define DEBUG false // STOPSHIP if true #include "logd/LogEvent.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -#include <set> #include <sstream> -#include "field_util.h" -#include "dimension.h" #include "stats_log_util.h" namespace android { @@ -152,9 +147,6 @@ bool LogEvent::write(const AttributionNode& node) { if (android_log_write_string8(mContext, node.tag().c_str()) < 0) { return false; } - if (android_log_write_int32(mContext, node.uid()) < 0) { - return false; - } if (android_log_write_list_end(mContext) < 0) { return false; } @@ -163,47 +155,23 @@ bool LogEvent::write(const AttributionNode& node) { return false; } -namespace { - -void increaseField(Field *field, bool is_child) { - if (is_child) { - if (field->child_size() <= 0) { - field->add_child(); - } - } else { - field->clear_child(); - } - Field* curr = is_child ? field->mutable_child(0) : field; - if (!curr->has_field()) { - curr->set_field(1); - } else { - curr->set_field(curr->field() + 1); - } -} - -} // namespace - /** * The elements of each log event are stored as a vector of android_log_list_elements. * The goal is to do as little preprocessing as possible, because we read a tiny fraction * of the elements that are written to the log. + * + * The idea here is to read through the log items once, we get as much information we need for + * matching as possible. Because this log will be matched against lots of matchers. */ void LogEvent::init(android_log_context context) { - if (!context) { - return; - } android_log_list_element elem; - // TODO: The log is actually structured inside one list. This is convenient - // because we'll be able to use it to put the attribution (WorkSource) block first - // without doing our own tagging scheme. Until that change is in, just drop the - // list-related log elements and the order we get there is our index-keyed data - // structure. int i = 0; int seenListStart = 0; - Field fieldTree; - Field* atomField = fieldTree.add_child(); + int32_t field = 0; + int depth = -1; + int pos[] = {1, 1, 1}; do { elem = android_log_read_next(context); switch ((int)elem.type) { @@ -211,55 +179,81 @@ void LogEvent::init(android_log_context context) { // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id. if (i == 1) { mTagId = elem.data.int32; - fieldTree.set_field(mTagId); } else { - increaseField(atomField, seenListStart > 0/* is_child */); - mFieldValueMap[fieldTree].set_value_int(elem.data.int32); + if (depth < 0 || depth > 2) { + return; + } + + mValues.push_back( + FieldValue(Field(mTagId, pos, depth), Value((int32_t)elem.data.int32))); + + pos[depth]++; } break; - case EVENT_TYPE_FLOAT: - { - increaseField(atomField, seenListStart > 0/* is_child */); - mFieldValueMap[fieldTree].set_value_float(elem.data.float32); + case EVENT_TYPE_FLOAT: { + if (depth < 0 || depth > 2) { + ALOGE("Depth > 2. Not supported!"); + return; } - break; - case EVENT_TYPE_STRING: - { - increaseField(atomField, seenListStart > 0/* is_child */); - mFieldValueMap[fieldTree].set_value_str( - string(elem.data.string, elem.len).c_str()); + + mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(elem.data.float32))); + + pos[depth]++; + + } break; + case EVENT_TYPE_STRING: { + if (depth < 0 || depth > 2) { + ALOGE("Depth > 2. Not supported!"); + return; } - break; - case EVENT_TYPE_LONG: - { - increaseField(atomField, seenListStart > 0 /* is_child */); - mFieldValueMap[fieldTree].set_value_long(elem.data.int64); + + mValues.push_back(FieldValue(Field(mTagId, pos, depth), + Value(string(elem.data.string, elem.len)))); + + pos[depth]++; + + } break; + case EVENT_TYPE_LONG: { + if (depth < 0 || depth > 2) { + ALOGE("Depth > 2. Not supported!"); + return; } - break; + mValues.push_back( + FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64))); + + pos[depth]++; + + } break; case EVENT_TYPE_LIST: - if (i >= 1) { - if (seenListStart > 0) { - increasePosition(atomField); - } else { - increaseField(atomField, false /* is_child */); - } - seenListStart++; - if (seenListStart >= 3) { - ALOGE("Depth > 2. Not supported!"); - return; - } + depth++; + if (depth > 2) { + ALOGE("Depth > 2. Not supported!"); + return; } + pos[depth] = 1; + break; - case EVENT_TYPE_LIST_STOP: - seenListStart--; - if (seenListStart == 0) { - atomField->clear_position_index(); - } else { - if (atomField->child_size() > 0) { - atomField->mutable_child(0)->clear_field(); + case EVENT_TYPE_LIST_STOP: { + int prevDepth = depth; + depth--; + if (depth >= 0 && depth < 2) { + // Now go back to decorate the previous items that are last at prevDepth. + // So that we can later easily match them with Position=Last matchers. + pos[prevDepth]--; + int path = getEncodedField(pos, prevDepth, false); + for (size_t j = mValues.size() - 1; j >= 0; j--) { + if (mValues[j].mField.getDepth() >= prevDepth && + mValues[j].mField.getPath(prevDepth) == path) { + mValues[j].mField.decorateLastPos(prevDepth); + } else { + // Safe to break, because the items are in DFS order. + break; + } } + pos[depth]++; } break; + } case EVENT_TYPE_UNKNOWN: break; default: @@ -270,162 +264,115 @@ void LogEvent::init(android_log_context context) { } int64_t LogEvent::GetLong(size_t key, status_t* err) const { - DimensionsValue value; - if (!GetSimpleAtomDimensionsValueProto(key, &value)) { - *err = BAD_INDEX; - return 0; - } - const DimensionsValue* leafValue = getSingleLeafValue(&value); - switch (leafValue->value_case()) { - case DimensionsValue::ValueCase::kValueInt: - return (int64_t)leafValue->value_int(); - case DimensionsValue::ValueCase::kValueLong: - return leafValue->value_long(); - case DimensionsValue::ValueCase::kValueBool: - return leafValue->value_bool() ? 1 : 0; - case DimensionsValue::ValueCase::kValueFloat: - return (int64_t)leafValue->value_float(); - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::VALUE_NOT_SET: { - *err = BAD_TYPE; - return 0; + // TODO: encapsulate the magical operations all in Field struct as a static function. + int field = getSimpleField(key); + for (const auto& value : mValues) { + if (value.mField.getField() == field) { + if (value.mValue.getType() == INT) { + return value.mValue.int_value; + } else { + *err = BAD_TYPE; + return 0; + } + } + if ((size_t)value.mField.getPosAtDepth(0) > key) { + break; } } + + *err = BAD_INDEX; + return 0; } int LogEvent::GetInt(size_t key, status_t* err) const { - DimensionsValue value; - if (!GetSimpleAtomDimensionsValueProto(key, &value)) { + int field = getSimpleField(key); + for (const auto& value : mValues) { + if (value.mField.getField() == field) { + if (value.mValue.getType() == INT) { + return value.mValue.int_value; + } else { + *err = BAD_TYPE; + return 0; + } + } + if ((size_t)value.mField.getPosAtDepth(0) > key) { + break; + } + } + *err = BAD_INDEX; return 0; - } - const DimensionsValue* leafValue = getSingleLeafValue(&value); - switch (leafValue->value_case()) { - case DimensionsValue::ValueCase::kValueInt: - return leafValue->value_int(); - case DimensionsValue::ValueCase::kValueLong: - case DimensionsValue::ValueCase::kValueBool: - case DimensionsValue::ValueCase::kValueFloat: - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::VALUE_NOT_SET: { - *err = BAD_TYPE; - return 0; - } - } } const char* LogEvent::GetString(size_t key, status_t* err) const { - DimensionsValue value; - if (!GetSimpleAtomDimensionsValueProto(key, &value)) { - *err = BAD_INDEX; - return 0; - } - const DimensionsValue* leafValue = getSingleLeafValue(&value); - switch (leafValue->value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return leafValue->value_str().c_str(); - case DimensionsValue::ValueCase::kValueInt: - case DimensionsValue::ValueCase::kValueLong: - case DimensionsValue::ValueCase::kValueBool: - case DimensionsValue::ValueCase::kValueFloat: - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::VALUE_NOT_SET: { - *err = BAD_TYPE; - return 0; + int field = getSimpleField(key); + for (const auto& value : mValues) { + if (value.mField.getField() == field) { + if (value.mValue.getType() == STRING) { + return value.mValue.str_value.c_str(); + } else { + *err = BAD_TYPE; + return 0; + } + } + if ((size_t)value.mField.getPosAtDepth(0) > key) { + break; } } + + *err = BAD_INDEX; + return NULL; } bool LogEvent::GetBool(size_t key, status_t* err) const { - DimensionsValue value; - if (!GetSimpleAtomDimensionsValueProto(key, &value)) { - *err = BAD_INDEX; - return 0; - } - const DimensionsValue* leafValue = getSingleLeafValue(&value); - switch (leafValue->value_case()) { - case DimensionsValue::ValueCase::kValueInt: - return leafValue->value_int() != 0; - case DimensionsValue::ValueCase::kValueLong: - return leafValue->value_long() != 0; - case DimensionsValue::ValueCase::kValueBool: - return leafValue->value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return leafValue->value_float() != 0; - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::VALUE_NOT_SET: { - *err = BAD_TYPE; - return 0; + int field = getSimpleField(key); + for (const auto& value : mValues) { + if (value.mField.getField() == field) { + if (value.mValue.getType() == INT) { + return value.mValue.int_value != 0; + } else if (value.mValue.getType() == LONG) { + return value.mValue.long_value != 0; + } else { + *err = BAD_TYPE; + return false; + } } - } -} - -float LogEvent::GetFloat(size_t key, status_t* err) const { - DimensionsValue value; - if (!GetSimpleAtomDimensionsValueProto(key, &value)) { - *err = BAD_INDEX; - return 0; - } - const DimensionsValue* leafValue = getSingleLeafValue(&value); - switch (leafValue->value_case()) { - case DimensionsValue::ValueCase::kValueInt: - return (float)leafValue->value_int(); - case DimensionsValue::ValueCase::kValueLong: - return (float)leafValue->value_long(); - case DimensionsValue::ValueCase::kValueBool: - return leafValue->value_bool() ? 1.0f : 0.0f; - case DimensionsValue::ValueCase::kValueFloat: - return leafValue->value_float(); - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::VALUE_NOT_SET: { - *err = BAD_TYPE; - return 0; + if ((size_t)value.mField.getPosAtDepth(0) > key) { + break; } } -} -void LogEvent::GetAtomDimensionsValueProtos(const FieldMatcher& matcher, - std::vector<DimensionsValue> *dimensionsValues) const { - findDimensionsValues(mFieldValueMap, matcher, dimensionsValues); + *err = BAD_INDEX; + return false; } -bool LogEvent::GetAtomDimensionsValueProto(const FieldMatcher& matcher, - DimensionsValue* dimensionsValue) const { - std::vector<DimensionsValue> rootDimensionsValues; - findDimensionsValues(mFieldValueMap, matcher, &rootDimensionsValues); - if (rootDimensionsValues.size() != 1) { - return false; +float LogEvent::GetFloat(size_t key, status_t* err) const { + int field = getSimpleField(key); + for (const auto& value : mValues) { + if (value.mField.getField() == field) { + if (value.mValue.getType() == FLOAT) { + return value.mValue.float_value; + } else { + *err = BAD_TYPE; + return 0.0; + } + } + if ((size_t)value.mField.getPosAtDepth(0) > key) { + break; + } } - *dimensionsValue = rootDimensionsValues.front(); - return true; -} -bool LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField, - DimensionsValue* dimensionsValue) const { - FieldMatcher matcher; - buildSimpleAtomFieldMatcher(mTagId, atomField, &matcher); - return GetAtomDimensionsValueProto(matcher, dimensionsValue); -} - -DimensionsValue* LogEvent::findFieldValueOrNull(const Field& field) { - auto it = mFieldValueMap.find(field); - if (it == mFieldValueMap.end()) { - return nullptr; - } - return &it->second; + *err = BAD_INDEX; + return 0.0; } string LogEvent::ToString() const { ostringstream result; result << "{ " << mTimestampNs << " (" << mTagId << ")"; - for (const auto& itr : mFieldValueMap) { - result << FieldToString(itr.first); + for (const auto& value : mValues) { + result << StringPrintf("%#x", value.mField.getField()); result << "->"; - result << DimensionsValueToString(itr.second); + result << value.mValue.toString(); result << " "; } result << " }"; @@ -433,7 +380,7 @@ string LogEvent::ToString() const { } void LogEvent::ToProto(ProtoOutputStream& protoOutput) const { - writeFieldValueTreeToStream(getFieldValueMap(), &protoOutput); + writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput); } } // namespace statsd diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index d521e09569b6..0895daa49ad3 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -16,7 +16,7 @@ #pragma once -#include "field_util.h" +#include "FieldValue.h" #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include <android/util/ProtoOutputStream.h> @@ -24,11 +24,8 @@ #include <log/log_read.h> #include <private/android_logger.h> #include <utils/Errors.h> -#include <utils/JenkinsHash.h> -#include <memory> #include <string> -#include <map> #include <vector> namespace android { @@ -37,7 +34,6 @@ namespace statsd { using std::string; using std::vector; - /** * Wrapper for the log_msg structure. */ @@ -81,19 +77,6 @@ public: bool GetBool(size_t key, status_t* err) const; float GetFloat(size_t key, status_t* err) const; - /* - * Get DimensionsValue proto objects from FieldMatcher. - */ - void GetAtomDimensionsValueProtos( - const FieldMatcher& matcher, std::vector<DimensionsValue> *dimensionsValues) const; - bool GetAtomDimensionsValueProto( - const FieldMatcher& matcher, DimensionsValue* dimensionsValue) const; - - /* - * Get a DimensionsValue proto objects from Field. - */ - bool GetSimpleAtomDimensionsValueProto(size_t field, DimensionsValue* dimensionsValue) const; - /** * Write test data to the LogEvent. This can only be used when the LogEvent is constructed * using LogEvent(tagId, timestampNs). You need to call init() before you can read from it. @@ -129,15 +112,16 @@ public: void setTimestampNs(uint64_t timestampNs) {mTimestampNs = timestampNs;} inline int size() const { - return mFieldValueMap.size(); + return mValues.size(); } - /** - * Returns the mutable DimensionsValue proto for the specific the field. - */ - DimensionsValue* findFieldValueOrNull(const Field& field); + const std::vector<FieldValue>& getValues() const { + return mValues; + } - inline const FieldValueMap& getFieldValueMap() const { return mFieldValueMap; } + std::vector<FieldValue>* getMutableValues() { + return &mValues; + } private: /** @@ -151,7 +135,9 @@ private: */ void init(android_log_context context); - FieldValueMap mFieldValueMap; + // The items are naturally sorted in DFS order as we read them. this allows us to do fast + // matching. + std::vector<FieldValue> mValues; // This field is used when statsD wants to create log event object and write fields to it. After // calling init() function, this object would be destroyed to save memory usage. diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index fae91729fe4f..944764bece8a 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -13,23 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "matchers/LogMatchingTracker.h" #include "matchers/matcher_util.h" -#include "dimension.h" #include "stats_util.h" -#include "field_util.h" - -#include <log/event_tag_map.h> -#include <log/log_event_list.h> -#include <log/logprint.h> -#include <utils/Errors.h> - -#include <sstream> -#include <unordered_map> using std::ostringstream; using std::set; @@ -93,198 +83,224 @@ bool combinationMatch(const vector<int>& children, const LogicalOperation& opera return matched; } -namespace { +bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value, + const string& str_match) { + if (isAttributionUidField(field, value)) { + int uid = value.int_value; + std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/); + return packageNames.find(str_match) != packageNames.end(); + } else if (value.getType() == STRING) { + return value.str_value == str_match; + } + return false; +} -bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap, - const FieldValueMatcher&matcher, Field* rootField, Field* leafField); +bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, + const vector<FieldValue>& values, int start, int end, int depth) { + if (depth > 2) { + ALOGE("Depth > 3 not supported"); + return false; + } -bool matchesNonRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap, - const FieldValueMatcher&matcher, Field* rootField, Field* leafField) { - if (matcher.value_matcher_case() == - FieldValueMatcher::ValueMatcherCase::VALUE_MATCHER_NOT_SET) { - return !fieldMap.empty() && fieldMap.begin()->first.field() == matcher.field(); - } else if (matcher.value_matcher_case() == FieldValueMatcher::ValueMatcherCase::kMatchesTuple) { - bool allMatched = true; - Field* newLeafField = leafField->add_child(); - for (int i = 0; allMatched && i < matcher.matches_tuple().field_value_matcher_size(); ++i) { - const auto& childMatcher = matcher.matches_tuple().field_value_matcher(i); - newLeafField->set_field(childMatcher.field()); - allMatched &= matchFieldSimple(uidMap, fieldMap, childMatcher, rootField, newLeafField); - } - leafField->clear_child(); - return allMatched; - } else { - auto ret = fieldMap.equal_range(*rootField); - int found = 0; - for (auto it = ret.first; it != ret.second; ++it) { - found++; - } - // Not found. - if (found <= 0) { - return false; + if (start >= end) { + return false; + } + + // Filter by entry field first + int newStart = -1; + int newEnd = end; + // because the fields are naturally sorted in the DFS order. we can safely + // break when pos is larger than the one we are searching for. + for (int i = start; i < end; i++) { + int pos = values[i].mField.getPosAtDepth(depth); + if (pos == matcher.field()) { + if (newStart == -1) { + newStart = i; + } + newEnd = i + 1; + } else if (pos > matcher.field()) { + break; } - if (found > 1) { - ALOGE("Found multiple values for optional field."); + } + + // Now we have zoomed in to a new range + start = newStart; + end = newEnd; + + if (start == -1) { + // No such field found. + return false; + } + + vector<pair<int, int>> ranges; // the ranges are for matching ANY position + if (matcher.has_position()) { + // Repeated fields position is stored as a node in the path. + depth++; + if (depth > 2) { return false; } - bool matched = false; - switch (matcher.value_matcher_case()) { - case FieldValueMatcher::ValueMatcherCase::kEqBool: { - // Logd does not support bool, it is int instead. - matched = ((ret.first->second.value_int() > 0) == matcher.eq_bool()); + switch (matcher.position()) { + case Position::FIRST: { + for (int i = start; i < end; i++) { + int pos = values[i].mField.getPosAtDepth(depth); + if (pos != 1) { + // Again, the log elements are stored in sorted order. so + // once the position is > 1, we break; + end = i; + break; + } + } + ranges.push_back(std::make_pair(start, end)); break; } - case FieldValueMatcher::ValueMatcherCase::kEqString: { - if (IsAttributionUidField(*rootField)) { - const int uid = ret.first->second.value_int(); - std::set<string> packageNames = - uidMap.getAppNamesFromUid(uid, true /* normalize*/); - matched = packageNames.find(matcher.eq_string()) != packageNames.end(); - } else { - matched = (ret.first->second.value_str() == matcher.eq_string()); + case Position::LAST: { + // move the starting index to the first LAST field at the depth. + for (int i = start; i < end; i++) { + if (values[i].mField.isLastPos(depth)) { + start = i; + break; + } } + ranges.push_back(std::make_pair(start, end)); break; } - case FieldValueMatcher::ValueMatcherCase::kEqInt: { - int64_t val = ret.first->second.has_value_int() ? - ret.first->second.value_int() : ret.first->second.value_long(); - matched = (val == matcher.eq_int()); + case Position::ANY: { + // ANY means all the children matchers match in any of the sub trees, it's a match + newStart = start; + newEnd = end; + // Here start is guaranteed to be a valid index. + int currentPos = values[start].mField.getPosAtDepth(depth); + // Now find all sub trees ranges. + for (int i = start; i < end; i++) { + int newPos = values[i].mField.getPosAtDepth(depth); + if (newPos != currentPos) { + ranges.push_back(std::make_pair(newStart, i)); + newStart = i; + currentPos = newPos; + } + } + ranges.push_back(std::make_pair(newStart, end)); break; } - case FieldValueMatcher::ValueMatcherCase::kLtInt: { - int64_t val = ret.first->second.has_value_int() ? - ret.first->second.value_int() : ret.first->second.value_long(); - matched = (val < matcher.lt_int()); + case Position::POSITION_UNKNOWN: break; + } + } else { + // No position + ranges.push_back(std::make_pair(start, end)); + } + // start and end are still pointing to the matched range. + switch (matcher.value_matcher_case()) { + case FieldValueMatcher::kMatchesTuple: { + ++depth; + // If any range matches all matchers, good. + for (const auto& range : ranges) { + bool matched = true; + for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) { + if (!matchesSimple(uidMap, subMatcher, values, range.first, range.second, + depth)) { + matched = false; + break; + } + } + if (matched) return true; } - case FieldValueMatcher::ValueMatcherCase::kGtInt: { - int64_t val = ret.first->second.has_value_int() ? - ret.first->second.value_int() : ret.first->second.value_long(); - matched = (val > matcher.gt_int()); - break; + return false; + } + case FieldValueMatcher::ValueMatcherCase::kEqBool: { + for (int i = start; i < end; i++) { + if ((values[i].mValue.getType() == INT && + (values[i].mValue.int_value != 0) == matcher.eq_bool()) || + (values[i].mValue.getType() == LONG && + (values[i].mValue.long_value != 0) == matcher.eq_bool())) { + return true; + } } - case FieldValueMatcher::ValueMatcherCase::kLtFloat: { - matched = (ret.first->second.value_float() < matcher.lt_float()); - break; + return false; + } + case FieldValueMatcher::ValueMatcherCase::kEqString: { + for (int i = start; i < end; i++) { + if (tryMatchString(uidMap, values[i].mField, values[i].mValue, + matcher.eq_string())) { + return true; + } } - case FieldValueMatcher::ValueMatcherCase::kGtFloat: { - matched = (ret.first->second.value_float() > matcher.gt_float()); - break; + } + return false; + case FieldValueMatcher::ValueMatcherCase::kEqInt: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == INT && + (matcher.eq_int() == values[i].mValue.int_value)) { + return true; + } } - case FieldValueMatcher::ValueMatcherCase::kLteInt: { - int64_t val = ret.first->second.has_value_int() ? - ret.first->second.value_int() : ret.first->second.value_long(); - matched = (val <= matcher.lte_int()); - break; + return false; + case FieldValueMatcher::ValueMatcherCase::kLtInt: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == INT && + (values[i].mValue.int_value < matcher.lt_int())) { + return true; + } } - case FieldValueMatcher::ValueMatcherCase::kGteInt: { - int64_t val = ret.first->second.has_value_int() ? - ret.first->second.value_int() : ret.first->second.value_long(); - matched = (val >= matcher.gte_int()); - break; + return false; + case FieldValueMatcher::ValueMatcherCase::kGtInt: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == INT && + (values[i].mValue.int_value > matcher.gt_int())) { + return true; + } } - default: - break; - } - return matched; - } -} - -bool matchesRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap, - const FieldValueMatcher&matcher, - Field* rootField, Field* leafField) { - if (matcher.position() == Position::FIRST) { - leafField->set_position_index(0); - bool res = matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField); - leafField->clear_position_index(); - return res; - } else { - auto itLower = fieldMap.lower_bound(*rootField); - if (itLower == fieldMap.end()) { return false; - } - - const int leafFieldNum = leafField->field(); - leafField->set_field(leafFieldNum + 1); - auto itUpper = fieldMap.lower_bound(*rootField); - // Resets the field number. - leafField->set_field(leafFieldNum); - - switch (matcher.position()) { - case Position::LAST: - { - itUpper--; - if (itUpper == fieldMap.end()) { - return false; - } else { - int last_index = getPositionByReferenceField(*rootField, itUpper->first); - if (last_index < 0) { - return false; - } - leafField->set_position_index(last_index); - bool res = matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField); - leafField->clear_position_index(); - return res; - } - } - break; - case Position::ANY: - { - bool matched = false; - for (auto it = itLower; it != itUpper; ++it) { - int index = getPositionByReferenceField(*rootField, it->first); - if (index >= 0) { - leafField->set_position_index(index); - matched |= matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField); - leafField->clear_position_index(); - if (matched) { - break; - } - } - } - return matched; - } - default: - return false; - } - } - -} - -bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap, - const FieldValueMatcher&matcher, Field* rootField, Field* leafField) { - if (!matcher.has_position()) { - return matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField); - } else { - return matchesRepeatedField(uidMap, fieldMap, matcher, rootField, leafField); + case FieldValueMatcher::ValueMatcherCase::kLtFloat: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == FLOAT && + (values[i].mValue.float_value < matcher.lt_float())) { + return true; + } + } + return false; + case FieldValueMatcher::ValueMatcherCase::kGtFloat: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == FLOAT && + (values[i].mValue.float_value > matcher.gt_float())) { + return true; + } + } + return false; + case FieldValueMatcher::ValueMatcherCase::kLteInt: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == INT && + (values[i].mValue.int_value <= matcher.lte_int())) { + return true; + } + } + return false; + case FieldValueMatcher::ValueMatcherCase::kGteInt: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == INT && + (values[i].mValue.int_value >= matcher.gte_int())) { + return true; + } + } + return false; + default: + return false; } } -} // namespace - bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher, const LogEvent& event) { if (simpleMatcher.field_value_matcher_size() <= 0) { return event.GetTagId() == simpleMatcher.atom_id(); } - Field root_field; - root_field.set_field(simpleMatcher.atom_id()); - FieldValueMatcher root_field_matcher; - root_field_matcher.set_field(simpleMatcher.atom_id()); - for (int i = 0; i < simpleMatcher.field_value_matcher_size(); i++) { - *root_field_matcher.mutable_matches_tuple()->add_field_value_matcher() = - simpleMatcher.field_value_matcher(i); + for (const auto& matcher : simpleMatcher.field_value_matcher()) { + if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) { + return false; + } } - return matchFieldSimple( - uidMap, event.getFieldValueMap(), root_field_matcher, &root_field, &root_field); + return true; } -void getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher, - std::vector<DimensionsValue> *dimensionKeys) { - if (matcher.has_field()) { - findDimensionsValues(event.getFieldValueMap(), matcher, dimensionKeys); - } -} } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h index a45a9fb26a13..872cd8e64575 100644 --- a/cmds/statsd/src/matchers/matcher_util.h +++ b/cmds/statsd/src/matchers/matcher_util.h @@ -45,9 +45,6 @@ bool combinationMatch(const std::vector<int>& children, const LogicalOperation& bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper); -void getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher, - std::vector<DimensionsValue> *dimensionKeys); - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 5a042b63c675..bd2674b86a46 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -21,7 +21,6 @@ #include "guardrail/StatsdStats.h" #include "stats_util.h" #include "stats_log_util.h" -#include "dimension.h" #include <limits.h> #include <stdlib.h> @@ -69,16 +68,26 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric mBucketSizeNs = LLONG_MAX; } - // TODO: use UidMap if uid->pkg_name is required - mDimensionsInWhat = metric.dimensions_in_what(); - mDimensionsInCondition = metric.dimensions_in_condition(); + if (metric.has_dimensions_in_what()) { + translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + } + + if (metric.has_dimensions_in_condition()) { + translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition); + } if (metric.links().size() > 0) { - mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), - metric.links().end()); + for (const auto& link : metric.links()) { + Metric2Condition mc; + mc.conditionId = link.condition(); + translateFieldMatcher(link.fields_in_what(), &mc.metricFields); + translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + mMetric2ConditionLinks.push_back(mc); + } + mConditionSliced = true; } - mConditionSliced = (metric.links().size() > 0)|| - (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); + + mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); @@ -92,26 +101,6 @@ void CountMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } -void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { - flushIfNeededLocked(dumpTimeNs); - report->set_metric_id(mMetricId); - - auto count_metrics = report->mutable_count_metrics(); - for (const auto& counter : mPastBuckets) { - CountMetricData* metricData = count_metrics->add_data(); - *metricData->mutable_dimensions_in_what() = - counter.first.getDimensionKeyInWhat().getDimensionsValue(); - *metricData->mutable_dimensions_in_condition() = - counter.first.getDimensionKeyInCondition().getDimensionsValue(); - for (const auto& bucket : counter.second) { - CountBucketInfo* bucketInfo = metricData->add_bucket_info(); - bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); - bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs); - bucketInfo->set_count(bucket.mCount); - } - } -} - void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); @@ -122,8 +111,6 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS); - VLOG("metric %lld dump report now...",(long long)mMetricId); - for (const auto& counter : mPastBuckets) { const MetricDimensionKey& dimensionKey = counter.first; VLOG(" dimension key %s", dimensionKey.c_str()); @@ -134,15 +121,13 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionInWhatToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionInWhatToken); if (dimensionKey.hasDimensionKeyInCondition()) { long long dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); } @@ -200,7 +185,6 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( const ConditionKey& conditionKey, bool condition, const LogEvent& event) { uint64_t eventTimeNs = event.GetTimestampNs(); - flushIfNeededLocked(eventTimeNs); if (condition == false) { diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index b06c77b27faf..0c4291d5dfc2 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -57,7 +57,6 @@ protected: private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; - void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 65cbc4afdad4..6b321e11edcf 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -21,7 +21,6 @@ #include "guardrail/StatsdStats.h" #include "stats_util.h" #include "stats_log_util.h" -#include "dimension.h" #include <limits.h> #include <stdlib.h> @@ -68,8 +67,7 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat mStartIndex(startIndex), mStopIndex(stopIndex), mStopAllIndex(stopAllIndex), - mNested(nesting), - mInternalDimensions(internalDimensions) { + mNested(nesting) { // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract // them in the base class, because the proto generated CountMetric, and DurationMetric are // not related. Maybe we should add a template in the future?? @@ -79,16 +77,28 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat mBucketSizeNs = LLONG_MAX; } - // TODO: use UidMap if uid->pkg_name is required - mDimensionsInWhat = metric.dimensions_in_what(); - mDimensionsInCondition = metric.dimensions_in_condition(); + if (metric.has_dimensions_in_what()) { + translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + } + + if (internalDimensions.has_field()) { + translateFieldMatcher(internalDimensions, &mInternalDimensions); + } + + if (metric.has_dimensions_in_condition()) { + translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition); + } if (metric.links().size() > 0) { - mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), - metric.links().end()); + for (const auto& link : metric.links()) { + Metric2Condition mc; + mc.conditionId = link.condition(); + translateFieldMatcher(link.fields_in_what(), &mc.metricFields); + translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + mMetric2ConditionLinks.push_back(mc); + } } - mConditionSliced = (metric.links().size() > 0)|| - (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); + mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); @@ -174,37 +184,18 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, } } -void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { - flushIfNeededLocked(dumpTimeNs); - report->set_metric_id(mMetricId); - - auto duration_metrics = report->mutable_duration_metrics(); - for (const auto& pair : mPastBuckets) { - DurationMetricData* metricData = duration_metrics->add_data(); - *metricData->mutable_dimensions_in_what() = - pair.first.getDimensionKeyInWhat().getDimensionsValue(); - *metricData->mutable_dimensions_in_condition() = - pair.first.getDimensionKeyInCondition().getDimensionsValue(); - for (const auto& bucket : pair.second) { - auto bucketInfo = metricData->add_bucket_info(); - bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); - bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs); - bucketInfo->set_duration_nanos(bucket.mDuration); - } - } -} - void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); if (mPastBuckets.empty()) { + VLOG(" Duration metric, empty return"); return; } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS); - VLOG("metric %lld dump report now...", (long long)mMetricId); + VLOG("Duration metric %lld dump report now...", (long long)mMetricId); for (const auto& pair : mPastBuckets) { const MetricDimensionKey& dimensionKey = pair.first; @@ -216,15 +207,13 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionToken); if (dimensionKey.hasDimensionKeyInCondition()) { long long dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); } @@ -339,8 +328,8 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( auto it = mCurrentSlicedDurationTrackerMap.find(eventKey); - std::vector<DimensionsValue> values; - getDimensionKeys(event, mInternalDimensions, &values); + std::vector<HashableDimensionKey> values; + filterValues(mInternalDimensions, event.getValues(), &values); if (values.empty()) { if (matcherIndex == mStartIndex) { it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, @@ -349,13 +338,11 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetTimestampNs(), false); } } else { - for (const DimensionsValue& value : values) { + for (const auto& value : values) { if (matcherIndex == mStartIndex) { - it->second->noteStart( - HashableDimensionKey(value), condition, event.GetTimestampNs(), conditionKeys); + it->second->noteStart(value, condition, event.GetTimestampNs(), conditionKeys); } else if (matcherIndex == mStopIndex) { - it->second->noteStop( - HashableDimensionKey(value), event.GetTimestampNs(), false); + it->second->noteStop(value, event.GetTimestampNs(), false); } } } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index a49601655e5e..5f29281a8a3c 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -57,7 +57,6 @@ protected: private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; - void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; @@ -90,7 +89,7 @@ private: const bool mNested; // The dimension from the atom predicate. e.g., uid, wakelock name. - const FieldMatcher mInternalDimensions; + vector<Matcher> mInternalDimensions; // Save the past buckets and we can clear when the StatsLogReport is dumped. // TODO: Add a lock to mPastBuckets. diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 936a2ef1fde4..ed7e44de09c0 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -55,8 +55,13 @@ EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric const uint64_t startTimeNs) : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) { if (metric.links().size() > 0) { - mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), - metric.links().end()); + for (const auto& link : metric.links()) { + Metric2Condition mc; + mc.conditionId = link.condition(); + translateFieldMatcher(link.fields_in_what(), &mc.metricFields); + translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + mMetric2ConditionLinks.push_back(mc); + } mConditionSliced = true; } mProto = std::make_unique<ProtoOutputStream>(); @@ -88,10 +93,6 @@ std::unique_ptr<std::vector<uint8_t>> serializeProtoLocked(ProtoOutputStream& pr return buffer; } -void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { - -} - void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { if (mProto->size() <= 0) { diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 394ed234f02e..3f2c5a54e022 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -48,7 +48,6 @@ private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; - void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 62ee6ef17d5c..da0cafeca427 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -19,7 +19,6 @@ #include "GaugeMetricProducer.h" #include "guardrail/StatsdStats.h" -#include "dimension.h" #include "stats_log_util.h" #include <cutils/log.h> @@ -77,18 +76,29 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric mBucketSizeNs = bucketSizeMills * 1000000; mSamplingType = metric.sampling_type(); - mFieldFilter = metric.gauge_fields_filter(); + if (!metric.gauge_fields_filter().include_all()) { + translateFieldMatcher(metric.gauge_fields_filter().fields(), &mFieldMatchers); + } // TODO: use UidMap if uid->pkg_name is required - mDimensionsInWhat = metric.dimensions_in_what(); - mDimensionsInCondition = metric.dimensions_in_condition(); + if (metric.has_dimensions_in_what()) { + translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + } + + if (metric.has_dimensions_in_condition()) { + translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition); + } if (metric.links().size() > 0) { - mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), - metric.links().end()); + for (const auto& link : metric.links()) { + Metric2Condition mc; + mc.conditionId = link.condition(); + translateFieldMatcher(link.fields_in_what(), &mc.metricFields); + translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + mMetric2ConditionLinks.push_back(mc); + } } - mConditionSliced = (metric.links().size() > 0)|| - (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); + mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); // Kicks off the puller immediately. if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { @@ -115,13 +125,6 @@ GaugeMetricProducer::~GaugeMetricProducer() { } } -void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { - flushIfNeededLocked(dumpTimeNs); - ProtoOutputStream pbOutput; - onDumpReportLocked(dumpTimeNs, &pbOutput); - parseProtoOutputStream(pbOutput, report); -} - void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { VLOG("gauge metric %lld report now...", (long long)mMetricId); @@ -144,15 +147,13 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionToken); if (dimensionKey.hasDimensionKeyInCondition()) { long long dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); } @@ -169,7 +170,7 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, long long atomsToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_ATOM); for (const auto& atom : bucket.mGaugeAtoms) { - writeFieldValueTreeToStream(*atom.mFields, protoOutput); + writeFieldValueTreeToStream(mTagId, *(atom.mFields), protoOutput); } protoOutput->end(atomsToken); @@ -246,13 +247,14 @@ void GaugeMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } -std::shared_ptr<FieldValueMap> GaugeMetricProducer::getGaugeFields(const LogEvent& event) { - std::shared_ptr<FieldValueMap> gaugeFields = - std::make_shared<FieldValueMap>(event.getFieldValueMap()); - if (!mFieldFilter.include_all()) { - filterFields(mFieldFilter.fields(), gaugeFields.get()); +std::shared_ptr<vector<FieldValue>> GaugeMetricProducer::getGaugeFields(const LogEvent& event) { + if (mFieldMatchers.size() > 0) { + std::shared_ptr<vector<FieldValue>> gaugeFields = std::make_shared<vector<FieldValue>>(); + filterGaugeValues(mFieldMatchers, event.getValues(), gaugeFields.get()); + return gaugeFields; + } else { + return std::make_shared<vector<FieldValue>>(event.getValues()); } - return gaugeFields; } void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) { @@ -292,6 +294,7 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( return; } uint64_t eventTimeNs = event.GetTimestampNs(); + mTagId = event.GetTagId(); if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); @@ -308,20 +311,18 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( if (hitGuardRailLocked(eventKey)) { return; } - GaugeAtom gaugeAtom; - gaugeAtom.mFields = getGaugeFields(event); - gaugeAtom.mTimestamps = eventTimeNs; + GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs); (*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom); // Anomaly detection on gauge metric only works when there is one numeric // field specified. if (mAnomalyTrackers.size() > 0) { if (gaugeAtom.mFields->size() == 1) { - const DimensionsValue& dimensionsValue = gaugeAtom.mFields->begin()->second; + const Value& value = gaugeAtom.mFields->begin()->mValue; long gaugeVal = 0; - if (dimensionsValue.has_value_int()) { - gaugeVal = (long)dimensionsValue.value_int(); - } else if (dimensionsValue.has_value_long()) { - gaugeVal = dimensionsValue.value_long(); + if (value.getType() == INT) { + gaugeVal = (long)value.int_value; + } else if (value.getType() == LONG) { + gaugeVal = value.long_value; } for (auto& tracker : mAnomalyTrackers) { tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, @@ -334,15 +335,15 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() { status_t err = NO_ERROR; for (const auto& slice : *mCurrentSlicedBucket) { - if (slice.second.empty() || slice.second.front().mFields->empty()) { + if (slice.second.empty()) { continue; } - const DimensionsValue& dimensionsValue = slice.second.front().mFields->begin()->second; + const Value& value = slice.second.front().mFields->front().mValue; long gaugeVal = 0; - if (dimensionsValue.has_value_int()) { - gaugeVal = (long)dimensionsValue.value_int(); - } else if (dimensionsValue.has_value_long()) { - gaugeVal = dimensionsValue.value_long(); + if (value.getType() == INT) { + gaugeVal = (long)value.int_value; + } else if (value.getType() == LONG) { + gaugeVal = value.long_value; } (*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal; } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index d5d34be04562..c3ae6ce921d2 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -33,7 +33,10 @@ namespace os { namespace statsd { struct GaugeAtom { - std::shared_ptr<FieldValueMap> mFields; + GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t timeNs) + : mFields(fields), mTimestamps(timeNs) { + } + std::shared_ptr<vector<FieldValue>> mFields; int64_t mTimestamps; }; @@ -87,7 +90,6 @@ protected: private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; - void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // for testing GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric, @@ -113,6 +115,8 @@ private: void pullLocked(); + int mTagId; + std::shared_ptr<StatsPullerManager> mStatsPullerManager; // tagId for pulled data. -1 if this is not pulled const int mPullTagId; @@ -133,12 +137,12 @@ private: void updateCurrentSlicedBucketForAnomaly(); // Whitelist of fields to report. Empty means all are reported. - FieldFilter mFieldFilter; + std::vector<Matcher> mFieldMatchers; GaugeMetric::SamplingType mSamplingType; // apply a whitelist on the original input - std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event); + std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event); // Util function to check whether the specified dimension hits the guardrail. bool hitGuardRailLocked(const MetricDimensionKey& newKey); diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 85e655b08f4d..beb90155f183 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "MetricProducer.h" -#include "dimension.h" +#define DEBUG true // STOPSHIP if true +#include "Log.h" +#include "MetricProducer.h" namespace android { namespace os { @@ -35,9 +36,10 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo std::unordered_set<HashableDimensionKey> dimensionKeysInCondition; if (mConditionSliced) { - for (const auto& link : mConditionLinks) { - getDimensionKeysForCondition(event, link, &conditionKey[link.condition()]); + for (const auto& link : mMetric2ConditionLinks) { + getDimensionForCondition(event, link, &conditionKey[link.conditionId]); } + auto conditionState = mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition, &dimensionKeysInCondition); @@ -46,20 +48,19 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo condition = mCondition; } - vector<DimensionsValue> dimensionInWhatValues; - if (mDimensionsInWhat.has_field() && mDimensionsInWhat.child_size() > 0) { - getDimensionKeys(event, mDimensionsInWhat, &dimensionInWhatValues); + vector<HashableDimensionKey> dimensionInWhatValues; + if (mDimensionsInWhat.size() > 0) { + filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues); } if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) { onMatchedLogEventInternalLocked( matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event); } else if (dimensionKeysInCondition.empty()) { - for (const DimensionsValue& whatValue : dimensionInWhatValues) { - onMatchedLogEventInternalLocked( - matcherIndex, - MetricDimensionKey(HashableDimensionKey(whatValue), DEFAULT_DIMENSION_KEY), - conditionKey, condition, event); + for (const HashableDimensionKey& whatValue : dimensionInWhatValues) { + onMatchedLogEventInternalLocked(matcherIndex, + MetricDimensionKey(whatValue, DEFAULT_DIMENSION_KEY), + conditionKey, condition, event); } } else if (dimensionInWhatValues.empty()) { for (const auto& conditionDimensionKey : dimensionKeysInCondition) { @@ -69,12 +70,11 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo conditionKey, condition, event); } } else { - for (const DimensionsValue& whatValue : dimensionInWhatValues) { + for (const auto& whatValue : dimensionInWhatValues) { for (const auto& conditionDimensionKey : dimensionKeysInCondition) { onMatchedLogEventInternalLocked( - matcherIndex, - MetricDimensionKey(HashableDimensionKey(whatValue), conditionDimensionKey), - conditionKey, condition, event); + matcherIndex, MetricDimensionKey(whatValue, conditionDimensionKey), + conditionKey, condition, event); } } } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 542dd8ab019d..e8f8299abd89 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -19,6 +19,7 @@ #include <shared_mutex> +#include "HashableDimensionKey.h" #include "anomaly/AnomalyTracker.h" #include "condition/ConditionWizard.h" #include "config/ConfigKey.h" @@ -109,11 +110,6 @@ public: std::lock_guard<std::mutex> lock(mMutex); return onDumpReportLocked(dumpTimeNs, protoOutput); } - // This method does not clear the past buckets. - void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) { - std::lock_guard<std::mutex> lock(mMutex); - return onDumpReportLocked(dumpTimeNs, report); - } void dumpStates(FILE* out, bool verbose) const { std::lock_guard<std::mutex> lock(mMutex); @@ -150,7 +146,6 @@ protected: virtual void onSlicedConditionMayChangeLocked(const uint64_t eventTime) = 0; virtual void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) = 0; - virtual void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) = 0; virtual size_t byteSizeLocked() const = 0; virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0; @@ -203,10 +198,10 @@ protected: int mConditionTrackerIndex; - FieldMatcher mDimensionsInWhat; // The dimensions_in_what defined in statsd_config - FieldMatcher mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config + vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config + vector<Matcher> mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config - std::vector<MetricConditionLink> mConditionLinks; + std::vector<Metric2Condition> mMetric2ConditionLinks; std::vector<sp<AnomalyTracker>> mAnomalyTrackers; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 6c21b052e807..dd6735b6faca 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define DEBUG false // STOPSHIP if true +#define DEBUG true // STOPSHIP if true #include "Log.h" #include "MetricsManager.h" #include "statslog.h" @@ -151,14 +151,6 @@ void MetricsManager::onUidMapReceived(const uint64_t& eventTimeNs) { initLogSourceWhiteList(); } -void MetricsManager::onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report) { - for (const auto& producer : mAllMetricProducers) { - if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { - producer->onDumpReport(dumpTimeStampNs, report->add_metrics()); - } - } -} - void MetricsManager::dumpStates(FILE* out, bool verbose) { fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str()); { @@ -173,9 +165,8 @@ void MetricsManager::dumpStates(FILE* out, bool verbose) { } } -void MetricsManager::onDumpReport(ProtoOutputStream* protoOutput) { +void MetricsManager::onDumpReport(const uint64_t dumpTimeStampNs, ProtoOutputStream* protoOutput) { VLOG("=========================Metric Reports Start=========================="); - uint64_t dumpTimeStampNs = time(nullptr) * NS_PER_SEC; // one StatsLogReport per MetricProduer for (const auto& producer : mAllMetricProducers) { if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 2b30f44d345a..d4f844fe386c 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -70,8 +70,8 @@ public: }; // Config source owner can call onDumpReport() to get all the metrics collected. - virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput); - virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report); + virtual void onDumpReport(const uint64_t dumpTimeNs, + android::util::ProtoOutputStream* protoOutput); // Computes the total byte size of all metrics managed by a single config source. // Does not change the state. diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 7b1944c595e5..45b4ac0cd43a 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -17,7 +17,6 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" -#include "dimension.h" #include "ValueMetricProducer.h" #include "guardrail/StatsdStats.h" #include "stats_log_util.h" @@ -79,15 +78,28 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric } mBucketSizeNs = bucketSizeMills * 1000000; - mDimensionsInWhat = metric.dimensions_in_what(); - mDimensionsInCondition = metric.dimensions_in_condition(); + if (metric.has_dimensions_in_what()) { + translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + } + + if (metric.has_dimensions_in_condition()) { + translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition); + } if (metric.links().size() > 0) { - mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), - metric.links().end()); + for (const auto& link : metric.links()) { + Metric2Condition mc; + mc.conditionId = link.condition(); + translateFieldMatcher(link.fields_in_what(), &mc.metricFields); + translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + mMetric2ConditionLinks.push_back(mc); + } } - mConditionSliced = (metric.links().size() > 0)|| - (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); + + if (mValueField.child_size()) { + mField = mValueField.child(0).field(); + } + mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); if (!metric.has_condition() && mPullTagId != -1) { VLOG("Setting up periodic pulling for %d", mPullTagId); @@ -117,25 +129,6 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } -void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { - flushIfNeededLocked(dumpTimeNs); - report->set_metric_id(mMetricId); - auto value_metrics = report->mutable_value_metrics(); - for (const auto& pair : mPastBuckets) { - ValueMetricData* metricData = value_metrics->add_data(); - *metricData->mutable_dimensions_in_what() = - pair.first.getDimensionKeyInWhat().getDimensionsValue(); - *metricData->mutable_dimensions_in_condition() = - pair.first.getDimensionKeyInCondition().getDimensionsValue(); - for (const auto& bucket : pair.second) { - ValueBucketInfo* bucketInfo = metricData->add_bucket_info(); - bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); - bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs); - bucketInfo->set_value(bucket.mValue); - } - } -} - void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { VLOG("metric %lld dump report now...", (long long)mMetricId); @@ -155,14 +148,12 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionToken); if (dimensionKey.hasDimensionKeyInCondition()) { long long dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); } @@ -284,11 +275,11 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( } Interval& interval = mCurrentSlicedBucket[eventKey]; - std::shared_ptr<FieldValueMap> valueFieldMap = getValueFields(event); - if (valueFieldMap->empty() || valueFieldMap->size() > 1) { + int error = 0; + const long value = event.GetLong(mField, &error); + if (error < 0) { return; } - const long value = getLongFromDimenValue(valueFieldMap->begin()->second); if (mPullTagId != -1) { // for pulled events if (mCondition == true) { @@ -324,13 +315,6 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( } } -std::shared_ptr<FieldValueMap> ValueMetricProducer::getValueFields(const LogEvent& event) { - std::shared_ptr<FieldValueMap> valueFields = - std::make_shared<FieldValueMap>(event.getFieldValueMap()); - filterFields(mValueField, valueFields.get()); - return valueFields; -} - void ValueMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index bf5b7dfa3ca6..6701a46acde1 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -89,7 +89,6 @@ protected: private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; - void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; @@ -120,6 +119,8 @@ private: // tagId for pulled data. -1 if this is not pulled const int mPullTagId; + int mField; + // internal state of a bucket. typedef struct { // Pulled data always come in pair of <start, end>. This holds the value @@ -142,8 +143,6 @@ private: // TODO: Add a lock to mPastBuckets. std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets; - std::shared_ptr<FieldValueMap> getValueFields(const LogEvent& event); - // Util function to check whether the specified dimension hits the guardrail. bool hitGuardRailLocked(const MetricDimensionKey& newKey); diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 356a81c3e88d..8f236fa8bab6 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -62,7 +62,7 @@ class DurationTracker { public: DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, - const FieldMatcher& dimensionInCondition, bool nesting, + const std::vector<Matcher>& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers) @@ -182,7 +182,7 @@ protected: const int64_t mBucketSizeNs; - const FieldMatcher mDimensionInCondition; + const std::vector<Matcher>& mDimensionInCondition; const bool mNested; diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index c3bafc67adbf..c29876b5eae0 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -27,7 +27,7 @@ namespace statsd { MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, - const FieldMatcher& dimensionInCondition, bool nesting, + const vector<Matcher>& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, @@ -55,9 +55,7 @@ bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { // 1. Report the tuple count if the tuple count > soft limit if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mInfos.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize( - mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey), - newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { ALOGE("MaxDurTracker %lld dropping data for dimension key %s", @@ -229,10 +227,11 @@ void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) { ConditionState conditionState = mWizard->query( mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition, &conditionDimensionKeySet); - bool conditionMet = (conditionState == ConditionState::kTrue) && - (!mDimensionInCondition.has_field() || - conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) != - conditionDimensionKeySet.end()); + bool conditionMet = + (conditionState == ConditionState::kTrue) && + (mDimensionInCondition.size() == 0 || + conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) != + conditionDimensionKeySet.end()); VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet); noteConditionChanged(pair.first, conditionMet, timestamp); } diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index fba4119656bd..95863b60a192 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -30,7 +30,7 @@ class MaxDurationTracker : public DurationTracker { public: MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, - const FieldMatcher& dimensionInCondition, bool nesting, + const std::vector<Matcher>& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers); diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index 85f7b7c4afc1..f583f91608b8 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -26,7 +26,7 @@ using std::pair; OringDurationTracker::OringDurationTracker( const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, - sp<ConditionWizard> wizard, int conditionIndex, const FieldMatcher& dimensionInCondition, + sp<ConditionWizard> wizard, int conditionIndex, const vector<Matcher>& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers) @@ -53,9 +53,7 @@ bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { } if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mConditionKeyMap.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize( - mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey), - newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { ALOGE("OringDurTracker %lld dropping data for dimension key %s", @@ -229,9 +227,9 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key], mDimensionInCondition, &conditionDimensionKeySet); if (conditionState != ConditionState::kTrue || - (mDimensionInCondition.has_field() && + (mDimensionInCondition.size() != 0 && conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) == - conditionDimensionKeySet.end())) { + conditionDimensionKeySet.end())) { startedToPaused.push_back(*it); it = mStarted.erase(it); VLOG("Key %s started -> paused", key.c_str()); @@ -261,9 +259,9 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key], mDimensionInCondition, &conditionDimensionKeySet); if (conditionState == ConditionState::kTrue && - (!mDimensionInCondition.has_field() || - conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) - != conditionDimensionKeySet.end())) { + (mDimensionInCondition.size() == 0 || + conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) != + conditionDimensionKeySet.end())) { pausedToStarted.push_back(*it); it = mPaused.erase(it); VLOG("Key %s paused -> started", key.c_str()); diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index 73e50e0569cb..07c13294fa44 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -29,8 +29,8 @@ class OringDurationTracker : public DurationTracker { public: OringDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard, - int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting, - uint64_t currentBucketStartNs, uint64_t currentBucketNum, + int conditionIndex, const std::vector<Matcher>& dimensionInCondition, + bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 205c8e4b22cd..769f46dbd7e6 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -38,6 +38,22 @@ namespace android { namespace os { namespace statsd { +namespace { + +bool hasLeafNode(const FieldMatcher& matcher) { + if (!matcher.has_field()) { + return false; + } + for (int i = 0; i < matcher.child_size(); ++i) { + if (hasLeafNode(matcher.child(i))) { + return true; + } + } + return true; +} + +} // namespace + bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex, const bool usedForDimension, const vector<sp<LogMatchingTracker>>& allAtomMatchers, diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 0d7b722c40bc..691423e054b2 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define DEBUG false // STOPSHIP if true +#define DEBUG true // STOPSHIP if true #include "Log.h" #include "guardrail/StatsdStats.h" diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 6c6140081bd8..86c258bd3dfd 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -16,9 +16,10 @@ #include "stats_log_util.h" +#include <logd/LogEvent.h> +#include <utils/Log.h> #include <set> #include <stack> -#include <utils/Log.h> using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_BOOL; @@ -42,6 +43,8 @@ const int DIMENSIONS_VALUE_VALUE_BOOL = 5; const int DIMENSIONS_VALUE_VALUE_FLOAT = 6; const int DIMENSIONS_VALUE_VALUE_TUPLE = 7; +const int DIMENSIONS_VALUE_TUPLE_VALUE = 1; + // for MessageValue Proto const int FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO = 1; @@ -51,186 +54,172 @@ const int FIELD_ID_PULL_ATOM_ID = 1; const int FIELD_ID_TOTAL_PULL = 2; const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3; const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4; +namespace { -void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue, - ProtoOutputStream* protoOutput) { - if (!dimensionsValue.has_field()) { - return; - } - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field()); - switch (dimensionsValue.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR, - dimensionsValue.value_str()); - break; - case DimensionsValue::ValueCase::kValueInt: - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT, - dimensionsValue.value_int()); - break; - case DimensionsValue::ValueCase::kValueLong: - protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG, - dimensionsValue.value_long()); - break; - case DimensionsValue::ValueCase::kValueBool: - protoOutput->write(FIELD_TYPE_BOOL | DIMENSIONS_VALUE_VALUE_BOOL, - dimensionsValue.value_bool()); - break; - case DimensionsValue::ValueCase::kValueFloat: - protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT, - dimensionsValue.value_float()); - break; - case DimensionsValue::ValueCase::kValueTuple: - { - long long tupleToken = protoOutput->start( - FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); - for (int i = 0; i < dimensionsValue.value_tuple().dimensions_value_size(); ++i) { - long long token = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED - | FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO); - writeDimensionsValueProtoToStream( - dimensionsValue.value_tuple().dimensions_value(i), protoOutput); - protoOutput->end(token); - } - protoOutput->end(tupleToken); +void writeDimensionToProtoHelper(const std::vector<FieldValue>& dims, size_t* index, int depth, + int prefix, ProtoOutputStream* protoOutput) { + size_t count = dims.size(); + while (*index < count) { + const auto& dim = dims[*index]; + const int valueDepth = dim.mField.getDepth(); + const int valuePrefix = dim.mField.getPrefix(depth); + const int fieldNum = dim.mField.getPosAtDepth(depth); + if (valueDepth > 2) { + ALOGE("Depth > 2 not supported"); + return; + } + + if (depth == valueDepth && valuePrefix == prefix) { + long long token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + DIMENSIONS_VALUE_TUPLE_VALUE); + protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); + switch (dim.mValue.getType()) { + case INT: + protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT, + dim.mValue.int_value); + break; + case LONG: + protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG, + (long long)dim.mValue.long_value); + break; + case FLOAT: + protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT, + dim.mValue.float_value); + break; + case STRING: + protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR, + dim.mValue.str_value); + break; + default: + break; } - break; - default: - break; + if (token != 0) { + protoOutput->end(token); + } + (*index)++; + } else if (valueDepth > depth && valuePrefix == prefix) { + // Writing the sub tree + long long dimensionToken = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE); + protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); + long long tupleToken = + protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); + writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth), + protoOutput); + protoOutput->end(tupleToken); + protoOutput->end(dimensionToken); + } else { + // Done with the prev sub tree + return; + } } } -// for Field Proto -const int FIELD_FIELD = 1; -const int FIELD_POSITION_INDEX = 2; -const int FIELD_CHILD = 3; +} // namespace -void writeFieldProtoToStream( - const Field& field, util::ProtoOutputStream* protoOutput) { - if (!field.has_field()) { +void writeDimensionToProto(const HashableDimensionKey& dimension, ProtoOutputStream* protoOutput) { + if (dimension.getValues().size() == 0) { return; } - protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field()); - if (field.has_position_index()) { - protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index()); - } - for (int i = 0; i < field.child_size(); ++i) { - long long childToken = protoOutput->start( - FIELD_TYPE_MESSAGE| FIELD_COUNT_REPEATED | FIELD_CHILD); - writeFieldProtoToStream(field.child(i), protoOutput); - protoOutput->end(childToken); - } + protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, + dimension.getValues()[0].mField.getTag()); + long long topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); + size_t index = 0; + writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, protoOutput); + protoOutput->end(topToken); } -namespace { +// for Field Proto +const int FIELD_FIELD = 1; +const int FIELD_POSITION_INDEX = 2; +const int FIELD_CHILD = 3; -void addOrUpdateChildrenMap( - const Field& root, - const Field& node, - std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) { - Field parentNode = root; - if (node.has_position_index()) { - appendLeaf(&parentNode, node.field(), node.position_index()); - } else { - appendLeaf(&parentNode, node.field()); - } - if (childrenMap->find(parentNode) == childrenMap->end()) { - childrenMap->insert(std::make_pair(parentNode, std::set<Field, FieldCmp>{})); - } - auto it = childrenMap->find(parentNode); - for (int i = 0; i < node.child_size(); ++i) { - auto child = node.child(i); - Field childNode = parentNode; - if (child.has_position_index()) { - appendLeaf(&childNode, child.field(), child.position_index()); - } else { - appendLeaf(&childNode, child.field()); +// Supported Atoms format +// XYZ_Atom { +// repeated SubMsg field_1 = 1; +// SubMsg2 field_2 = 2; +// int32/float/string/int63 field_3 = 3; +// } +// logd's msg format, doesn't allow us to distinguish between the 2 cases below +// Case (1): +// Atom { +// SubMsg { +// int i = 1; +// int j = 2; +// } +// repeated SubMsg +// } +// +// and case (2): +// Atom { +// SubMsg { +// repeated int i = 1; +// repeated int j = 2; +// } +// optional SubMsg = 1; +// } +// +// +void writeFieldValueTreeToStreamHelper(const std::vector<FieldValue>& dims, size_t* index, + int depth, int prefix, ProtoOutputStream* protoOutput) { + size_t count = dims.size(); + while (*index < count) { + const auto& dim = dims[*index]; + const int valueDepth = dim.mField.getDepth(); + const int valuePrefix = dim.mField.getPrefix(depth); + const int fieldNum = dim.mField.getPosAtDepth(depth); + if (valueDepth > 2) { + ALOGE("Depth > 2 not supported"); + return; } - it->second.insert(childNode); - addOrUpdateChildrenMap(parentNode, child, childrenMap); - } -} - -void addOrUpdateChildrenMap( - const Field& field, - std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) { - Field root; - addOrUpdateChildrenMap(root, field, childrenMap); -} -} // namespace - -void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap, - util::ProtoOutputStream* protoOutput) { - std::map<Field, std::set<Field, FieldCmp>, FieldCmp> childrenMap; - // Rebuild the field tree. - for (auto it = fieldValueMap.begin(); it != fieldValueMap.end(); ++it) { - addOrUpdateChildrenMap(it->first, &childrenMap); - } - std::stack<std::pair<long long, Field>> tokenStack; - // Iterate over the node tree to fill the Atom proto. - for (auto it = childrenMap.begin(); it != childrenMap.end(); ++it) { - const Field* nodeLeaf = getSingleLeaf(&it->first); - const int fieldNum = nodeLeaf->field(); - while (!tokenStack.empty()) { - auto currentMsgNode = tokenStack.top().second; - auto currentMsgNodeChildrenIt = childrenMap.find(currentMsgNode); - if (currentMsgNodeChildrenIt->second.find(it->first) == - currentMsgNodeChildrenIt->second.end()) { - protoOutput->end(tokenStack.top().first); - tokenStack.pop(); - } else { - break; + if (depth == valueDepth && valuePrefix == prefix) { + switch (dim.mValue.getType()) { + case INT: + protoOutput->write(FIELD_TYPE_INT32 | fieldNum, dim.mValue.int_value); + break; + case LONG: + protoOutput->write(FIELD_TYPE_INT64 | fieldNum, + (long long)dim.mValue.long_value); + break; + case FLOAT: + protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value); + break; + case STRING: + protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value); + break; } - } - if (it->second.size() == 0) { - auto itValue = fieldValueMap.find(it->first); - if (itValue != fieldValueMap.end()) { - const DimensionsValue& value = itValue->second; - switch (value.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - protoOutput->write(FIELD_TYPE_STRING | fieldNum, - value.value_str()); - break; - case DimensionsValue::ValueCase::kValueInt: - protoOutput->write(FIELD_TYPE_INT32 | fieldNum, - value.value_int()); - break; - case DimensionsValue::ValueCase::kValueLong: - protoOutput->write(FIELD_TYPE_INT64 | fieldNum, - value.value_long()); - break; - case DimensionsValue::ValueCase::kValueBool: - protoOutput->write(FIELD_TYPE_BOOL | fieldNum, - value.value_bool()); - break; - case DimensionsValue::ValueCase::kValueFloat: - protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, - value.value_float()); - break; - // This would not happen as the node has no child. - case DimensionsValue::ValueCase::kValueTuple: - break; - default: - break; - } - } else { - ALOGE("Leaf node value not found. This should never happen."); + (*index)++; + } else if (valueDepth > depth && valuePrefix == prefix) { + // Writing the sub tree + long long msg_token = 0; + if (valueDepth == depth + 2) { + msg_token = + protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum); + } else if (valueDepth == depth + 1) { + msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum); } - } else { - long long token; - if (nodeLeaf->has_position_index()) { - token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum); - } else { - token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum); + // Directly jump to the leaf value because the repeated position field is implied + // by the position of the sub msg in the parent field. + writeFieldValueTreeToStreamHelper(dims, index, valueDepth, + dim.mField.getPrefix(valueDepth), protoOutput); + if (msg_token != 0) { + protoOutput->end(msg_token); } - tokenStack.push(std::make_pair(token, it->first)); + } else { + // Done with the prev sub tree + return; } } +} - while (!tokenStack.empty()) { - protoOutput->end(tokenStack.top().first); - tokenStack.pop(); - } +void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values, + util::ProtoOutputStream* protoOutput) { + long long atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId); + + size_t index = 0; + writeFieldValueTreeToStreamHelper(values, &index, 0, 0, protoOutput); + protoOutput->end(atomToken); } int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) { diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index cee920038eef..6583f579648b 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -17,7 +17,8 @@ #pragma once #include <android/util/ProtoOutputStream.h> -#include "field_util.h" +#include "FieldValue.h" +#include "HashableDimensionKey.h" #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "guardrail/StatsdStats.h" @@ -26,16 +27,10 @@ namespace android { namespace os { namespace statsd { -// Helper function to write DimensionsValue proto to ProtoOutputStream. -void writeDimensionsValueProtoToStream(const DimensionsValue& fieldValue, - util::ProtoOutputStream* protoOutput); - -// Helper function to write Field proto to ProtoOutputStream. -void writeFieldProtoToStream(const Field& field, util::ProtoOutputStream* protoOutput); - -// Helper function to construct the field value tree and write to ProtoOutputStream -void writeFieldValueTreeToStream(const FieldValueMap& fieldValueMap, +void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values, util::ProtoOutputStream* protoOutput); +void writeDimensionToProto(const HashableDimensionKey& dimension, + util::ProtoOutputStream* protoOutput); // Convert the TimeUnit enum to the bucket size in millis. int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit); diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp index 3af684fa6069..9f68fc4c20be 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp +++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp @@ -98,48 +98,64 @@ void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender, ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService."); return; } - mStatsCompanionService->sendSubscriberBroadcast(intentSender, - configKey.GetUid(), - configKey.GetId(), - subscription.id(), - subscription.rule_id(), - protoToStatsDimensionsValue(dimKey)); + mStatsCompanionService->sendSubscriberBroadcast( + intentSender, configKey.GetUid(), configKey.GetId(), subscription.id(), + subscription.rule_id(), getStatsDimensionsValue(dimKey.getDimensionKeyInWhat())); } -StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue( - const MetricDimensionKey& dimKey) { - return protoToStatsDimensionsValue(dimKey.getDimensionKeyInWhat().getDimensionsValue()); +void getStatsDimensionsValueHelper(const std::vector<FieldValue>& dims, size_t* index, int depth, + int prefix, vector<StatsDimensionsValue>* output) { + size_t count = dims.size(); + while (*index < count) { + const auto& dim = dims[*index]; + const int valueDepth = dim.mField.getDepth(); + const int valuePrefix = dim.mField.getPrefix(depth); + if (valueDepth > 2) { + ALOGE("Depth > 2 not supported"); + return; + } + if (depth == valueDepth && valuePrefix == prefix) { + switch (dim.mValue.getType()) { + case INT: + output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), + dim.mValue.int_value)); + break; + case LONG: + output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), + dim.mValue.long_value)); + break; + case FLOAT: + output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), + dim.mValue.float_value)); + break; + case STRING: + output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), + String16(dim.mValue.str_value.c_str()))); + break; + default: + break; + } + (*index)++; + } else if (valueDepth > depth && valuePrefix == prefix) { + vector<StatsDimensionsValue> childOutput; + getStatsDimensionsValueHelper(dims, index, depth + 1, dim.mField.getPrefix(depth + 1), + &childOutput); + output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), childOutput)); + } else { + return; + } + } } -StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue( - const DimensionsValue& protoDimsVal) { - int32_t field = protoDimsVal.field(); - - switch (protoDimsVal.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return StatsDimensionsValue(field, String16(protoDimsVal.value_str().c_str())); - case DimensionsValue::ValueCase::kValueInt: - return StatsDimensionsValue(field, static_cast<int32_t>(protoDimsVal.value_int())); - case DimensionsValue::ValueCase::kValueLong: - return StatsDimensionsValue(field, static_cast<int64_t>(protoDimsVal.value_long())); - case DimensionsValue::ValueCase::kValueBool: - return StatsDimensionsValue(field, static_cast<bool>(protoDimsVal.value_bool())); - case DimensionsValue::ValueCase::kValueFloat: - return StatsDimensionsValue(field, static_cast<float>(protoDimsVal.value_float())); - case DimensionsValue::ValueCase::kValueTuple: - { - int sz = protoDimsVal.value_tuple().dimensions_value_size(); - std::vector<StatsDimensionsValue> sdvVec(sz); - for (int i = 0; i < sz; i++) { - sdvVec[i] = protoToStatsDimensionsValue( - protoDimsVal.value_tuple().dimensions_value(i)); - } - return StatsDimensionsValue(field, sdvVec); - } - default: - ALOGW("protoToStatsDimensionsValue failed: illegal type."); - return StatsDimensionsValue(); +StatsDimensionsValue SubscriberReporter::getStatsDimensionsValue(const HashableDimensionKey& dim) { + if (dim.getValues().size() == 0) { + return StatsDimensionsValue(); } + + vector<StatsDimensionsValue> fields; + size_t index = 0; + getStatsDimensionsValueHelper(dim.getValues(), &index, 0, 0, &fields); + return StatsDimensionsValue(dim.getValues()[0].mField.getTag(), fields); } } // namespace statsd diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h index 13fc7fd06279..c7d1a5ba9425 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.h +++ b/cmds/statsd/src/subscriber/SubscriberReporter.h @@ -82,6 +82,8 @@ public: const Subscription& subscription, const MetricDimensionKey& dimKey) const; + static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim); + private: SubscriberReporter() {}; @@ -102,14 +104,6 @@ private: const ConfigKey& configKey, const Subscription& subscription, const MetricDimensionKey& dimKey) const; - - /** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */ - static StatsDimensionsValue protoToStatsDimensionsValue( - const DimensionsValue& protoDimsVal); - - /** Converts a HashableDimensionKey to a StatsDimensionsValue. */ - static StatsDimensionsValue protoToStatsDimensionsValue( - const MetricDimensionKey& dimKey); }; } // namespace statsd diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp new file mode 100644 index 000000000000..f1ad0c88b242 --- /dev/null +++ b/cmds/statsd/tests/FieldValue_test.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <gtest/gtest.h> +#include "src/logd/LogEvent.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "matchers/matcher_util.h" +#include "stats_log_util.h" +#include "stats_util.h" +#include "subscriber/SubscriberReporter.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +TEST(AtomMatcherTest, TestFieldTranslation) { + FieldMatcher matcher1; + matcher1.set_field(10); + FieldMatcher* child = matcher1.add_child(); + child->set_field(1); + child->set_position(Position::ANY); + + child = child->add_child(); + child->set_field(1); + + vector<Matcher> output; + translateFieldMatcher(matcher1, &output); + + EXPECT_EQ((size_t)1, output.size()); + + const auto& matcher12 = output[0]; + EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag()); + EXPECT_EQ((int32_t)0x2010001, matcher12.mMatcher.getField()); + EXPECT_EQ((int32_t)0xff7f007f, matcher12.mMask); +} + +TEST(AtomMatcherTest, TestFilter) { + FieldMatcher matcher1; + matcher1.set_field(10); + FieldMatcher* child = matcher1.add_child(); + child->set_field(1); + child->set_position(Position::ANY); + + child = child->add_child(); + child->set_field(1); + + child = matcher1.add_child(); + child->set_field(2); + + vector<Matcher> matchers; + translateFieldMatcher(matcher1, &matchers); + + AttributionNode attribution_node1; + attribution_node1.set_uid(1111); + attribution_node1.set_tag("location1"); + + AttributionNode attribution_node2; + attribution_node2.set_uid(2222); + attribution_node2.set_tag("location2"); + + AttributionNode attribution_node3; + attribution_node3.set_uid(3333); + attribution_node3.set_tag("location3"); + std::vector<AttributionNode> attribution_nodes = {attribution_node1, attribution_node2, + attribution_node3}; + + // Set up the event + LogEvent event(10, 12345); + event.write(attribution_nodes); + event.write("some value"); + // Convert to a LogEvent + event.init(); + vector<HashableDimensionKey> output; + + filterValues(matchers, event.getValues(), &output); + + EXPECT_EQ((size_t)(3), output.size()); + + const auto& key1 = output[0]; + EXPECT_EQ((size_t)2, key1.getValues().size()); + EXPECT_EQ((int32_t)0x02010001, key1.getValues()[0].mField.getField()); + EXPECT_EQ((int32_t)1111, key1.getValues()[0].mValue.int_value); + EXPECT_EQ((int32_t)0x00020000, key1.getValues()[1].mField.getField()); + EXPECT_EQ("some value", key1.getValues()[1].mValue.str_value); + + const auto& key2 = output[1]; + EXPECT_EQ((size_t)2, key2.getValues().size()); + EXPECT_EQ((int32_t)0x02010001, key2.getValues()[0].mField.getField()); + EXPECT_EQ((int32_t)2222, key2.getValues()[0].mValue.int_value); + EXPECT_EQ((int32_t)0x00020000, key2.getValues()[1].mField.getField()); + EXPECT_EQ("some value", key2.getValues()[1].mValue.str_value); + + const auto& key3 = output[2]; + EXPECT_EQ((size_t)2, key3.getValues().size()); + EXPECT_EQ((int32_t)0x02010001, key3.getValues()[0].mField.getField()); + EXPECT_EQ((int32_t)3333, key3.getValues()[0].mValue.int_value); + EXPECT_EQ((int32_t)0x00020000, key3.getValues()[1].mField.getField()); + EXPECT_EQ("some value", key3.getValues()[1].mValue.str_value); +} + +TEST(AtomMatcherTest, TestSubDimension) { + HashableDimensionKey dim; + + int pos1[] = {1, 1, 1}; + int pos2[] = {1, 1, 2}; + int pos3[] = {1, 1, 3}; + int pos4[] = {2, 0, 0}; + Field field1(10, pos1, 2); + Field field2(10, pos2, 2); + + Field field3(10, pos3, 2); + Field field4(10, pos4, 0); + + Value value1((int32_t)10025); + Value value2("tag"); + + Value value11((int32_t)10026); + Value value22("tag2"); + + dim.addValue(FieldValue(field1, value1)); + dim.addValue(FieldValue(field2, value2)); + + HashableDimensionKey subDim1; + subDim1.addValue(FieldValue(field1, value1)); + + HashableDimensionKey subDim2; + subDim1.addValue(FieldValue(field2, value2)); + + EXPECT_TRUE(dim.contains(dim)); + EXPECT_TRUE(dim.contains(subDim1)); + EXPECT_TRUE(dim.contains(subDim2)); + + HashableDimensionKey subDim3; + subDim3.addValue(FieldValue(field1, value11)); + EXPECT_FALSE(dim.contains(subDim3)); + + HashableDimensionKey subDim4; + // Empty dimension is always a sub dimension of other dimensions + EXPECT_TRUE(dim.contains(subDim4)); +} + +TEST(AtomMatcherTest, TestMetric2ConditionLink) { + AttributionNode attribution_node1; + attribution_node1.set_uid(1111); + attribution_node1.set_tag("location1"); + + AttributionNode attribution_node2; + attribution_node2.set_uid(2222); + attribution_node2.set_tag("location2"); + + AttributionNode attribution_node3; + attribution_node3.set_uid(3333); + attribution_node3.set_tag("location3"); + std::vector<AttributionNode> attribution_nodes = {attribution_node1, attribution_node2, + attribution_node3}; + + // Set up the event + LogEvent event(10, 12345); + event.write(attribution_nodes); + event.write("some value"); + // Convert to a LogEvent + event.init(); + + FieldMatcher whatMatcher; + whatMatcher.set_field(10); + FieldMatcher* child11 = whatMatcher.add_child(); + child11->set_field(1); + child11->set_position(Position::ANY); + child11 = child11->add_child(); + child11->set_field(1); + + FieldMatcher conditionMatcher; + conditionMatcher.set_field(27); + FieldMatcher* child2 = conditionMatcher.add_child(); + child2->set_field(2); + child2->set_position(Position::LAST); + + child2 = child2->add_child(); + child2->set_field(2); + + Metric2Condition link; + + translateFieldMatcher(whatMatcher, &link.metricFields); + translateFieldMatcher(conditionMatcher, &link.conditionFields); + + EXPECT_EQ((size_t)1, link.metricFields.size()); + EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField()); + EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask); + EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag()); + + EXPECT_EQ((size_t)1, link.conditionFields.size()); + EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField()); + EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask); + EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag()); +} + +TEST(AtomMatcherTest, TestSubscriberDimensionWrite) { + HashableDimensionKey dim; + + int pos1[] = {1, 1, 1}; + int pos2[] = {1, 1, 2}; + int pos3[] = {1, 1, 3}; + int pos4[] = {2, 0, 0}; + + Field field1(10, pos1, 2); + Field field2(10, pos2, 2); + Field field3(10, pos3, 2); + Field field4(10, pos4, 0); + + Value value1((int32_t)10025); + Value value2("tag"); + Value value3((int32_t)987654); + Value value4((int32_t)99999); + + dim.addValue(FieldValue(field1, value1)); + dim.addValue(FieldValue(field2, value2)); + dim.addValue(FieldValue(field3, value3)); + dim.addValue(FieldValue(field4, value4)); + + SubscriberReporter::getStatsDimensionsValue(dim); + // TODO: can't test anything here because SubscriberReport class doesn't have any read api. +} + +TEST(AtomMatcherTest, TestWriteDimensionToProto) { + HashableDimensionKey dim; + int pos1[] = {1, 1, 1}; + int pos2[] = {1, 1, 2}; + int pos3[] = {1, 1, 3}; + int pos4[] = {2, 0, 0}; + Field field1(10, pos1, 2); + Field field2(10, pos2, 2); + Field field3(10, pos3, 2); + Field field4(10, pos4, 0); + + Value value1((int32_t)10025); + Value value2("tag"); + Value value3((int32_t)987654); + Value value4((int32_t)99999); + + dim.addValue(FieldValue(field1, value1)); + dim.addValue(FieldValue(field2, value2)); + dim.addValue(FieldValue(field3, value3)); + dim.addValue(FieldValue(field4, value4)); + + android::util::ProtoOutputStream protoOut; + writeDimensionToProto(dim, &protoOut); + + vector<uint8_t> outData; + outData.resize(protoOut.size()); + size_t pos = 0; + auto iter = protoOut.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } + + DimensionsValue result; + EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); + EXPECT_EQ(10, result.field()); + EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case()); + EXPECT_EQ(2, result.value_tuple().dimensions_value_size()); + + const auto& dim1 = result.value_tuple().dimensions_value(0); + EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, dim1.value_case()); + EXPECT_EQ(3, dim1.value_tuple().dimensions_value_size()); + + const auto& dim11 = dim1.value_tuple().dimensions_value(0); + EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim11.value_case()); + EXPECT_EQ(10025, dim11.value_int()); + + const auto& dim12 = dim1.value_tuple().dimensions_value(1); + EXPECT_EQ(DimensionsValue::ValueCase::kValueStr, dim12.value_case()); + EXPECT_EQ("tag", dim12.value_str()); + + const auto& dim13 = dim1.value_tuple().dimensions_value(2); + EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim13.value_case()); + EXPECT_EQ(987654, dim13.value_int()); + + const auto& dim2 = result.value_tuple().dimensions_value(1); + EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim2.value_case()); + EXPECT_EQ(99999, dim2.value_int()); +} + +TEST(AtomMatcherTest, TestWriteAtomToProto) { + AttributionNode attribution_node1; + attribution_node1.set_uid(1111); + attribution_node1.set_tag("location1"); + + AttributionNode attribution_node2; + attribution_node2.set_uid(2222); + attribution_node2.set_tag("location2"); + + std::vector<AttributionNode> attribution_nodes = {attribution_node1, attribution_node2}; + + // Set up the event + LogEvent event(4, 12345); + event.write(attribution_nodes); + event.write((int32_t)999); + // Convert to a LogEvent + event.init(); + + android::util::ProtoOutputStream protoOutput; + writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput); + + vector<uint8_t> outData; + outData.resize(protoOutput.size()); + size_t pos = 0; + auto iter = protoOutput.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } + + Atom result; + EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); + EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case()); + const auto& atom = result.ble_scan_result_received(); + EXPECT_EQ(2, atom.attribution_node_size()); + EXPECT_EQ(1111, atom.attribution_node(0).uid()); + EXPECT_EQ("location1", atom.attribution_node(0).tag()); + EXPECT_EQ(2222, atom.attribution_node(1).uid()); + EXPECT_EQ("location2", atom.attribution_node(1).tag()); + EXPECT_EQ(999, atom.num_of_results()); +} + + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif
\ No newline at end of file diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index 111b4ba7a32c..1023ea40aa55 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -14,6 +14,7 @@ #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "matchers/matcher_util.h" +#include "stats_log_util.h" #include "stats_util.h" #include <gtest/gtest.h> @@ -35,8 +36,6 @@ const int FIELD_ID_3 = 2; const int ATTRIBUTION_UID_FIELD_ID = 1; const int ATTRIBUTION_TAG_FIELD_ID = 2; -// Private API from liblog. -extern "C" void android_log_rewind(android_log_context ctx); #ifdef __ANDROID__ TEST(AtomMatcherTest, TestSimpleMatcher) { @@ -597,7 +596,6 @@ TEST(AtomMatcherTest, TestNorMatcher) { matcherResults.push_back(MatchingState::kMatched); EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); } - #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp index fd28460e8e01..b64921501275 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -22,548 +22,142 @@ namespace android { namespace os { namespace statsd { -TEST(LogEventTest, testEmptyEvent) { - const int32_t TAG_ID = 123; - LogEvent event(TAG_ID, 0); - event.init(); - - DimensionsValue dimensionsValue; - EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(234, &dimensionsValue)); - FieldMatcher dimensions; - dimensions.set_field(event.GetTagId()); - EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); - - dimensions.add_child()->set_field(3); - dimensions.mutable_child(0)->set_position(Position::FIRST); - EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); - - dimensions.mutable_child(0)->set_position(Position::ANY); - EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); - - dimensions.mutable_child(0)->set_position(Position::LAST); - EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); +TEST(LogEventTest, TestLogParsing) { + LogEvent event1(1, 2000); + + std::vector<AttributionNode> nodes; + + AttributionNode node1; + node1.set_uid(1000); + node1.set_tag("tag1"); + nodes.push_back(node1); + + AttributionNode node2; + node2.set_uid(2000); + node2.set_tag("tag2"); + nodes.push_back(node2); + + event1.write(nodes); + event1.write("hello"); + event1.write((int32_t)10); + event1.write((int64_t)20); + event1.write((float)1.1); + event1.init(); + + const auto& items = event1.getValues(); + EXPECT_EQ((size_t)8, items.size()); + EXPECT_EQ(1, event1.GetTagId()); + + const FieldValue& item0 = event1.getValues()[0]; + EXPECT_EQ(0x2010101, item0.mField.getField()); + EXPECT_EQ(Type::INT, item0.mValue.getType()); + EXPECT_EQ(1000, item0.mValue.int_value); + + const FieldValue& item1 = event1.getValues()[1]; + EXPECT_EQ(0x2010182, item1.mField.getField()); + EXPECT_EQ(Type::STRING, item1.mValue.getType()); + EXPECT_EQ("tag1", item1.mValue.str_value); + + const FieldValue& item2 = event1.getValues()[2]; + EXPECT_EQ(0x2018201, item2.mField.getField()); + EXPECT_EQ(Type::INT, item2.mValue.getType()); + EXPECT_EQ(2000, item2.mValue.int_value); + + const FieldValue& item3 = event1.getValues()[3]; + EXPECT_EQ(0x2018282, item3.mField.getField()); + EXPECT_EQ(Type::STRING, item3.mValue.getType()); + EXPECT_EQ("tag2", item3.mValue.str_value); + + const FieldValue& item4 = event1.getValues()[4]; + EXPECT_EQ(0x20000, item4.mField.getField()); + EXPECT_EQ(Type::STRING, item4.mValue.getType()); + EXPECT_EQ("hello", item4.mValue.str_value); + + const FieldValue& item5 = event1.getValues()[5]; + EXPECT_EQ(0x30000, item5.mField.getField()); + EXPECT_EQ(Type::INT, item5.mValue.getType()); + EXPECT_EQ(10, item5.mValue.int_value); + + const FieldValue& item6 = event1.getValues()[6]; + EXPECT_EQ(0x40000, item6.mField.getField()); + EXPECT_EQ(Type::LONG, item6.mValue.getType()); + EXPECT_EQ((int64_t)20, item6.mValue.long_value); + + const FieldValue& item7 = event1.getValues()[7]; + EXPECT_EQ(0x50000, item7.mField.getField()); + EXPECT_EQ(Type::FLOAT, item7.mValue.getType()); + EXPECT_EQ((float)1.1, item7.mValue.float_value); } -TEST(LogEventTest, testRepeatedAttributionNode) { - const int32_t TAG_ID = 123; - LogEvent event(TAG_ID, 0); - AttributionNode attribution_node1; - attribution_node1.set_uid(1111); - attribution_node1.set_tag("locationService"); - - AttributionNode attribution_node2; - attribution_node2.set_uid(2222); - attribution_node2.set_tag("locationService2"); - - AttributionNode attribution_node3; - attribution_node3.set_uid(3333); - attribution_node3.set_tag("locationService3"); - std::vector<AttributionNode> attribution_nodes = - {attribution_node1, attribution_node2, attribution_node3}; - - // 1nd field: int32. - EXPECT_TRUE(event.write(int32_t(11))); - // 2rd field: float. - EXPECT_TRUE(event.write(3.45f)); - // Here it assume that the atom proto contains a repeated AttributionNode field. - // 3rd field: attribution node. This is repeated field. - EXPECT_TRUE(event.write(attribution_nodes)); - // 4th field: bool. - EXPECT_TRUE(event.write(true)); - // 5th field: long. - EXPECT_TRUE(event.write(uint64_t(1234))); - - event.init(); - - DimensionsValue dimensionsValue; - // Query single primitive fields. - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); - // The bool value is stored in value_int field as logD does not support bool. - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234)); - - // First attribution. - FieldMatcher first_uid_dimensions; - first_uid_dimensions.set_field(event.GetTagId()); - first_uid_dimensions.add_child()->set_field(3); - first_uid_dimensions.mutable_child(0)->set_position(Position::FIRST); - first_uid_dimensions.mutable_child(0)->add_child()->set_field(1); - EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_uid_dimensions, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - - FieldMatcher first_tag_dimensions = first_uid_dimensions; - first_tag_dimensions.mutable_child(0)->mutable_child(0)->set_field(2); - EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_tag_dimensions, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_str(), "locationService"); - - FieldMatcher first_attribution_dimensions = first_uid_dimensions; - first_attribution_dimensions.mutable_child(0)->add_child()->set_field(2); - EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_attribution_dimensions, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService"); - - FieldMatcher last_attribution_dimensions = first_attribution_dimensions; - last_attribution_dimensions.mutable_child(0)->set_position(Position::LAST); - EXPECT_TRUE(event.GetAtomDimensionsValueProto(last_attribution_dimensions, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 3333); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService3"); - - FieldMatcher any_attribution_dimensions = first_attribution_dimensions; - any_attribution_dimensions.mutable_child(0)->set_position(Position::ANY); - std::vector<DimensionsValue> dimensionsValues; - event.GetAtomDimensionsValueProtos(any_attribution_dimensions, &dimensionsValues); - EXPECT_EQ(dimensionsValues.size(), 3u); - EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService"); - EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 2222); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService2"); - EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 3333); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService3"); - - FieldMatcher mixed_dimensions = any_attribution_dimensions; - mixed_dimensions.add_child()->set_field(1000); - mixed_dimensions.add_child()->set_field(6); // missing field. - mixed_dimensions.add_child()->set_field(3); // position not set. - mixed_dimensions.add_child()->set_field(5); - mixed_dimensions.add_child()->set_field(1); - dimensionsValues.clear(); - event.GetAtomDimensionsValueProtos(mixed_dimensions, &dimensionsValues); - EXPECT_EQ(dimensionsValues.size(), 3u); - EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 3); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), - 1111); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(), - "locationService"); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).field(), 5); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).value_long(), long(1234)); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).field(), 1); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).value_int(), 11); - - EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 3); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), - 2222); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(), - "locationService2"); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).field(), 5); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).value_long(), long(1234)); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).field(), 1); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).value_int(), 11); - - EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 3); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), - 3333); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(), - "locationService3"); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).field(), 5); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).value_long(), long(1234)); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).field(), 1); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).value_int(), 11); - - FieldMatcher wrong_dimensions = mixed_dimensions; - // Wrong tagId. - wrong_dimensions.set_field(event.GetTagId() + 100); - dimensionsValues.clear(); - event.GetAtomDimensionsValueProtos(wrong_dimensions, &dimensionsValues); - EXPECT_TRUE(dimensionsValues.empty()); +TEST(LogEventTest, TestLogParsing2) { + LogEvent event1(1, 2000); + + std::vector<AttributionNode> nodes; + + event1.write("hello"); + + // repeated msg can be in the middle + AttributionNode node1; + node1.set_uid(1000); + node1.set_tag("tag1"); + nodes.push_back(node1); + + AttributionNode node2; + node2.set_uid(2000); + node2.set_tag("tag2"); + nodes.push_back(node2); + event1.write(nodes); + + event1.write((int32_t)10); + event1.write((int64_t)20); + event1.write((float)1.1); + event1.init(); + + const auto& items = event1.getValues(); + EXPECT_EQ((size_t)8, items.size()); + EXPECT_EQ(1, event1.GetTagId()); + + const FieldValue& item = event1.getValues()[0]; + EXPECT_EQ(0x00010000, item.mField.getField()); + EXPECT_EQ(Type::STRING, item.mValue.getType()); + EXPECT_EQ("hello", item.mValue.str_value); + + const FieldValue& item0 = event1.getValues()[1]; + EXPECT_EQ(0x2020101, item0.mField.getField()); + EXPECT_EQ(Type::INT, item0.mValue.getType()); + EXPECT_EQ(1000, item0.mValue.int_value); + + const FieldValue& item1 = event1.getValues()[2]; + EXPECT_EQ(0x2020182, item1.mField.getField()); + EXPECT_EQ(Type::STRING, item1.mValue.getType()); + EXPECT_EQ("tag1", item1.mValue.str_value); + + const FieldValue& item2 = event1.getValues()[3]; + EXPECT_EQ(0x2028201, item2.mField.getField()); + EXPECT_EQ(Type::INT, item2.mValue.getType()); + EXPECT_EQ(2000, item2.mValue.int_value); + + const FieldValue& item3 = event1.getValues()[4]; + EXPECT_EQ(0x2028282, item3.mField.getField()); + EXPECT_EQ(Type::STRING, item3.mValue.getType()); + EXPECT_EQ("tag2", item3.mValue.str_value); + + const FieldValue& item5 = event1.getValues()[5]; + EXPECT_EQ(0x30000, item5.mField.getField()); + EXPECT_EQ(Type::INT, item5.mValue.getType()); + EXPECT_EQ(10, item5.mValue.int_value); + + const FieldValue& item6 = event1.getValues()[6]; + EXPECT_EQ(0x40000, item6.mField.getField()); + EXPECT_EQ(Type::LONG, item6.mValue.getType()); + EXPECT_EQ((int64_t)20, item6.mValue.long_value); + + const FieldValue& item7 = event1.getValues()[7]; + EXPECT_EQ(0x50000, item7.mField.getField()); + EXPECT_EQ(Type::FLOAT, item7.mValue.getType()); + EXPECT_EQ((float)1.1, item7.mValue.float_value); } -TEST(LogEventTest, testMessageField) { - const int32_t TAG_ID = 123; - LogEvent event(TAG_ID, 0); - AttributionNode attribution_node1; - attribution_node1.set_uid(1111); - attribution_node1.set_tag("locationService"); - - AttributionNode attribution_node2; - attribution_node2.set_uid(2222); - attribution_node2.set_tag("locationService2"); - - // 1nd field: int32. - EXPECT_TRUE(event.write(int32_t(11))); - // 2rd field: float. - EXPECT_TRUE(event.write(3.45f)); - // Here it assume that the atom proto contains two optional AttributionNode fields. - // 3rd field: attribution node. This is not repeated field. - EXPECT_TRUE(event.write(attribution_node1)); - // 4th field: another attribution field. This is not repeated field. - EXPECT_TRUE(event.write(attribution_node2)); - // 5th field: bool. - EXPECT_TRUE(event.write(true)); - // 6th field: long. - EXPECT_TRUE(event.write(uint64_t(1234))); - - event.init(); - - FieldMatcher uid_dimensions1; - uid_dimensions1.set_field(event.GetTagId()); - uid_dimensions1.add_child()->set_field(3); - uid_dimensions1.mutable_child(0)->add_child()->set_field(1); - - FieldMatcher tag_dimensions1; - tag_dimensions1.set_field(event.GetTagId()); - tag_dimensions1.add_child()->set_field(3); - tag_dimensions1.mutable_child(0)->add_child()->set_field(2); - - FieldMatcher attribution_dimensions1; - attribution_dimensions1.set_field(event.GetTagId()); - attribution_dimensions1.add_child()->set_field(3); - attribution_dimensions1.mutable_child(0)->add_child()->set_field(1); - attribution_dimensions1.mutable_child(0)->add_child()->set_field(2); - - FieldMatcher uid_dimensions2 = uid_dimensions1; - uid_dimensions2.mutable_child(0)->set_field(4); - - FieldMatcher tag_dimensions2 = tag_dimensions1; - tag_dimensions2.mutable_child(0)->set_field(4); - - FieldMatcher attribution_dimensions2 = attribution_dimensions1; - attribution_dimensions2.mutable_child(0)->set_field(4); - - FieldMatcher mixed_dimensions = attribution_dimensions1; - mixed_dimensions.add_child()->set_field(4); - mixed_dimensions.mutable_child(1)->add_child()->set_field(1); - mixed_dimensions.add_child()->set_field(1000); - mixed_dimensions.add_child()->set_field(5); - mixed_dimensions.add_child()->set_field(1); - - DimensionsValue dimensionsValue; - - // Query single primitive fields. - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5); - // The bool value is stored in value_int field as logD does not support bool. - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234)); - - // Query atom field 3: attribution node uid field only. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - - // Query atom field 3: attribution node tag field only. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_str(), "locationService"); - - // Query atom field 3: attribution node uid + tag fields. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService"); - - // Query atom field 4: attribution node uid field only. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 2222); - - // Query atom field 4: attribution node tag field only. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_str(), "locationService2"); - - // Query atom field 4: attribution node uid + tag fields. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 2222); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService2"); - - // Query multiple fields: - // 1/ Field 3: attribution uid + tag. - // 2/ Field 4: attribution uid only. - // 3/ Field not exist. - // 4/ Primitive fields #5 - // 5/ Primitive fields #1 - EXPECT_TRUE(event.GetAtomDimensionsValueProto(mixed_dimensions, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 4); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService"); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).field(), 4); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple() - .dimensions_value(0).value_int(), 2222); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).field(), 5); - // The bool value is stored in value_int field as logD does not support bool. - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).value_int(), true); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).value_int(), 11); -} - -TEST(LogEventTest, testAllPrimitiveFields) { - const int32_t TAG_ID = 123; - LogEvent event(TAG_ID, 0); - - // 1nd field: int32. - EXPECT_TRUE(event.write(int32_t(11))); - // 2rd field: float. - EXPECT_TRUE(event.write(3.45f)); - // 3th field: string. - EXPECT_TRUE(event.write("test")); - // 4th field: bool. - EXPECT_TRUE(event.write(true)); - // 5th field: bool. - EXPECT_TRUE(event.write(false)); - // 6th field: long. - EXPECT_TRUE(event.write(uint64_t(1234))); - - event.init(); - - DimensionsValue dimensionsValue; - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(3, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_str(), "test"); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); - // The bool value is stored in value_int field as logD does not support bool. - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5); - // The bool value is stored in value_int field as logD does not support bool. - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), false); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234)); - - // Field not exist. - EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(7, &dimensionsValue)); -} - -TEST(LogEventTest, testWriteAtomProtoToStream) { - AttributionNode attribution_node1; - attribution_node1.set_uid(1111); - attribution_node1.set_tag("locationService"); - - AttributionNode attribution_node2; - attribution_node2.set_uid(2222); - attribution_node2.set_tag("locationService2"); - - AttributionNode attribution_node3; - attribution_node3.set_uid(3333); - attribution_node3.set_tag("locationService3"); - std::vector<AttributionNode> attribution_nodes = - {attribution_node1, attribution_node2, attribution_node3}; - - LogEvent event(1, 0); - EXPECT_TRUE(event.write("222")); - EXPECT_TRUE(event.write(attribution_nodes)); - EXPECT_TRUE(event.write(345)); - EXPECT_TRUE(event.write(attribution_node3)); - EXPECT_TRUE(event.write("hello")); - event.init(); - - util::ProtoOutputStream protoOutput; - // For now only see whether it will crash. - // TODO(yanglu): test parsing from stream. - event.ToProto(protoOutput); -} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index aab5bedb3cbe..cb72697941e0 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -45,7 +45,7 @@ public: } MOCK_METHOD0(byteSize, size_t()); - MOCK_METHOD1(onDumpReport, void(ProtoOutputStream* output)); + MOCK_METHOD2(onDumpReport, void(const uint64_t timeNs, ProtoOutputStream* output)); }; TEST(StatsLogProcessorTest, TestRateLimitByteSize) { @@ -69,24 +69,26 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { sp<UidMap> m = new UidMap(); sp<AnomalyMonitor> anomalyMonitor; int broadcastCount = 0; - StatsLogProcessor p(m, anomalyMonitor, 0, - [&broadcastCount](const ConfigKey& key) { broadcastCount++; }); + StatsLogProcessor p(m, anomalyMonitor, 0, [&broadcastCount](const ConfigKey& key) { + broadcastCount++; + }); MockMetricsManager mockMetricsManager; ConfigKey key(100, 12345); EXPECT_CALL(mockMetricsManager, byteSize()) - .Times(2) + .Times(1) .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95))); // Expect only one broadcast despite always returning a size that should trigger broadcast. p.flushIfNecessaryLocked(1, key, mockMetricsManager); EXPECT_EQ(1, broadcastCount); + // b/73089712 // This next call to flush should not trigger a broadcast. - p.mLastByteSizeTimes.clear(); // Force another check for byte size. - p.flushIfNecessaryLocked(2, key, mockMetricsManager); - EXPECT_EQ(1, broadcastCount); + // p.mLastByteSizeTimes.clear(); // Force another check for byte size. + // p.flushIfNecessaryLocked(2, key, mockMetricsManager); + // EXPECT_EQ(1, broadcastCount); } TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { @@ -103,7 +105,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { .Times(1) .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); - EXPECT_CALL(mockMetricsManager, onDumpReport(_)).Times(1); + EXPECT_CALL(mockMetricsManager, onDumpReport(_, _)).Times(1); // Expect to call the onDumpReport and skip the broadcast. p.flushIfNecessaryLocked(1, key, mockMetricsManager); diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index a415ea1a3069..b4a7bb706029 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -34,10 +34,10 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); MetricDimensionKey getMockMetricDimensionKey(int key, string value) { - DimensionsValue dimensionsValue; - dimensionsValue.set_field(key); - dimensionsValue.set_value_str(value); - return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY); + int pos[] = {key, 0, 0}; + HashableDimensionKey dim; + dim.addValue(FieldValue(Field(1, pos, 0), Value(value))); + return MetricDimensionKey(dim, DEFAULT_DIMENSION_KEY); } void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list, diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index d1b7b2842fd2..038d44936838 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -80,31 +80,31 @@ std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey( const std::vector<int> &uids, const string& conditionName) { std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap; std::vector<int> uid_indexes; + int pos[] = {1, 1, 1}; + int depth = 2; + Field field(1, pos, depth); switch(position) { case Position::FIRST: uid_indexes.push_back(0); break; case Position::LAST: uid_indexes.push_back(uids.size() - 1); + field.setField(0x02018001); break; case Position::ANY: uid_indexes.resize(uids.size()); std::iota(uid_indexes.begin(), uid_indexes.end(), 0); + field.setField(0x02010001); break; default: break; } for (const int idx : uid_indexes) { - DimensionsValue dimensionsValue; - dimensionsValue.set_field(TAG_ID); - dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field( - ATTRIBUTION_NODE_FIELD_ID); - dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0) - ->mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID); - dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0) - ->mutable_value_tuple()->mutable_dimensions_value(0)->set_value_int(uids[idx]); - outputKeyMap[StringToId(conditionName)].push_back(HashableDimensionKey(dimensionsValue)); + Value value((int32_t)uids[idx]); + HashableDimensionKey dim; + dim.addValue(FieldValue(field, value)); + outputKeyMap[StringToId(conditionName)].push_back(dim); } return outputKeyMap; } @@ -265,7 +265,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { TEST(SimpleConditionTrackerTest, TestSlicedCondition) { for (Position position : { Position::ANY, Position::FIRST, Position::LAST}) { - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; std::unordered_set<HashableDimensionKey> dimensionKeys; SimplePredicate simplePredicate = getWakeLockHeldCondition( @@ -374,7 +374,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { } TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; std::unordered_set<HashableDimensionKey> dimensionKeys; SimplePredicate simplePredicate = getWakeLockHeldCondition( @@ -470,7 +470,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { TEST(SimpleConditionTrackerTest, TestStopAll) { for (Position position : {Position::ANY, Position::FIRST, Position::LAST}) { - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; std::unordered_set<HashableDimensionKey> dimensionKeys; SimplePredicate simplePredicate = getWakeLockHeldCondition( true /*nesting*/, true /*default to false*/, true /*output slice by uid*/, @@ -576,7 +576,6 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } - } } // namespace statsd diff --git a/cmds/statsd/tests/dimension_test.cpp b/cmds/statsd/tests/dimension_test.cpp deleted file mode 100644 index 678abaed5fd8..000000000000 --- a/cmds/statsd/tests/dimension_test.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "dimension.h" - -#include <gtest/gtest.h> - -using namespace android::os::statsd; - -#ifdef __ANDROID__ - -TEST(DimensionTest, subLeafNodes) { - DimensionsValue dimension; - int tagId = 100; - dimension.set_field(tagId); - auto child = dimension.mutable_value_tuple()->add_dimensions_value(); - child->set_field(1); - child->set_value_int(2000); - - child = dimension.mutable_value_tuple()->add_dimensions_value(); - child->set_field(3); - child->set_value_str("test"); - - child = dimension.mutable_value_tuple()->add_dimensions_value(); - child->set_field(4); - auto grandChild = child->mutable_value_tuple()->add_dimensions_value(); - grandChild->set_field(1); - grandChild->set_value_float(1.3f); - grandChild = child->mutable_value_tuple()->add_dimensions_value(); - grandChild->set_field(3); - grandChild->set_value_str("tag"); - - child = dimension.mutable_value_tuple()->add_dimensions_value(); - child->set_field(6); - child->set_value_bool(false); - - DimensionsValue sub_dimension; - FieldMatcher matcher; - - // Tag id not matched. - matcher.set_field(tagId + 1); - EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Field not exist. - matcher.Clear(); - matcher.set_field(tagId); - matcher.add_child()->set_field(5); - EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Field exists. - matcher.Clear(); - matcher.set_field(tagId); - matcher.add_child()->set_field(3); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Field exists. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - matcher.add_child()->set_field(6); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Field exists. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - matcher.add_child()->set_field(1); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Not leaf field. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - matcher.add_child()->set_field(4); - EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Grand-child leaf field not exist. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - auto childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(2); - EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Grand-child leaf field. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(1); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(3); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Multiple grand-child fields. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(3); - childMatcher->add_child()->set_field(1); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Multiple fields. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(3); - childMatcher->add_child()->set_field(1); - matcher.add_child()->set_field(3); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Subset of the fields not exist. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(3); - childMatcher->add_child()->set_field(1); - matcher.add_child()->set_field(2); - EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp index a56db28e64b3..01743ef1a45c 100644 --- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp @@ -146,9 +146,13 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { processor->OnLogEvent(event.get()); } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, &reports); + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); + StatsLogReport::CountMetricDataWrapper countMetrics; sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); EXPECT_EQ(countMetrics.data_size(), 4); diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp index b5d48efb55f6..275b58244e0f 100644 --- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp @@ -43,8 +43,8 @@ StatsdConfig CreateCountMetricWithNoLinkConfig() { auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); // The predicate is dimensioning by any attribution node and both by uid and tag. *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateAttributionUidAndTagDimensions( - android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + CreateAttributionUidAndTagDimensions(android::util::WAKELOCK_STATE_CHANGED, + {Position::FIRST}); *config.add_predicate() = holdingWakelockPredicate; auto combinationPredicate = config.add_predicate(); @@ -57,8 +57,8 @@ StatsdConfig CreateCountMetricWithNoLinkConfig() { metric->set_id(StringToId("ScreenBrightnessChangeMetric")); metric->set_what(screenBrightnessChangeAtomMatcher.id()); metric->set_condition(combinationPredicate->id()); - *metric->mutable_dimensions_in_what() = CreateDimensions( - android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */}); + *metric->mutable_dimensions_in_what() = + CreateDimensions(android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */}); *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions( android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); metric->set_bucket(ONE_MINUTE); @@ -72,62 +72,54 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { auto config = CreateCountMetricWithNoLinkConfig(); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - std::vector<AttributionNode> attributions1 = - {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; + std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; - std::vector<AttributionNode> attributions2 = - {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; + std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; std::vector<std::unique_ptr<LogEvent>> events; - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + bucketSizeNs + 1)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs - 10)); - - events.push_back(CreateAcquireWakelockEvent( - attributions1, "wl1", bucketStartTimeNs + 200)); - events.push_back(CreateReleaseWakelockEvent( - attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1)); - - events.push_back(CreateAcquireWakelockEvent( - attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 100)); - events.push_back(CreateReleaseWakelockEvent( - attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 50)); - - events.push_back(CreateScreenBrightnessChangedEvent( - 123, bucketStartTimeNs + 11)); - events.push_back(CreateScreenBrightnessChangedEvent( - 123, bucketStartTimeNs + 101)); - events.push_back(CreateScreenBrightnessChangedEvent( - 123, bucketStartTimeNs + 201)); - events.push_back(CreateScreenBrightnessChangedEvent( - 456, bucketStartTimeNs + 203)); - events.push_back(CreateScreenBrightnessChangedEvent( - 456, bucketStartTimeNs + bucketSizeNs - 99)); - events.push_back(CreateScreenBrightnessChangedEvent( - 456, bucketStartTimeNs + bucketSizeNs - 2)); - events.push_back(CreateScreenBrightnessChangedEvent( - 789, bucketStartTimeNs + bucketSizeNs - 1)); - events.push_back(CreateScreenBrightnessChangedEvent( - 456, bucketStartTimeNs + bucketSizeNs + 2)); - events.push_back(CreateScreenBrightnessChangedEvent( - 789, bucketStartTimeNs + 2 * bucketSizeNs - 11)); - events.push_back(CreateScreenBrightnessChangedEvent( - 789, bucketStartTimeNs + 2 * bucketSizeNs - 9)); - events.push_back(CreateScreenBrightnessChangedEvent( - 789, bucketStartTimeNs + 2 * bucketSizeNs - 1)); + events.push_back( + CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + bucketSizeNs + 1)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 2 * bucketSizeNs - 10)); + + events.push_back(CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 200)); + events.push_back( + CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1)); + + events.push_back(CreateAcquireWakelockEvent(attributions2, "wl2", + bucketStartTimeNs + bucketSizeNs - 100)); + events.push_back(CreateReleaseWakelockEvent(attributions2, "wl2", + bucketStartTimeNs + 2 * bucketSizeNs - 50)); + + events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 11)); + events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 101)); + events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 201)); + events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + 203)); + events.push_back( + CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs - 99)); + events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs - 2)); + events.push_back(CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs + 2)); + events.push_back( + CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 11)); + events.push_back( + CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 9)); + events.push_back( + CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 1)); sortLogEventsByTimestamp(&events); @@ -136,7 +128,10 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); @@ -147,7 +142,7 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { auto data = countMetrics.data(0); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs ); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); @@ -164,7 +159,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123); - ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidDimension(data.dimensions_in_condition(), + android::util::WAKELOCK_STATE_CHANGED, 111); data = countMetrics.data(2); EXPECT_EQ(data.bucket_info_size(), 1); @@ -175,7 +171,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456); - ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidDimension(data.dimensions_in_condition(), + android::util::WAKELOCK_STATE_CHANGED, 111); data = countMetrics.data(3); EXPECT_EQ(data.bucket_info_size(), 2); @@ -189,7 +186,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456); - ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidDimension(data.dimensions_in_condition(), + android::util::WAKELOCK_STATE_CHANGED, 333); data = countMetrics.data(4); EXPECT_EQ(data.bucket_info_size(), 1); @@ -211,7 +209,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789); - ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidDimension(data.dimensions_in_condition(), + android::util::WAKELOCK_STATE_CHANGED, 111); data = countMetrics.data(6); EXPECT_EQ(data.bucket_info_size(), 1); @@ -222,7 +221,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789); - ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidDimension(data.dimensions_in_condition(), + android::util::WAKELOCK_STATE_CHANGED, 333); } namespace { @@ -239,8 +239,8 @@ StatsdConfig CreateCountMetricWithLinkConfig() { auto screenIsOffPredicate = CreateScreenIsOffPredicate(); auto isSyncingPredicate = CreateIsSyncingPredicate(); auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidAndTagDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, + {Position::FIRST}); syncDimension->add_child()->set_field(2 /* name field*/); *config.add_predicate() = screenIsOffPredicate; @@ -256,8 +256,8 @@ StatsdConfig CreateCountMetricWithLinkConfig() { metric->set_id(StringToId("AppCrashMetric")); metric->set_what(appCrashMatcher.id()); metric->set_condition(combinationPredicate->id()); - *metric->mutable_dimensions_in_what() = CreateDimensions( - android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */}); + *metric->mutable_dimensions_in_what() = + CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */}); *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( android::util::SYNC_STATE_CHANGED, {Position::FIRST}); @@ -267,8 +267,8 @@ StatsdConfig CreateCountMetricWithLinkConfig() { auto dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); return config; } @@ -279,18 +279,18 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { auto config = CreateCountMetricWithLinkConfig(); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - std::vector<AttributionNode> attributions1 = - {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; + std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; - std::vector<AttributionNode> attributions2 = - {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; + std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; std::vector<std::unique_ptr<LogEvent>> events; @@ -311,26 +311,26 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 700)); + events.push_back( + CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 202)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + bucketSizeNs + 700)); events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 300)); + events.push_back( + CreateSyncEndEvent(attributions1, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300)); events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc", - bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back( + CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1)); events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 400)); - events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 600)); + events.push_back( + CreateSyncEndEvent(attributions2, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 600)); sortLogEventsByTimestamp(&events); @@ -339,7 +339,10 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); @@ -363,8 +366,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 111, "App1"); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 2); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); @@ -386,8 +389,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 333, "App2"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 1); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); @@ -424,8 +427,8 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag auto screenIsOffPredicate = CreateScreenIsOffPredicate(); auto isSyncingPredicate = CreateIsSyncingPredicate(); auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidAndTagDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, + {Position::FIRST}); syncDimension->add_child()->set_field(2 /* name field */); *config.add_predicate() = inBatterySaverModePredicate; @@ -449,26 +452,25 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag } // namespace - TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { - for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { ConfigKey cfgKey; auto config = CreateDurationMetricConfigNoLink(aggregationType); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - std::vector<AttributionNode> attributions1 = - {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; + std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; - std::vector<AttributionNode> attributions2 = - {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; + std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; std::vector<std::unique_ptr<LogEvent>> events; @@ -485,26 +487,26 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870)); events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 800)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 202)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + bucketSizeNs + 800)); events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200)); events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 300)); + bucketStartTimeNs + bucketSizeNs + 300)); events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc", - bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back( + CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1)); events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401)); events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 700)); + bucketStartTimeNs + bucketSizeNs + 700)); sortLogEventsByTimestamp(&events); @@ -513,7 +515,10 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); @@ -534,8 +539,8 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { data = metrics.data(1); EXPECT_FALSE(data.dimensions_in_what().has_field()); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 111, "App1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); @@ -546,8 +551,8 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { data = metrics.data(2); EXPECT_FALSE(data.dimensions_in_what().has_field()); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 333, "App2"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); @@ -572,13 +577,13 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType auto screenIsOffPredicate = CreateScreenIsOffPredicate(); auto isSyncingPredicate = CreateIsSyncingPredicate(); auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidAndTagDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, + {Position::FIRST}); syncDimension->add_child()->set_field(2 /* name field */); auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); + CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */}); *config.add_predicate() = screenIsOffPredicate; *config.add_predicate() = isSyncingPredicate; @@ -594,8 +599,8 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType metric->set_id(StringToId("AppInBackgroundMetric")); metric->set_what(isInBackgroundPredicate.id()); metric->set_condition(combinationPredicate->id()); - *metric->mutable_dimensions_in_what() = CreateDimensions( - android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); + *metric->mutable_dimensions_in_what() = + CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */}); *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( android::util::SYNC_STATE_CHANGED, {Position::FIRST}); @@ -605,32 +610,32 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType auto dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); return config; } } // namespace TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { - for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { ConfigKey cfgKey; auto config = CreateDurationMetricConfigWithLink(aggregationType); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - std::vector<AttributionNode> attributions1 = - {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; + std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; - std::vector<AttributionNode> attributions2 = - {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; + std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; std::vector<std::unique_ptr<LogEvent>> events; @@ -643,26 +648,26 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399)); events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 801)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 202)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + bucketSizeNs + 801)); events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200)); events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 300)); + bucketStartTimeNs + bucketSizeNs + 300)); events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc", - bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back( + CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1)); events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401)); events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 700)); + bucketStartTimeNs + bucketSizeNs + 700)); sortLogEventsByTimestamp(&events); @@ -671,7 +676,10 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); @@ -691,8 +699,8 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { data = metrics.data(1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 111, "App1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); @@ -704,8 +712,8 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { data = metrics.data(2); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 333, "App2"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp index a80fdc5606b7..674d810a9ea9 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp @@ -140,7 +140,10 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { processor->OnLogEvent(event.get()); } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, &reports); + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp index 233031c5c8da..d00518147bfe 100644 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp @@ -95,8 +95,10 @@ StatsdConfig CreateStatsdConfig() { } } // namespace - -TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { +// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests, +// we should use the real API which will clear the data after dump data is called. +// TODO: better refactor the code so that the tests are not so verbose. +TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) { auto config = CreateStatsdConfig(); uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = @@ -195,7 +197,10 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { processor->OnLogEvent(event.get()); } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports); + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); @@ -208,16 +213,115 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { // Uid field. EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); +} + +TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) { + auto config = CreateStatsdConfig(); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - reports.Clear(); - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + int appUid = 123; + auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1); + auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201); + auto crashEvent3 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101); + + auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51); + auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299); + auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001); + + auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16); + auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249); + + auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351); + auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2); + + auto screenTurnedOnEvent = CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2); + auto screenTurnedOffEvent = CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 2 * bucketSizeNs - 100); + + std::vector<AttributionNode> attributions = {CreateAttribution(appUid, "App1"), + CreateAttribution(appUid + 1, "GMSCoreModule1")}; + auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50); + auto syncOffEvent1 = + CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); + auto syncOnEvent2 = + CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); + + auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15); + auto moveToForegroundEvent1 = + CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250); + + auto moveToBackgroundEvent2 = + CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350); + auto moveToForegroundEvent2 = + CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1); + + /* + bucket #1 bucket #2 + + + | | | | | | | | | | (crashEvents) + |-------------------------------------|-----------------------------------|--------- + + | | (MoveToBkground) + + | | (MoveToForeground) + + | | (SyncIsOn) + | (SyncIsOff) + | | (ScreenIsOn) + | (ScreenIsOff) + */ + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(std::move(crashEvent1)); + events.push_back(std::move(crashEvent2)); + events.push_back(std::move(crashEvent3)); + events.push_back(std::move(crashEvent4)); + events.push_back(std::move(crashEvent5)); + events.push_back(std::move(crashEvent6)); + events.push_back(std::move(crashEvent7)); + events.push_back(std::move(crashEvent8)); + events.push_back(std::move(crashEvent9)); + events.push_back(std::move(crashEvent10)); + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(syncOnEvent1)); + events.push_back(std::move(syncOffEvent1)); + events.push_back(std::move(syncOnEvent2)); + events.push_back(std::move(moveToBackgroundEvent1)); + events.push_back(std::move(moveToForegroundEvent1)); + events.push_back(std::move(moveToBackgroundEvent2)); + events.push_back(std::move(moveToForegroundEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3); - data = reports.reports(0).metrics(0).count_metrics().data(0); + auto data = reports.reports(0).metrics(0).count_metrics().data(0); // Validate dimension value. EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp index a99dbe85e7a5..3b25694b6517 100644 --- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp @@ -114,7 +114,7 @@ void FeedEvents(StatsdConfig config, sp<StatsLogProcessor> processor) { } // namespace -TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration) { +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) { ConfigKey cfgKey; auto config = CreateStatsdConfig(DurationMetric::SUM); uint64_t bucketStartTimeNs = 10000000000; @@ -124,8 +124,11 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration) { EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); FeedEvents(config, processor); + vector<uint8_t> buffer; ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports); + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); @@ -142,15 +145,30 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration) { // The wakelock holding interval starts from the screen off event and to the end of the 1st // bucket. EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200); +} - reports.Clear(); - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); // Dump the report after the end of 2nd bucket. EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); - data = reports.reports(0).metrics(0).duration_metrics().data(0); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); // Validate dimension value. ValidateAttributionUidDimension(data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111); @@ -162,6 +180,19 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration) { // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and // ends at the second screen on event. EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL); +} +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector<uint8_t> buffer; + ConfigMetricsReportList reports; std::vector<std::unique_ptr<LogEvent>> events; events.push_back( @@ -175,13 +206,15 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration) { for (const auto& event : events) { processor->OnLogEvent(event.get()); } - reports.Clear(); - processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &reports); + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6); - data = reports.reports(0).metrics(0).duration_metrics().data(0); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); ValidateAttributionUidDimension(data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111); // The last wakelock holding spans 4 buckets. @@ -191,7 +224,7 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration) { EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL); } -TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration) { +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) { ConfigKey cfgKey; auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); uint64_t bucketStartTimeNs = 10000000000; @@ -202,15 +235,35 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration) { EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); FeedEvents(config, processor); ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports); + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); - EXPECT_EQ(reports.reports(0).metrics_size(), 1); - // Nothing has ended in the first bucket. - EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 0); - reports.Clear(); - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as + // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by + // itself. + EXPECT_EQ(0, reports.reports(0).metrics_size()); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); @@ -222,6 +275,20 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration) { android::util::WAKELOCK_STATE_CHANGED, 111); // The max is acquire event for wl1 to screen off start. EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector<uint8_t> buffer; std::vector<std::unique_ptr<LogEvent>> events; events.push_back( @@ -235,13 +302,15 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration) { for (const auto& event : events) { processor->OnLogEvent(event.get()); } - reports.Clear(); - processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &reports); + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); - data = reports.reports(0).metrics(0).duration_metrics().data(0); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); ValidateAttributionUidDimension(data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111); // The last wakelock holding spans 4 buckets. diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 87a1079aa722..1e71b73ca9fa 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -13,7 +13,6 @@ // limitations under the License. #include "src/metrics/CountMetricProducer.h" -#include "src/dimension.h" #include "src/stats_log_util.h" #include "metrics_test_helper.h" #include "tests/statsd_test_util.h" diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index 3deab3710dd1..8246268d88e7 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -13,7 +13,6 @@ // limitations under the License. #include "src/metrics/EventMetricProducer.h" -#include "src/dimension.h" #include "metrics_test_helper.h" #include "tests/statsd_test_util.h" diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 470d4d0415b2..26f7c2669f15 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -80,9 +80,10 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { gaugeProducer.onDataPulled(allData); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); - EXPECT_EQ(10, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(10, it->mValue.int_value); it++; - EXPECT_EQ(11, it->second.value_int()); + EXPECT_EQ(11, it->mValue.int_value); EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); allData.clear(); @@ -96,16 +97,20 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { gaugeProducer.onDataPulled(allData); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); - EXPECT_EQ(24, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(24, it->mValue.int_value); it++; - EXPECT_EQ(25, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(25, it->mValue.int_value); // One dimension. EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size()); it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); - EXPECT_EQ(10L, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(10L, it->mValue.int_value); it++; - EXPECT_EQ(11L, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(11L, it->mValue.int_value); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum); gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs); @@ -114,9 +119,11 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); - EXPECT_EQ(24L, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(24L, it->mValue.int_value); it++; - EXPECT_EQ(25L, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(25L, it->mValue.int_value); EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum); } @@ -230,7 +237,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() .mFields->begin() - ->second.value_int()); + ->mValue.int_value); gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); @@ -240,7 +247,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() .mFields->begin() - ->second.value_int()); + ->mValue.int_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 1); @@ -254,7 +261,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() .mFields->begin() - ->second.value_int()); + ->mValue.int_value); } TEST(GaugeMetricProducerTest, TestWithCondition) { @@ -288,9 +295,10 @@ TEST(GaugeMetricProducerTest, TestWithCondition) { gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(100, - gaugeProducer.mCurrentSlicedBucket->begin()-> - second.front().mFields->begin()->second.value_int()); + EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); vector<shared_ptr<LogEvent>> allData; @@ -303,19 +311,26 @@ TEST(GaugeMetricProducerTest, TestWithCondition) { gaugeProducer.onDataPulled(allData); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(110, - gaugeProducer.mCurrentSlicedBucket->begin()-> - second.front().mFields->begin()->second.value_int()); + EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back() - .mGaugeAtoms.front().mFields->begin()->second.value_int()); + EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms.front() + .mFields->begin() + ->mValue.int_value); gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10); gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back() - .mGaugeAtoms.front().mFields->begin()->second.value_int()); + EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum); } @@ -353,9 +368,10 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { gaugeProducer.onDataPulled({event1}); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(13L, - gaugeProducer.mCurrentSlicedBucket->begin()-> - second.front().mFields->begin()->second.value_int()); + EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); std::shared_ptr<LogEvent> event2 = @@ -366,9 +382,10 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { gaugeProducer.onDataPulled({event2}); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(15L, - gaugeProducer.mCurrentSlicedBucket->begin()-> - second.front().mFields->begin()->second.value_int()); + EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec); @@ -380,9 +397,10 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { gaugeProducer.onDataPulled({event3}); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(26L, - gaugeProducer.mCurrentSlicedBucket->begin()-> - second.front().mFields->begin()->second.value_int()); + EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec); diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 2658e4ecc53c..3397f144cfbc 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -53,7 +53,8 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; @@ -89,7 +90,7 @@ TEST(MaxDurationTrackerTest, TestStopAll) { const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; @@ -126,7 +127,7 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; @@ -168,7 +169,7 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; @@ -206,7 +207,7 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey conditionKey1; diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index 4b579b15b16e..293b1a872a5a 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -52,7 +52,7 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; @@ -88,7 +88,7 @@ TEST(OringDurationTrackerTest, TestDurationNested) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; @@ -122,7 +122,7 @@ TEST(OringDurationTrackerTest, TestStopAll) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; @@ -155,7 +155,7 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; @@ -197,7 +197,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; @@ -238,7 +238,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; @@ -283,7 +283,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; @@ -326,7 +326,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; Alert alert; alert.set_id(101); alert.set_metric_id(1); @@ -395,7 +395,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; Alert alert; alert.set_id(101); alert.set_metric_id(1); @@ -446,7 +446,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector<Matcher> dimensionInCondition; Alert alert; alert.set_id(101); alert.set_metric_id(1); diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp index ab9345af172e..7b9c0d6ab28e 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp +++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp @@ -19,20 +19,26 @@ namespace os { namespace statsd { HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) { - DimensionsValue dimensionsValue; - dimensionsValue.set_field(tagId); - dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key); - dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value); - return HashableDimensionKey(dimensionsValue); + HashableDimensionKey dimension; + int pos[] = {key, 0, 0}; + dimension.addValue(FieldValue(Field(tagId, pos, 0), Value(value))); + + return dimension; } MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) { - DimensionsValue dimensionsValue; - dimensionsValue.set_field(tagId); - dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key); - dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value); - return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY); + return MetricDimensionKey(getMockedDimensionKey(tagId, key, value), DEFAULT_DIMENSION_KEY); +} + +void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) { + matcher->set_field(tagId); } + +void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatcher* matcher) { + matcher->set_field(tagId); + matcher->add_child()->set_field(fieldNum); +} + } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h index b48de540ac09..a01de6334ab4 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.h +++ b/cmds/statsd/tests/metrics/metrics_test_helper.h @@ -26,12 +26,10 @@ namespace statsd { class MockConditionWizard : public ConditionWizard { public: - MOCK_METHOD4( - query, - ConditionState(const int conditionIndex, - const ConditionKey& conditionParameters, - const FieldMatcher& dimensionFields, - std::unordered_set<HashableDimensionKey> *dimensionKeySet)); + MOCK_METHOD4(query, + ConditionState(const int conditionIndex, const ConditionKey& conditionParameters, + const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>* dimensionKeySet)); }; class MockStatsPullerManager : public StatsPullerManager { @@ -49,6 +47,10 @@ class MockUidMap : public UidMap { HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value); MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value); +// Utils to build FieldMatcher proto for simple one-depth atoms. +void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher); +void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher); + } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 13055cb9e7a3..d3a89617e921 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -186,7 +186,7 @@ Predicate CreateScreenIsOnPredicate() { Predicate CreateScreenIsOffPredicate() { Predicate predicate; - predicate.set_id(StringToId("ScreenIsOff")); + predicate.set_id(1111123); predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff")); predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn")); return predicate; @@ -202,7 +202,7 @@ Predicate CreateHoldingWakelockPredicate() { Predicate CreateIsSyncingPredicate() { Predicate predicate; - predicate.set_id(StringToId("IsSyncing")); + predicate.set_id(33333333333333); predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart")); predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd")); return predicate; @@ -461,6 +461,93 @@ void ValidateAttributionUidAndTagDimension( .value_tuple().dimensions_value(1).value_str(), tag); } +bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) { + if (s1.field() != s2.field()) { + return false; + } + if (s1.value_case() != s2.value_case()) { + return false; + } + switch (s1.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + return (s1.value_str() == s2.value_str()); + case DimensionsValue::ValueCase::kValueInt: + return s1.value_int() == s2.value_int(); + case DimensionsValue::ValueCase::kValueLong: + return s1.value_long() == s2.value_long(); + case DimensionsValue::ValueCase::kValueBool: + return s1.value_bool() == s2.value_bool(); + case DimensionsValue::ValueCase::kValueFloat: + return s1.value_float() == s2.value_float(); + case DimensionsValue::ValueCase::kValueTuple: { + if (s1.value_tuple().dimensions_value_size() != + s2.value_tuple().dimensions_value_size()) { + return false; + } + bool allMatched = true; + for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) { + allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i), + s2.value_tuple().dimensions_value(i)); + } + return allMatched; + } + case DimensionsValue::ValueCase::VALUE_NOT_SET: + default: + return true; + } +} + +bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) { + if (s1.field() != s2.field()) { + return s1.field() < s2.field(); + } + if (s1.value_case() != s2.value_case()) { + return s1.value_case() < s2.value_case(); + } + switch (s1.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + return s1.value_str() < s2.value_str(); + case DimensionsValue::ValueCase::kValueInt: + return s1.value_int() < s2.value_int(); + case DimensionsValue::ValueCase::kValueLong: + return s1.value_long() < s2.value_long(); + case DimensionsValue::ValueCase::kValueBool: + return (int)s1.value_bool() < (int)s2.value_bool(); + case DimensionsValue::ValueCase::kValueFloat: + return s1.value_float() < s2.value_float(); + case DimensionsValue::ValueCase::kValueTuple: { + if (s1.value_tuple().dimensions_value_size() != + s2.value_tuple().dimensions_value_size()) { + return s1.value_tuple().dimensions_value_size() < + s2.value_tuple().dimensions_value_size(); + } + for (int i = 0; i < s1.value_tuple().dimensions_value_size(); ++i) { + if (EqualsTo(s1.value_tuple().dimensions_value(i), + s2.value_tuple().dimensions_value(i))) { + continue; + } else { + return LessThan(s1.value_tuple().dimensions_value(i), + s2.value_tuple().dimensions_value(i)); + } + } + return false; + } + case DimensionsValue::ValueCase::VALUE_NOT_SET: + default: + return false; + } +} + +bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2) { + if (LessThan(s1.dimInWhat, s2.dimInWhat)) { + return true; + } else if (LessThan(s2.dimInWhat, s1.dimInWhat)) { + return false; + } + + return LessThan(s1.dimInCondition, s2.dimInCondition); +} + } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 6638893f6aeb..5d83ed7c1666 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -159,14 +159,30 @@ void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, i void ValidateAttributionUidAndTagDimension( const DimensionsValue& value, int atomId, int uid, const std::string& tag); +struct DimensionsPair { + DimensionsPair(DimensionsValue m1, DimensionsValue m2) : dimInWhat(m1), dimInCondition(m2){}; + + DimensionsValue dimInWhat; + DimensionsValue dimInCondition; +}; + +bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2); +bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2); + +struct DimensionCompare { + bool operator()(const DimensionsPair& s1, const DimensionsPair& s2) const { + return LessThan(s1, s2); + } +}; + template <typename T> void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) { - std::map<MetricDimensionKey, int> dimensionIndexMap; + std::map<DimensionsPair, int, DimensionCompare> dimensionIndexMap; for (int i = 0; i < metricData.data_size(); ++i) { - dimensionIndexMap.insert(std::make_pair( - MetricDimensionKey(HashableDimensionKey(metricData.data(i).dimensions_in_what()), - HashableDimensionKey(metricData.data(i).dimensions_in_condition())), - i)); + dimensionIndexMap.insert( + std::make_pair(DimensionsPair(metricData.data(i).dimensions_in_what(), + metricData.data(i).dimensions_in_condition()), + i)); } for (const auto& itr : dimensionIndexMap) { *sortedMetricData->add_data() = metricData.data(itr.second); diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 3eeaf65c903c..7b70f5997762 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -37,9 +37,11 @@ Landroid/app/ActivityThread;->currentActivityThread()Landroid/app/ActivityThread Landroid/app/ActivityThread;->currentApplication()Landroid/app/Application; Landroid/app/ActivityThread;->currentPackageName()Ljava/lang/String; Landroid/app/ActivityThread;->currentProcessName()Ljava/lang/String; +Landroid/app/ActivityThread;->getActivity(Landroid/os/IBinder;)Landroid/app/Activity; Landroid/app/ActivityThread;->getApplication()Landroid/app/Application; Landroid/app/ActivityThread;->getApplicationThread()Landroid/app/ActivityThread$ApplicationThread; Landroid/app/ActivityThread;->getHandler()Landroid/os/Handler; +Landroid/app/ActivityThread;->getInstrumentation()Landroid/app/Instrumentation; Landroid/app/ActivityThread;->getPackageManager()Landroid/content/pm/IPackageManager; Landroid/app/ActivityThread;->getProcessName()Ljava/lang/String; Landroid/app/ActivityThread$H;->BIND_SERVICE:I @@ -573,6 +575,7 @@ Landroid/net/TrafficStats;->getStatsService()Landroid/net/INetworkStatsService; Landroid/net/TrafficStats;->setThreadStatsTagBackup()V Landroid/net/TrafficStats;->setThreadStatsTagRestore()V Landroid/net/TrafficStats;->setThreadStatsUid(I)V +Landroid/net/WebAddress;-><init>(Ljava/lang/String;)V Landroid/net/WifiKey;-><init>(Ljava/lang/String;Ljava/lang/String;)V Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection; @@ -616,6 +619,7 @@ Landroid/net/wifi/WifiScanner;->startBackgroundScan(Landroid/net/wifi/WifiScanne Landroid/net/wifi/WifiScanner;->startScan(Landroid/net/wifi/WifiScanner$ScanSettings;Landroid/net/wifi/WifiScanner$ScanListener;Landroid/os/WorkSource;)V Landroid/net/wifi/WifiScanner;->startScan(Landroid/net/wifi/WifiScanner$ScanSettings;Landroid/net/wifi/WifiScanner$ScanListener;)V Landroid/net/wifi/WifiScanner;->stopBackgroundScan(Landroid/net/wifi/WifiScanner$ScanListener;)V +Landroid/net/wifi/WifiSsid;->NONE:Ljava/lang/String; Landroid/nfc/NfcAdapter;->addNfcUnlockHandler(Landroid/nfc/NfcAdapter$NfcUnlockHandler;[Ljava/lang/String;)Z Landroid/nfc/NfcAdapter;->disable()Z Landroid/nfc/NfcAdapter;->enable()Z @@ -875,6 +879,7 @@ Landroid/service/trust/TrustAgentService;-><init>()V Landroid/service/voice/VoiceInteractionService;->isKeyphraseAndLocaleSupportedForHotword(Ljava/lang/String;Ljava/util/Locale;)Z Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String; +Landroid/system/StructTimeval;->fromMillis(J)Landroid/system/StructTimeval; Landroid/telecom/AudioState;->getRoute()I Landroid/telecom/AudioState;->getSupportedRouteMask()I Landroid/telecom/AudioState;->isMuted()Z @@ -969,6 +974,7 @@ Landroid/text/SpannableStringInternal;->sendSpanRemoved(Ljava/lang/Object;II)V Landroid/text/SpannableStringInternal;->setSpan(Ljava/lang/Object;III)V Landroid/text/SpannableStringInternal;->setSpan(Ljava/lang/Object;IIIZ)V Landroid/text/SpannableStringInternal;->START:I +Landroid/text/StaticLayout;-><init>(Ljava/lang/CharSequence;IILandroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZLandroid/text/TextUtils$TruncateAt;II)V Landroid/text/StaticLayout;->mColumns:I Landroid/text/StaticLayout;->mLineCount:I Landroid/text/StaticLayout;->mLines:[I @@ -1017,6 +1023,7 @@ Landroid/view/LayoutInflater;->mFactory2:Landroid/view/LayoutInflater$Factory2; Landroid/view/LayoutInflater;->mFactory:Landroid/view/LayoutInflater$Factory; Landroid/view/LayoutInflater;->mFactorySet:Z Landroid/view/LayoutInflater;->sConstructorMap:Ljava/util/HashMap; +Landroid/view/LayoutInflater;->setPrivateFactory(Landroid/view/LayoutInflater$Factory2;)V Landroid/view/MotionEvent;->HISTORY_CURRENT:I Landroid/view/MotionEvent;->mNativePtr:J Landroid/view/MotionEvent;->nativeGetRawAxisValue(JIII)F @@ -1089,6 +1096,7 @@ Landroid/view/View;->mScrollCache:Landroid/view/View$ScrollabilityCache; Landroid/view/View;->mScrollX:I Landroid/view/View;->mScrollY:I Landroid/view/View;->mStartActivityRequestWho:Ljava/lang/String; +Landroid/view/View;->mTag:Ljava/lang/Object; Landroid/view/View;->mTop:I Landroid/view/View;->mUnscaledDrawingCache:Landroid/graphics/Bitmap; Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V @@ -1127,20 +1135,72 @@ Landroid/view/WindowManager$LayoutParams;->userActivityTimeout:J Landroid/view/Window;->mAppName:Ljava/lang/String; Landroid/view/Window;->mAppToken:Landroid/os/IBinder; Landroid/view/Window;->mHardwareAccelerated:Z +Landroid/webkit/FindActionModeCallback;->findAll()V +Landroid/webkit/FindActionModeCallback;-><init>(Landroid/content/Context;)V +Landroid/webkit/FindActionModeCallback;->setText(Ljava/lang/String;)V +Landroid/webkit/FindActionModeCallback;->setWebView(Landroid/webkit/WebView;)V +Landroid/webkit/FindActionModeCallback;->showSoftInput()V +Landroid/webkit/GeolocationPermissions;-><init>()V +Landroid/webkit/HttpAuthHandler;-><init>()V +Landroid/webkit/JsDialogHelper;-><init>(Landroid/webkit/JsPromptResult;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V +Landroid/webkit/JsDialogHelper;->showDialog(Landroid/content/Context;)V +Landroid/webkit/JsPromptResult;->getStringResult()Ljava/lang/String; +Landroid/webkit/JsPromptResult;-><init>(Landroid/webkit/JsResult$ResultReceiver;)V +Landroid/webkit/SslErrorHandler;-><init>()V +Landroid/webkit/TokenBindingService;-><init>()V +Landroid/webkit/TokenBindingService$TokenBindingKey;-><init>()V +Landroid/webkit/WebChromeClient;->openFileChooser(Landroid/webkit/ValueCallback;Ljava/lang/String;Ljava/lang/String;)V +Landroid/webkit/WebMessagePort;-><init>()V +Landroid/webkit/WebResourceError;-><init>()V +Landroid/webkit/WebResourceResponse;-><init>(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/util/Map;Ljava/io/InputStream;)V +Landroid/webkit/WebSettings;->getAcceptThirdPartyCookies()Z +Landroid/webkit/WebSettings;->setAcceptThirdPartyCookies(Z)V Landroid/webkit/WebSettings;->setNavDump(Z)V Landroid/webkit/WebSettings;->setPluginsEnabled(Z)V +Landroid/webkit/WebStorage;-><init>()V +Landroid/webkit/WebStorage$Origin;-><init>(Ljava/lang/String;JJ)V Landroid/webkit/WebView;->debugDump()V +Landroid/webkit/WebViewDelegate;->addWebViewAssetPath(Landroid/content/Context;)V +Landroid/webkit/WebViewDelegate;->callDrawGlFunction(Landroid/graphics/Canvas;JLjava/lang/Runnable;)V +Landroid/webkit/WebViewDelegate;->callDrawGlFunction(Landroid/graphics/Canvas;J)V +Landroid/webkit/WebViewDelegate;->canInvokeDrawGlFunctor(Landroid/view/View;)Z +Landroid/webkit/WebViewDelegate;->detachDrawGlFunctor(Landroid/view/View;J)V +Landroid/webkit/WebViewDelegate;->getApplication()Landroid/app/Application; +Landroid/webkit/WebViewDelegate;->getDataDirectorySuffix()Ljava/lang/String; +Landroid/webkit/WebViewDelegate;->getErrorString(Landroid/content/Context;I)Ljava/lang/String; +Landroid/webkit/WebViewDelegate;->getPackageId(Landroid/content/res/Resources;Ljava/lang/String;)I +Landroid/webkit/WebViewDelegate;->invokeDrawGlFunctor(Landroid/view/View;JZ)V +Landroid/webkit/WebViewDelegate;->isMultiProcessEnabled()Z +Landroid/webkit/WebViewDelegate;->isTraceTagEnabled()Z +Landroid/webkit/WebViewDelegate;->setOnTraceEnabledChangeListener(Landroid/webkit/WebViewDelegate$OnTraceEnabledChangeListener;)V Landroid/webkit/WebView;->disablePlatformNotifications()V Landroid/webkit/WebView;->emulateShiftHeld()V Landroid/webkit/WebView;->enablePlatformNotifications()V Landroid/webkit/WebViewFactory;->getLoadedPackageInfo()Landroid/content/pm/PackageInfo; Landroid/webkit/WebViewFactory;->getProvider()Landroid/webkit/WebViewFactoryProvider; +Landroid/webkit/WebViewFactory;->loadWebViewNativeLibraryFromPackage(Ljava/lang/String;Ljava/lang/ClassLoader;)I Landroid/webkit/WebViewFactory;->sPackageInfo:Landroid/content/pm/PackageInfo; Landroid/webkit/WebView;->getVisibleTitleHeight()I Landroid/webkit/WebView;->getWebViewProvider()Landroid/webkit/WebViewProvider; +Landroid/webkit/WebView$HitTestResult;-><init>()V +Landroid/webkit/WebView$HitTestResult;->setExtra(Ljava/lang/String;)V +Landroid/webkit/WebView$HitTestResult;->setType(I)V Landroid/webkit/WebView;->isPaused()Z Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider; Landroid/webkit/WebView;->notifyFindDialogDismissed()V +Landroid/webkit/WebView$PrivateAccess;->overScrollBy(IIIIIIIIZ)V +Landroid/webkit/WebView$PrivateAccess;->setMeasuredDimension(II)V +Landroid/webkit/WebView$PrivateAccess;->super_dispatchKeyEvent(Landroid/view/KeyEvent;)Z +Landroid/webkit/WebView$PrivateAccess;->super_getScrollBarStyle()I +Landroid/webkit/WebView$PrivateAccess;->super_onDrawVerticalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V +Landroid/webkit/WebView$PrivateAccess;->super_onGenericMotionEvent(Landroid/view/MotionEvent;)Z +Landroid/webkit/WebView$PrivateAccess;->super_performAccessibilityAction(ILandroid/os/Bundle;)Z +Landroid/webkit/WebView$PrivateAccess;->super_performLongClick()Z +Landroid/webkit/WebView$PrivateAccess;->super_requestFocus(ILandroid/graphics/Rect;)Z +Landroid/webkit/WebView$PrivateAccess;->super_scrollTo(II)V +Landroid/webkit/WebView$PrivateAccess;->super_setFrame(IIII)Z +Landroid/webkit/WebView$PrivateAccess;->super_setLayoutParams(Landroid/view/ViewGroup$LayoutParams;)V +Landroid/webkit/WebView$PrivateAccess;->super_startActivityForResult(Landroid/content/Intent;I)V Landroid/webkit/WebView;->restorePicture(Landroid/os/Bundle;Ljava/io/File;)Z Landroid/webkit/WebView;->savePicture(Landroid/os/Bundle;Ljava/io/File;)Z Landroid/webkit/WebView;->sEnforceThreadChecking:Z @@ -1395,6 +1455,7 @@ Ldalvik/system/CloseGuard;->close()V Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard; Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V Ldalvik/system/CloseGuard;->warnIfOpen()V +Ldalvik/system/DexFile;->loadClassBinaryName(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class; Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object; Ldalvik/system/DexFile;->mFileName:Ljava/lang/String; Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object; @@ -1414,6 +1475,7 @@ Ldalvik/system/VMRuntime;->is64Bit()Z Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object; Ldalvik/system/VMRuntime;->registerNativeAllocation(I)V Ldalvik/system/VMRuntime;->registerNativeFree(I)V +Ldalvik/system/VMRuntime;->runFinalization(J)V Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z @@ -1434,13 +1496,39 @@ Ljava/lang/Daemons$Daemon;->thread:Ljava/lang/Thread; Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object; Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon; Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon; +Ljava/lang/Daemons;->requestHeapTrim()V +Ljava/lang/Daemons;->start()V +Ljava/lang/Daemons;->stop()V +Ljava/lang/ref/FinalizerReference;->add(Ljava/lang/Object;)V +Ljava/lang/reflect/Executable;->artMethod:J +Ljava/lang/reflect/Parameter;-><init>(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V +Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String; +Ljava/lang/String;-><init>(II[C)V +Ljava/lang/Thread;->daemon:Z +Ljava/lang/Thread;->dispatchUncaughtException(Ljava/lang/Throwable;)V +Ljava/lang/ThreadGroup;->add(Ljava/lang/Thread;)V +Ljava/lang/ThreadGroup;->groups:[Ljava/lang/ThreadGroup; +Ljava/lang/Thread;->group:Ljava/lang/ThreadGroup; +Ljava/lang/ThreadGroup;->mainThreadGroup:Ljava/lang/ThreadGroup; +Ljava/lang/ThreadGroup;->name:Ljava/lang/String; +Ljava/lang/ThreadGroup;->ngroups:I Ljava/lang/ThreadGroup;->parent:Ljava/lang/ThreadGroup; Ljava/lang/ThreadGroup;->systemThreadGroup:Ljava/lang/ThreadGroup; +Ljava/lang/ThreadGroup;->threadTerminated(Ljava/lang/Thread;)V Ljava/lang/Thread;->inheritableThreadLocals:Ljava/lang/ThreadLocal$ThreadLocalMap; +Ljava/lang/Thread;-><init>(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V +Ljava/lang/Thread;->lock:Ljava/lang/Object; +Ljava/lang/Thread;->name:Ljava/lang/String; +Ljava/lang/Thread;->nativePeer:J +Ljava/lang/Thread;->priority:I +Ljava/lang/Throwable;->backtrace:Ljava/lang/Object; +Ljava/lang/Throwable;->cause:Ljava/lang/Throwable; Ljava/lang/Throwable;->detailMessage:Ljava/lang/String; +Ljava/lang/Throwable;->stackTrace:[Ljava/lang/StackTraceElement; +Ljava/lang/Throwable;->suppressedExceptions:Ljava/util/List; Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator; Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl; Ljava/net/InetAddress;->clearDnsCache()V @@ -1449,9 +1537,17 @@ Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAdd Ljava/net/Socket;->impl:Ljava/net/SocketImpl; Ljava/net/URI;->host:Ljava/lang/String; Ljava/nio/Buffer;->address:J +Ljava/nio/Buffer;->capacity:I +Ljava/nio/Buffer;->limit:I +Ljava/nio/ByteBuffer;->hb:[B +Ljava/nio/ByteBuffer;->isReadOnly:Z +Ljava/nio/ByteBuffer;->offset:I Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z +Ljava/nio/DirectByteBuffer;-><init>(JI)V Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String; Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V +Ljava/util/ArrayList;->elementData:[Ljava/lang/Object; +Ljava/util/ArrayList;->size:I Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList; Ljava/util/ArrayList$SubList;->parentOffset:I Ljava/util/ArrayList$SubList;->size:I @@ -1461,6 +1557,7 @@ Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable; Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale; Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory; Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory; +Llibcore/util/ZoneInfo;->mTransitions:[J Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V Lorg/json/JSONArray;->values:Ljava/util/List; Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe; diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index ea0fd75bec90..256c47934dc5 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -471,6 +471,14 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * {@link #onStart} and returns either {@link #START_STICKY} * or {@link #START_STICKY_COMPATIBILITY}. * + * <p>If you need your application to run on platform versions prior to API + * level 5, you can use the following model to handle the older {@link #onStart} + * callback in that case. The <code>handleCommand</code> method is implemented by + * you as appropriate: + * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java + * start_compatibility} + * * <p class="caution">Note that the system calls this on your * service's main thread. A service's main thread is the same * thread where UI operations take place for Activities running in the @@ -679,10 +687,6 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * {@link #startService(Intent)} first to tell the system it should keep the service running, * and then use this method to tell it to keep it running harder.</p> * - * <p>Apps targeting API {@link android.os.Build.VERSION_CODES#P} or later must request - * the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use - * this API.</p> - * * @param id The identifier for this notification as per * {@link NotificationManager#notify(int, Notification) * NotificationManager.notify(int, Notification)}; must not be 0. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 77e118ccb1bf..b29644bcf320 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3444,9 +3444,6 @@ public class DevicePolicyManager { /** * Flag for {@link #wipeData(int)}: also erase the device's eUICC data. - * - * TODO(b/35851809): make this public. - * @hide */ public static final int WIPE_EUICC = 0x0004; diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 8f49bc177dcb..0f1c249faf83 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -24,6 +24,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.view.View; +import android.view.View.AutofillImportance; import android.view.ViewRootImpl; import android.view.ViewStructure; import android.view.ViewStructure.HtmlInfo; @@ -632,6 +633,7 @@ public class AssistStructure implements Parcelable { int mMaxEms = -1; int mMaxLength = -1; @Nullable String mTextIdEntry; + @AutofillImportance int mImportantForAutofill; // POJO used to override some autofill-related values when the node is parcelized. // Not written to parcel. @@ -733,6 +735,7 @@ public class AssistStructure implements Parcelable { mMaxEms = in.readInt(); mMaxLength = in.readInt(); mTextIdEntry = preader.readString(); + mImportantForAutofill = in.readInt(); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { mX = in.readInt(); @@ -900,6 +903,7 @@ public class AssistStructure implements Parcelable { out.writeInt(mMaxEms); out.writeInt(mMaxLength); pwriter.writeString(mTextIdEntry); + out.writeInt(mImportantForAutofill); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { out.writeInt(mX); @@ -1512,6 +1516,16 @@ public class AssistStructure implements Parcelable { public int getMaxTextLength() { return mMaxLength; } + + /** + * Gets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of + * the view associated with this node. + * + * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes. + */ + public @AutofillImportance int getImportantForAutofill() { + return mImportantForAutofill; + } } /** @@ -1844,6 +1858,11 @@ public class AssistStructure implements Parcelable { } @Override + public void setImportantForAutofill(@AutofillImportance int mode) { + mNode.mImportantForAutofill = mode; + } + + @Override public void setInputType(int inputType) { mNode.mInputType = inputType; } @@ -2144,7 +2163,8 @@ public class AssistStructure implements Parcelable { + ", options=" + Arrays.toString(node.getAutofillOptions()) + ", hints=" + Arrays.toString(node.getAutofillHints()) + ", value=" + node.getAutofillValue() - + ", sanitized=" + node.isSanitized()); + + ", sanitized=" + node.isSanitized() + + ", importantFor=" + node.getImportantForAutofill()); } final int NCHILDREN = node.getChildCount(); diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index a1ad825c3a29..ee13880c9016 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -365,9 +365,7 @@ public class JobInfo implements Parcelable { /** @hide */ public boolean isExemptedFromAppStandby() { - return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) - && !hasEarlyConstraint() - && !hasLateConstraint(); + return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic(); } /** diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java index 0a38eb9ae777..dc7925694176 100644 --- a/core/java/android/app/timezone/RulesManager.java +++ b/core/java/android/app/timezone/RulesManager.java @@ -68,6 +68,23 @@ public final class RulesManager { private static final String TAG = "timezone.RulesManager"; private static final boolean DEBUG = false; + /** + * The action of the intent that the Android system will broadcast when a time zone rules update + * operation has been successfully staged (i.e. to be applied next reboot) or unstaged. + * + * <p>See {@link #EXTRA_OPERATION_STAGED} + * + * <p>This is a protected intent that can only be sent by the system. + */ + public static final String ACTION_RULES_UPDATE_OPERATION = + "com.android.intent.action.timezone.RULES_UPDATE_OPERATION"; + + /** + * The key for a boolean extra for the {@link #ACTION_RULES_UPDATE_OPERATION} intent used to + * indicate whether the operation was a "stage" or an "unstage". + */ + public static final String EXTRA_OPERATION_STAGED = "staged"; + @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "SUCCESS", "ERROR_" }, value = { SUCCESS, diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 15f3777b99cc..a738312fe0dc 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3671,10 +3671,8 @@ public abstract class Context { * * @see #getSystemService(String) * @see android.telephony.euicc.EuiccManager - * TODO(b/35851809): Unhide this API. - * @hide */ - public static final String EUICC_SERVICE = "euicc_service"; + public static final String EUICC_SERVICE = "euicc"; /** * Use with {@link #getSystemService(String)} to retrieve a @@ -3682,10 +3680,10 @@ public abstract class Context { * * @see #getSystemService(String) * @see android.telephony.euicc.EuiccCardManager - * TODO(b/35851809): Make this a SystemApi. * @hide */ - public static final String EUICC_CARD_SERVICE = "euicc_card_service"; + @SystemApi + public static final String EUICC_CARD_SERVICE = "euicc_card"; /** * Use with {@link #getSystemService(String)} to retrieve a diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 486c86cedba3..08fccab6a5b2 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2108,8 +2108,6 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device * supports embedded subscriptions on eUICCs. - * TODO(b/35851809): Make this public. - * @hide */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc"; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 91dd7ee14d1b..cf0145123b87 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -912,7 +912,7 @@ public class ResourcesImpl { * first try to load CSL from the cache. If not found, try to get from the constant state. * Last, parse the XML and generate the CSL. */ - @NonNull + @Nullable private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme, TypedValue value, int id) { final long key = (((long) value.assetCookie) << 32) | value.data; @@ -932,15 +932,17 @@ public class ResourcesImpl { complexColor = loadComplexColorForCookie(wrapper, value, id, theme); } - complexColor.setBaseChangingConfigurations(value.changingConfigurations); + if (complexColor != null) { + complexColor.setBaseChangingConfigurations(value.changingConfigurations); - if (mPreloading) { - if (verifyPreloadConfig(complexColor.getChangingConfigurations(), - 0, value.resourceId, "color")) { - sPreloadedComplexColors.put(key, complexColor.getConstantState()); + if (mPreloading) { + if (verifyPreloadConfig(complexColor.getChangingConfigurations(), + 0, value.resourceId, "color")) { + sPreloadedComplexColors.put(key, complexColor.getConstantState()); + } + } else { + cache.put(key, theme, complexColor.getConstantState()); } - } else { - cache.put(key, theme, complexColor.getConstantState()); } return complexColor; } @@ -1044,7 +1046,8 @@ public class ResourcesImpl { * We deferred the parser creation to this function b/c we need to differentiate b/t gradient * and selector tag. * - * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content. + * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content, or + * {@code null} if the XML file is neither. */ @NonNull private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id, diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index fc7886191898..48f56847e88d 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -894,14 +894,6 @@ public class Build { /** * P. - * - * <p>Applications targeting this or a later release will get these - * new changes in behavior:</p> - * <ul> - * <li>{@link android.app.Service#startForeground Service.startForeground} requires - * that apps hold the permission - * {@link android.Manifest.permission#FOREGROUND_SERVICE}.</li> - * </ul> */ public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version. } diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index c6149bed9d6d..24c9c9177360 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -271,4 +271,22 @@ public class UpdateEngine { } } } + + /** + * Verifies that a payload associated with the given payload metadata + * {@code payloadMetadataFilename} can be safely applied to ths device. + * Returns {@code true} if the update can successfully be applied and + * returns {@code false} otherwise. + * + * @param payloadMetadataFilename the location of the metadata without the + * {@code file://} prefix. + */ + @SystemApi + public boolean verifyPayloadMetadata(String payloadMetadataFilename) { + try { + return mUpdateEngine.verifyPayloadApplicable(payloadMetadataFilename); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a183895d1f19..021c22c763af 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8563,9 +8563,8 @@ public final class Settings { * * @see android.service.euicc.EuiccService * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ + @SystemApi public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus"; /** diff --git a/core/java/android/service/euicc/EuiccProfileInfo.java b/core/java/android/service/euicc/EuiccProfileInfo.java index 8e752d1c6c1d..cb4f10455ec9 100644 --- a/core/java/android/service/euicc/EuiccProfileInfo.java +++ b/core/java/android/service/euicc/EuiccProfileInfo.java @@ -17,6 +17,7 @@ package android.service.euicc; import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.service.carrier.CarrierIdentifier; @@ -26,15 +27,15 @@ import android.text.TextUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.List; import java.util.Objects; /** * Information about an embedded profile (subscription) on an eUICC. * * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ +@SystemApi public final class EuiccProfileInfo implements Parcelable { /** Profile policy rules (bit mask) */ @@ -44,6 +45,7 @@ public final class EuiccProfileInfo implements Parcelable { POLICY_RULE_DO_NOT_DELETE, POLICY_RULE_DELETE_AFTER_DISABLING }) + /** @hide */ public @interface PolicyRule {} /** Once this profile is enabled, it cannot be disabled. */ public static final int POLICY_RULE_DO_NOT_DISABLE = 1; @@ -60,6 +62,7 @@ public final class EuiccProfileInfo implements Parcelable { PROFILE_CLASS_OPERATIONAL, PROFILE_CLASS_UNSET }) + /** @hide */ public @interface ProfileClass {} /** Testing profiles */ public static final int PROFILE_CLASS_TESTING = 0; @@ -80,6 +83,7 @@ public final class EuiccProfileInfo implements Parcelable { PROFILE_STATE_ENABLED, PROFILE_STATE_UNSET }) + /** @hide */ public @interface ProfileState {} /** Disabled profiles */ public static final int PROFILE_STATE_DISABLED = 0; @@ -92,34 +96,34 @@ public final class EuiccProfileInfo implements Parcelable { public static final int PROFILE_STATE_UNSET = -1; /** The iccid of the subscription. */ - public final String iccid; + private final String mIccid; /** An optional nickname for the subscription. */ - public final @Nullable String nickname; + private final @Nullable String mNickname; /** The service provider name for the subscription. */ - public final String serviceProviderName; + private final String mServiceProviderName; /** The profile name for the subscription. */ - public final String profileName; + private final String mProfileName; /** Profile class for the subscription. */ - @ProfileClass public final int profileClass; + @ProfileClass private final int mProfileClass; /** The profile state of the subscription. */ - @ProfileState public final int state; + @ProfileState private final int mState; /** The operator Id of the subscription. */ - public final CarrierIdentifier carrierIdentifier; + private final CarrierIdentifier mCarrierIdentifier; /** The policy rules of the subscription. */ - @PolicyRule public final int policyRules; + @PolicyRule private final int mPolicyRules; /** * Optional access rules defining which apps can manage this subscription. If unset, only the * platform can manage it. */ - public final @Nullable UiccAccessRule[] accessRules; + private final @Nullable UiccAccessRule[] mAccessRules; public static final Creator<EuiccProfileInfo> CREATOR = new Creator<EuiccProfileInfo>() { @Override @@ -144,51 +148,51 @@ public final class EuiccProfileInfo implements Parcelable { if (!TextUtils.isDigitsOnly(iccid)) { throw new IllegalArgumentException("iccid contains invalid characters: " + iccid); } - this.iccid = iccid; - this.accessRules = accessRules; - this.nickname = nickname; - - this.serviceProviderName = null; - this.profileName = null; - this.profileClass = PROFILE_CLASS_UNSET; - this.state = PROFILE_CLASS_UNSET; - this.carrierIdentifier = null; - this.policyRules = 0; + this.mIccid = iccid; + this.mAccessRules = accessRules; + this.mNickname = nickname; + + this.mServiceProviderName = null; + this.mProfileName = null; + this.mProfileClass = PROFILE_CLASS_UNSET; + this.mState = PROFILE_STATE_UNSET; + this.mCarrierIdentifier = null; + this.mPolicyRules = 0; } private EuiccProfileInfo(Parcel in) { - iccid = in.readString(); - nickname = in.readString(); - serviceProviderName = in.readString(); - profileName = in.readString(); - profileClass = in.readInt(); - state = in.readInt(); + mIccid = in.readString(); + mNickname = in.readString(); + mServiceProviderName = in.readString(); + mProfileName = in.readString(); + mProfileClass = in.readInt(); + mState = in.readInt(); byte exist = in.readByte(); if (exist == (byte) 1) { - carrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in); + mCarrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in); } else { - carrierIdentifier = null; + mCarrierIdentifier = null; } - policyRules = in.readInt(); - accessRules = in.createTypedArray(UiccAccessRule.CREATOR); + mPolicyRules = in.readInt(); + mAccessRules = in.createTypedArray(UiccAccessRule.CREATOR); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(iccid); - dest.writeString(nickname); - dest.writeString(serviceProviderName); - dest.writeString(profileName); - dest.writeInt(profileClass); - dest.writeInt(state); - if (carrierIdentifier != null) { + dest.writeString(mIccid); + dest.writeString(mNickname); + dest.writeString(mServiceProviderName); + dest.writeString(mProfileName); + dest.writeInt(mProfileClass); + dest.writeInt(mState); + if (mCarrierIdentifier != null) { dest.writeByte((byte) 1); - carrierIdentifier.writeToParcel(dest, flags); + mCarrierIdentifier.writeToParcel(dest, flags); } else { dest.writeByte((byte) 0); } - dest.writeInt(policyRules); - dest.writeTypedArray(accessRules, flags); + dest.writeInt(mPolicyRules); + dest.writeTypedArray(mAccessRules, flags); } @Override @@ -198,45 +202,50 @@ public final class EuiccProfileInfo implements Parcelable { /** The builder to build a new {@link EuiccProfileInfo} instance. */ public static final class Builder { - public String iccid; - public UiccAccessRule[] accessRules; - public String nickname; - public String serviceProviderName; - public String profileName; - @ProfileClass public int profileClass; - @ProfileState public int state; - public CarrierIdentifier carrierIdentifier; - @PolicyRule public int policyRules; - - public Builder() {} + private String mIccid; + private List<UiccAccessRule> mAccessRules; + private String mNickname; + private String mServiceProviderName; + private String mProfileName; + @ProfileClass private int mProfileClass; + @ProfileState private int mState; + private CarrierIdentifier mCarrierIdentifier; + @PolicyRule private int mPolicyRules; + + public Builder(String value) { + if (!TextUtils.isDigitsOnly(value)) { + throw new IllegalArgumentException("iccid contains invalid characters: " + value); + } + mIccid = value; + } public Builder(EuiccProfileInfo baseProfile) { - iccid = baseProfile.iccid; - nickname = baseProfile.nickname; - serviceProviderName = baseProfile.serviceProviderName; - profileName = baseProfile.profileName; - profileClass = baseProfile.profileClass; - state = baseProfile.state; - carrierIdentifier = baseProfile.carrierIdentifier; - policyRules = baseProfile.policyRules; - accessRules = baseProfile.accessRules; + mIccid = baseProfile.mIccid; + mNickname = baseProfile.mNickname; + mServiceProviderName = baseProfile.mServiceProviderName; + mProfileName = baseProfile.mProfileName; + mProfileClass = baseProfile.mProfileClass; + mState = baseProfile.mState; + mCarrierIdentifier = baseProfile.mCarrierIdentifier; + mPolicyRules = baseProfile.mPolicyRules; + mAccessRules = Arrays.asList(baseProfile.mAccessRules); } /** Builds the profile instance. */ public EuiccProfileInfo build() { - if (iccid == null) { + if (mIccid == null) { throw new IllegalStateException("ICCID must be set for a profile."); } return new EuiccProfileInfo( - iccid, - nickname, - serviceProviderName, - profileName, - profileClass, - state, - carrierIdentifier, - policyRules, - accessRules); + mIccid, + mNickname, + mServiceProviderName, + mProfileName, + mProfileClass, + mState, + mCarrierIdentifier, + mPolicyRules, + mAccessRules); } /** Sets the iccId of the subscription. */ @@ -244,55 +253,55 @@ public final class EuiccProfileInfo implements Parcelable { if (!TextUtils.isDigitsOnly(value)) { throw new IllegalArgumentException("iccid contains invalid characters: " + value); } - iccid = value; + mIccid = value; return this; } /** Sets the nickname of the subscription. */ public Builder setNickname(String value) { - nickname = value; + mNickname = value; return this; } /** Sets the service provider name of the subscription. */ public Builder setServiceProviderName(String value) { - serviceProviderName = value; + mServiceProviderName = value; return this; } /** Sets the profile name of the subscription. */ public Builder setProfileName(String value) { - profileName = value; + mProfileName = value; return this; } /** Sets the profile class of the subscription. */ public Builder setProfileClass(@ProfileClass int value) { - profileClass = value; + mProfileClass = value; return this; } /** Sets the state of the subscription. */ public Builder setState(@ProfileState int value) { - state = value; + mState = value; return this; } /** Sets the carrier identifier of the subscription. */ public Builder setCarrierIdentifier(CarrierIdentifier value) { - carrierIdentifier = value; + mCarrierIdentifier = value; return this; } /** Sets the policy rules of the subscription. */ public Builder setPolicyRules(@PolicyRule int value) { - policyRules = value; + mPolicyRules = value; return this; } /** Sets the access rules of the subscription. */ - public Builder setUiccAccessRule(@Nullable UiccAccessRule[] value) { - accessRules = value; + public Builder setUiccAccessRule(@Nullable List<UiccAccessRule> value) { + mAccessRules = value; return this; } } @@ -306,75 +315,81 @@ public final class EuiccProfileInfo implements Parcelable { @ProfileState int state, CarrierIdentifier carrierIdentifier, @PolicyRule int policyRules, - @Nullable UiccAccessRule[] accessRules) { - this.iccid = iccid; - this.nickname = nickname; - this.serviceProviderName = serviceProviderName; - this.profileName = profileName; - this.profileClass = profileClass; - this.state = state; - this.carrierIdentifier = carrierIdentifier; - this.policyRules = policyRules; - this.accessRules = accessRules; + @Nullable List<UiccAccessRule> accessRules) { + this.mIccid = iccid; + this.mNickname = nickname; + this.mServiceProviderName = serviceProviderName; + this.mProfileName = profileName; + this.mProfileClass = profileClass; + this.mState = state; + this.mCarrierIdentifier = carrierIdentifier; + this.mPolicyRules = policyRules; + if (accessRules != null && accessRules.size() > 0) { + this.mAccessRules = accessRules.toArray(new UiccAccessRule[accessRules.size()]); + } else { + this.mAccessRules = null; + } } /** Gets the ICCID string. */ public String getIccid() { - return iccid; + return mIccid; } /** Gets the access rules. */ @Nullable - public UiccAccessRule[] getUiccAccessRules() { - return accessRules; + public List<UiccAccessRule> getUiccAccessRules() { + if (mAccessRules == null) return null; + return Arrays.asList(mAccessRules); } /** Gets the nickname. */ + @Nullable public String getNickname() { - return nickname; + return mNickname; } /** Gets the service provider name. */ public String getServiceProviderName() { - return serviceProviderName; + return mServiceProviderName; } /** Gets the profile name. */ public String getProfileName() { - return profileName; + return mProfileName; } /** Gets the profile class. */ @ProfileClass public int getProfileClass() { - return profileClass; + return mProfileClass; } /** Gets the state of the subscription. */ @ProfileState public int getState() { - return state; + return mState; } /** Gets the carrier identifier. */ public CarrierIdentifier getCarrierIdentifier() { - return carrierIdentifier; + return mCarrierIdentifier; } /** Gets the policy rules. */ @PolicyRule public int getPolicyRules() { - return policyRules; + return mPolicyRules; } /** Returns whether any policy rule exists. */ public boolean hasPolicyRules() { - return policyRules != 0; + return mPolicyRules != 0; } /** Checks whether a certain policy rule exists. */ public boolean hasPolicyRule(@PolicyRule int policy) { - return (policyRules & policy) != 0; + return (mPolicyRules & policy) != 0; } @Override @@ -387,50 +402,50 @@ public final class EuiccProfileInfo implements Parcelable { } EuiccProfileInfo that = (EuiccProfileInfo) obj; - return Objects.equals(iccid, that.iccid) - && Objects.equals(nickname, that.nickname) - && Objects.equals(serviceProviderName, that.serviceProviderName) - && Objects.equals(profileName, that.profileName) - && profileClass == that.profileClass - && state == that.state - && Objects.equals(carrierIdentifier, that.carrierIdentifier) - && policyRules == that.policyRules - && Arrays.equals(accessRules, that.accessRules); + return Objects.equals(mIccid, that.mIccid) + && Objects.equals(mNickname, that.mNickname) + && Objects.equals(mServiceProviderName, that.mServiceProviderName) + && Objects.equals(mProfileName, that.mProfileName) + && mProfileClass == that.mProfileClass + && mState == that.mState + && Objects.equals(mCarrierIdentifier, that.mCarrierIdentifier) + && mPolicyRules == that.mPolicyRules + && Arrays.equals(mAccessRules, that.mAccessRules); } @Override public int hashCode() { int result = 1; - result = 31 * result + Objects.hashCode(iccid); - result = 31 * result + Objects.hashCode(nickname); - result = 31 * result + Objects.hashCode(serviceProviderName); - result = 31 * result + Objects.hashCode(profileName); - result = 31 * result + profileClass; - result = 31 * result + state; - result = 31 * result + Objects.hashCode(carrierIdentifier); - result = 31 * result + policyRules; - result = 31 * result + Arrays.hashCode(accessRules); + result = 31 * result + Objects.hashCode(mIccid); + result = 31 * result + Objects.hashCode(mNickname); + result = 31 * result + Objects.hashCode(mServiceProviderName); + result = 31 * result + Objects.hashCode(mProfileName); + result = 31 * result + mProfileClass; + result = 31 * result + mState; + result = 31 * result + Objects.hashCode(mCarrierIdentifier); + result = 31 * result + mPolicyRules; + result = 31 * result + Arrays.hashCode(mAccessRules); return result; } @Override public String toString() { return "EuiccProfileInfo (nickname=" - + nickname + + mNickname + ", serviceProviderName=" - + serviceProviderName + + mServiceProviderName + ", profileName=" - + profileName + + mProfileName + ", profileClass=" - + profileClass + + mProfileClass + ", state=" - + state + + mState + ", CarrierIdentifier=" - + carrierIdentifier.toString() + + mCarrierIdentifier.toString() + ", policyRules=" - + policyRules + + mPolicyRules + ", accessRules=" - + Arrays.toString(accessRules) + + Arrays.toString(mAccessRules) + ")"; } } diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java index be8580074f73..b87faef5bb44 100644 --- a/core/java/android/service/euicc/EuiccService.java +++ b/core/java/android/service/euicc/EuiccService.java @@ -17,6 +17,7 @@ package android.service.euicc; import android.annotation.CallSuper; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; import android.os.IBinder; @@ -41,8 +42,11 @@ import java.util.concurrent.atomic.AtomicInteger; * <p>To implement the LPA backend, you must extend this class and declare this service in your * manifest file. The service must require the * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission and include an intent filter - * with the {@link #EUICC_SERVICE_INTERFACE} action. The priority of the intent filter must be set - * to a non-zero value in case multiple implementations are present on the device. For example: + * with the {@link #EUICC_SERVICE_INTERFACE} action. It's suggested that the priority of the intent + * filter to be set to a non-zero value in case multiple implementations are present on the device. + * See the below example. Note that there will be problem if two LPAs are present and they have the + * same priority. + * Example: * * <pre>{@code * <service android:name=".MyEuiccService" @@ -65,9 +69,9 @@ import java.util.concurrent.atomic.AtomicInteger; * filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero * priority. * - * TODO(b/35851809): Make this a SystemApi. * @hide */ +@SystemApi public abstract class EuiccService extends Service { /** Action which must be included in this service's intent filter. */ public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService"; @@ -77,7 +81,10 @@ public abstract class EuiccService extends Service { // LUI actions. These are passthroughs of the corresponding EuiccManager actions. - /** @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS */ + /** + * @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS + * The difference is this one is used by system to bring up the LUI. + */ public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS"; /** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */ @@ -88,7 +95,10 @@ public abstract class EuiccService extends Service { // require user interaction. // TODO(b/33075886): Define extras for any input parameters to these dialogs once they are // more scoped out. - /** Alert the user that this action will result in an active SIM being deactivated. */ + /** + * Alert the user that this action will result in an active SIM being deactivated. + * To implement the LUI triggered by the system, you need to define this in AndroidManifest.xml. + */ public static final String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM"; /** @@ -102,7 +112,11 @@ public abstract class EuiccService extends Service { public static final String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE"; - /** Intent extra set for resolution requests containing the package name of the calling app. */ + /** + * Intent extra set for resolution requests containing the package name of the calling app. + * This is used by the above actions including ACTION_RESOLVE_DEACTIVATE_SIM, + * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_CONFIRMATION_CODE. + */ public static final String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE"; @@ -136,10 +150,18 @@ public abstract class EuiccService extends Service { RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE); } - /** Boolean extra for resolution actions indicating whether the user granted consent. */ - public static final String RESOLUTION_EXTRA_CONSENT = "consent"; - /** String extra for resolution actions indicating the carrier confirmation code. */ - public static final String RESOLUTION_EXTRA_CONFIRMATION_CODE = "confirmation_code"; + /** + * Boolean extra for resolution actions indicating whether the user granted consent. + * This is used and set by the implementation and used in {@code EuiccOperation}. + */ + public static final String EXTRA_RESOLUTION_CONSENT = + "android.service.euicc.extra.RESOLUTION_CONSENT"; + /** + * String extra for resolution actions indicating the carrier confirmation code. + * This is used and set by the implementation and used in {@code EuiccOperation}. + */ + public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE = + "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE"; private final IEuiccService.Stub mStubWrapper; @@ -199,9 +221,9 @@ public abstract class EuiccService extends Service { * * @see IEuiccService#startOtaIfNecessary */ - public interface OtaStatusChangedCallback { + public abstract static class OtaStatusChangedCallback { /** Called when OTA status is changed. */ - void onOtaStatusChanged(int status); + public abstract void onOtaStatusChanged(int status); } /** @@ -238,8 +260,7 @@ public abstract class EuiccService extends Service { /** * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param subscription A subscription whose metadata needs to be populated. * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)} @@ -267,8 +288,7 @@ public abstract class EuiccService extends Service { /** * Download the given subscription. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param subscription The subscription to download. * @param switchAfterDownload If true, the subscription should be enabled upon successful * download. @@ -286,8 +306,7 @@ public abstract class EuiccService extends Service { /** * Return a list of all @link EuiccProfileInfo}s. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @return The result of the operation. * @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList * @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList @@ -297,8 +316,7 @@ public abstract class EuiccService extends Service { /** * Return info about the eUICC chip/device. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @return the {@link EuiccInfo} for the eUICC chip/device. * @see android.telephony.euicc.EuiccManager#getEuiccInfo */ @@ -310,8 +328,7 @@ public abstract class EuiccService extends Service { * <p>If the subscription is currently active, it should be deactivated first (equivalent to a * physical SIM being ejected). * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param iccid the ICCID of the subscription to delete. * @return the result of the delete operation. May be one of the predefined {@code RESULT_} * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. @@ -322,8 +339,7 @@ public abstract class EuiccService extends Service { /** * Switch to the given subscription. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param iccid the ICCID of the subscription to enable. May be null, in which case the current * profile should be deactivated and no profile should be activated to replace it - this is * equivalent to a physical SIM being ejected. @@ -340,8 +356,7 @@ public abstract class EuiccService extends Service { /** * Update the nickname of the given subscription. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param iccid the ICCID of the subscription to update. * @param nickname the new nickname to apply. * @return the result of the update operation. May be one of the predefined {@code RESULT_} diff --git a/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java b/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java index 5a24492007f3..e2171ae6bc76 100644 --- a/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java +++ b/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java @@ -16,16 +16,19 @@ package android.service.euicc; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.euicc.DownloadableSubscription; +import java.util.Arrays; +import java.util.List; + /** * Result of a {@link EuiccService#onGetDefaultDownloadableSubscriptionList} operation. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ +@SystemApi public final class GetDefaultDownloadableSubscriptionListResult implements Parcelable { public static final Creator<GetDefaultDownloadableSubscriptionListResult> CREATOR = @@ -42,20 +45,35 @@ public final class GetDefaultDownloadableSubscriptionListResult implements Parce }; /** - * Result of the operation. + * @hide + * @deprecated - Do no use. Use getResult() instead. + */ + @Deprecated + public final int result; + + @Nullable + private final DownloadableSubscription[] mSubscriptions; + + /** + * Gets the result of the operation. * * <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any * implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}. */ - public final int result; + public int getResult() { + return result; + } /** - * The available {@link DownloadableSubscription}s (with filled-in metadata). + * Gets the available {@link DownloadableSubscription}s (with filled-in metadata). * * <p>Only non-null if {@link #result} is {@link EuiccService#RESULT_OK}. */ @Nullable - public final DownloadableSubscription[] subscriptions; + public List<DownloadableSubscription> getDownloadableSubscriptions() { + if (mSubscriptions == null) return null; + return Arrays.asList(mSubscriptions); + } /** * Construct a new {@link GetDefaultDownloadableSubscriptionListResult}. @@ -70,25 +88,25 @@ public final class GetDefaultDownloadableSubscriptionListResult implements Parce @Nullable DownloadableSubscription[] subscriptions) { this.result = result; if (this.result == EuiccService.RESULT_OK) { - this.subscriptions = subscriptions; + this.mSubscriptions = subscriptions; } else { if (subscriptions != null) { throw new IllegalArgumentException( "Error result with non-null subscriptions: " + result); } - this.subscriptions = null; + this.mSubscriptions = null; } } private GetDefaultDownloadableSubscriptionListResult(Parcel in) { this.result = in.readInt(); - this.subscriptions = in.createTypedArray(DownloadableSubscription.CREATOR); + this.mSubscriptions = in.createTypedArray(DownloadableSubscription.CREATOR); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(result); - dest.writeTypedArray(subscriptions, flags); + dest.writeTypedArray(mSubscriptions, flags); } @Override diff --git a/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java b/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java index de8a30706945..1edb5398add4 100644 --- a/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java +++ b/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java @@ -16,6 +16,7 @@ package android.service.euicc; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.euicc.DownloadableSubscription; @@ -23,9 +24,8 @@ import android.telephony.euicc.DownloadableSubscription; /** * Result of a {@link EuiccService#onGetDownloadableSubscriptionMetadata} operation. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ +@SystemApi public final class GetDownloadableSubscriptionMetadataResult implements Parcelable { public static final Creator<GetDownloadableSubscriptionMetadataResult> CREATOR = @@ -42,20 +42,34 @@ public final class GetDownloadableSubscriptionMetadataResult implements Parcelab }; /** - * Result of the operation. + * @hide + * @deprecated - Do no use. Use getResult() instead. + */ + @Deprecated + public final int result; + + @Nullable + private final DownloadableSubscription mSubscription; + + /** + * Gets the result of the operation. * * <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any * implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}. */ - public final int result; + public int getResult() { + return result; + } /** - * The {@link DownloadableSubscription} with filled-in metadata. + * Gets the {@link DownloadableSubscription} with filled-in metadata. * * <p>Only non-null if {@link #result} is {@link EuiccService#RESULT_OK}. */ @Nullable - public final DownloadableSubscription subscription; + public DownloadableSubscription getDownloadableSubscription() { + return mSubscription; + } /** * Construct a new {@link GetDownloadableSubscriptionMetadataResult}. @@ -70,25 +84,25 @@ public final class GetDownloadableSubscriptionMetadataResult implements Parcelab @Nullable DownloadableSubscription subscription) { this.result = result; if (this.result == EuiccService.RESULT_OK) { - this.subscription = subscription; + this.mSubscription = subscription; } else { if (subscription != null) { throw new IllegalArgumentException( "Error result with non-null subscription: " + result); } - this.subscription = null; + this.mSubscription = null; } } private GetDownloadableSubscriptionMetadataResult(Parcel in) { this.result = in.readInt(); - this.subscription = in.readTypedObject(DownloadableSubscription.CREATOR); + this.mSubscription = in.readTypedObject(DownloadableSubscription.CREATOR); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(result); - dest.writeTypedObject(this.subscription, flags); + dest.writeTypedObject(this.mSubscription, flags); } @Override diff --git a/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java b/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java index 7ad84888dc82..464d136e70e5 100644 --- a/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java +++ b/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java @@ -16,15 +16,18 @@ package android.service.euicc; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.util.Arrays; +import java.util.List; + /** * Result of a {@link EuiccService#onGetEuiccProfileInfoList} operation. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ +@SystemApi public final class GetEuiccProfileInfoListResult implements Parcelable { public static final Creator<GetEuiccProfileInfoListResult> CREATOR = @@ -41,19 +44,38 @@ public final class GetEuiccProfileInfoListResult implements Parcelable { }; /** - * Result of the operation. + * @hide + * @deprecated - Do no use. Use getResult() instead. + */ + @Deprecated + public final int result; + + @Nullable + private final EuiccProfileInfo[] mProfiles; + + private final boolean mIsRemovable; + + /** + * Gets the result of the operation. * * <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any * implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}. */ - public final int result; + public int getResult() { + return result; + } - /** The profile list (only upon success). */ + /** Gets the profile list (only upon success). */ @Nullable - public final EuiccProfileInfo[] profiles; + public List<EuiccProfileInfo> getProfiles() { + if (mProfiles == null) return null; + return Arrays.asList(mProfiles); + } - /** Whether the eUICC is removable. */ - public final boolean isRemovable; + /** Gets whether the eUICC is removable. */ + public boolean getIsRemovable() { + return mIsRemovable; + } /** * Construct a new {@link GetEuiccProfileInfoListResult}. @@ -71,30 +93,29 @@ public final class GetEuiccProfileInfoListResult implements Parcelable { public GetEuiccProfileInfoListResult( int result, @Nullable EuiccProfileInfo[] profiles, boolean isRemovable) { this.result = result; - this.isRemovable = isRemovable; + this.mIsRemovable = isRemovable; if (this.result == EuiccService.RESULT_OK) { - this.profiles = profiles; + this.mProfiles = profiles; } else { if (profiles != null) { throw new IllegalArgumentException( "Error result with non-null profiles: " + result); } - this.profiles = null; + this.mProfiles = null; } - } private GetEuiccProfileInfoListResult(Parcel in) { this.result = in.readInt(); - this.profiles = in.createTypedArray(EuiccProfileInfo.CREATOR); - this.isRemovable = in.readBoolean(); + this.mProfiles = in.createTypedArray(EuiccProfileInfo.CREATOR); + this.mIsRemovable = in.readBoolean(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(result); - dest.writeTypedArray(profiles, flags); - dest.writeBoolean(isRemovable); + dest.writeTypedArray(mProfiles, flags); + dest.writeBoolean(mIsRemovable); } @Override diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5de25bae6a3e..cf26b2bce04a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7871,6 +7871,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, structure.setAutofillHints(getAutofillHints()); structure.setAutofillValue(getAutofillValue()); } + structure.setImportantForAutofill(getImportantForAutofill()); } int ignoredParentLeft = 0; diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index 1d94abeb51a2..3f7ab2aed4af 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -23,6 +23,8 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.LocaleList; import android.util.Pair; +import android.view.View.AutofillImportance; +import android.view.ViewStructure.HtmlInfo; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; @@ -347,6 +349,12 @@ public abstract class ViewStructure { public abstract void setAutofillOptions(CharSequence[] options); /** + * Sets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of the + * view associated with this node. + */ + public void setImportantForAutofill(@AutofillImportance int mode) {} + + /** * Sets the {@link android.text.InputType} bits of this node. * * @param inputType inputType bits as defined by {@link android.text.InputType}. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1cf75a0f9229..6f3c25fa8194 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -573,6 +573,10 @@ <protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" /> <protected-broadcast android:name="com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" /> + <!-- Time zone rules update intents fired by the system server --> + <protected-broadcast android:name="com.android.intent.action.timezone.RULES_UPDATE_OPERATION" /> + <protected-broadcast android:name="com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK" /> + <!-- Made protected in P (was introduced in JB-MR2) --> <protected-broadcast android:name="android.intent.action.GET_RESTRICTION_ENTRIES" /> <protected-broadcast android:name="android.telephony.euicc.action.OTA_STATUS_CHANGED" /> @@ -1823,19 +1827,20 @@ <permission android:name="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" android:protectionLevel="signature" /> - <!-- Allows an application to manage embedded subscriptions (those on a eUICC) through - EuiccManager APIs. + <!-- @SystemApi Allows an application to manage embedded subscriptions (those on a eUICC) + through EuiccManager APIs. <p>Protection level: signature|privileged|development - TODO(b/35851809): Mark this as a SystemApi and remove com. prefix. - @hide --> - <permission android:name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" + @hide + --> + <permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" android:protectionLevel="signature|privileged|development" /> - <!-- Must be required by an EuiccService to ensure that only the system can bind to it. + <!-- @SystemApi Must be required by an EuiccService to ensure that only the system can bind to + it. <p>Protection level: signature - TODO(b/35851809): Mark this as a SystemApi and remove com. prefix. - @hide --> - <permission android:name="com.android.permission.BIND_EUICC_SERVICE" + @hide + --> + <permission android:name="android.permission.BIND_EUICC_SERVICE" android:protectionLevel="signature" /> <!-- ================================== --> @@ -2966,11 +2971,16 @@ <permission android:name="android.permission.MANAGE_SCOPED_ACCESS_DIRECTORY_PERMISSIONS" android:protectionLevel="signature" /> - <!-- @SystemApi Allows an application to delete cache files. - <p>Not for use by third-party applications. --> + <!-- @SystemApi Old permission for deleting an app's cache files, no longer used, + but signals for us to quietly ignore calls instead of throwing an exception. --> <permission android:name="android.permission.DELETE_CACHE_FILES" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to delete cache files. + @hide --> + <permission android:name="android.permission.INTERNAL_DELETE_CACHE_FILES" + android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to delete packages. <p>Not for use by third-party applications. <p>Starting in {@link android.os.Build.VERSION_CODES#N}, user confirmation is requested @@ -3796,15 +3806,6 @@ <permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE" android:protectionLevel="signature|development|instant|appop" /> - <!-- Allows a regular application to use {@link android.app.Service#startForeground - Service.startForeground}. - <p>Protection level: normal - --> - <permission android:name="android.permission.FOREGROUND_SERVICE" - android:description="@string/permdesc_foregroundService" - android:label="@string/permlab_foregroundService" - android:protectionLevel="normal|instant" /> - <!-- @hide Allows system components to access all app shortcuts. --> <permission android:name="android.permission.ACCESS_SHORTCUTS" android:protectionLevel="signature" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2b7b056cdf4b..ec81df7b89e2 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -916,11 +916,6 @@ <string name="permdesc_persistentActivity" product="default">Allows the app to make parts of itself persistent in memory. This can limit memory available to other apps slowing down the phone.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_foregroundService">run foreground service</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_foregroundService">Allows the app to make use of foreground services.</string> - - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_getPackageSize">measure app storage space</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_getPackageSize">Allows the app to retrieve its code, data, and cache sizes</string> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 53c22f624e8c..7d5c60aa292b 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -51,7 +51,6 @@ <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.DELETE_CACHE_FILES" /> <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" /> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INJECT_EVENTS" /> diff --git a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java index 1e3ddf3226af..e69d1e7505d1 100644 --- a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java +++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java @@ -30,14 +30,15 @@ import android.telephony.UiccAccessRule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; + @SmallTest @RunWith(AndroidJUnit4.class) public class EuiccProfileInfoTest { @Test public void testWriteToParcel() { EuiccProfileInfo p = - new EuiccProfileInfo.Builder() - .setIccid("21430000000000006587") + new EuiccProfileInfo.Builder("21430000000000006587") .setNickname("profile nickname") .setServiceProviderName("service provider") .setProfileName("profile name") @@ -50,9 +51,7 @@ public class EuiccProfileInfoTest { "45")) .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE) .setUiccAccessRule( - new UiccAccessRule[] { - new UiccAccessRule(new byte[] {}, "package", 12345L) - }) + Arrays.asList(new UiccAccessRule(new byte[] {}, "package", 12345L))) .build(); Parcel parcel = Parcel.obtain(); @@ -68,8 +67,7 @@ public class EuiccProfileInfoTest { @Test public void testWriteToParcelNullCarrierId() { EuiccProfileInfo p = - new EuiccProfileInfo.Builder() - .setIccid("21430000000000006587") + new EuiccProfileInfo.Builder("21430000000000006587") .setNickname("profile nickname") .setServiceProviderName("service provider") .setProfileName("profile name") @@ -77,9 +75,8 @@ public class EuiccProfileInfoTest { .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED) .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE) .setUiccAccessRule( - new UiccAccessRule[] { - new UiccAccessRule(new byte[] {}, "package", 12345L) - }) + Arrays.asList(new UiccAccessRule(new byte[] {}, "package", 12345L)) + ) .build(); Parcel parcel = Parcel.obtain(); @@ -95,8 +92,7 @@ public class EuiccProfileInfoTest { @Test public void testBuilderAndGetters() { EuiccProfileInfo p = - new EuiccProfileInfo.Builder() - .setIccid("21430000000000006587") + new EuiccProfileInfo.Builder("21430000000000006587") .setNickname("profile nickname") .setProfileName("profile name") .setServiceProviderName("service provider") @@ -108,10 +104,7 @@ public class EuiccProfileInfoTest { .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED) .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL) .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE) - .setUiccAccessRule( - new UiccAccessRule[] { - new UiccAccessRule(new byte[0], null, 0) - }) + .setUiccAccessRule(Arrays.asList(new UiccAccessRule(new byte[0], null, 0))) .build(); assertEquals("21430000000000006587", p.getIccid()); @@ -130,14 +123,13 @@ public class EuiccProfileInfoTest { assertFalse(p.hasPolicyRule(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE)); assertArrayEquals( new UiccAccessRule[] {new UiccAccessRule(new byte[0], null, 0)}, - p.getUiccAccessRules()); + p.getUiccAccessRules().toArray()); } @Test public void testBuilder_BasedOnAnotherProfile() { EuiccProfileInfo p = - new EuiccProfileInfo.Builder() - .setIccid("21430000000000006587") + new EuiccProfileInfo.Builder("21430000000000006587") .setNickname("profile nickname") .setProfileName("profile name") .setServiceProviderName("service provider") @@ -150,9 +142,7 @@ public class EuiccProfileInfoTest { .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL) .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE) .setUiccAccessRule( - new UiccAccessRule[] { - new UiccAccessRule(new byte[0], null, 0) - }) + Arrays.asList(new UiccAccessRule(new byte[] {}, "package", 12345L))) .build(); EuiccProfileInfo copied = new EuiccProfileInfo.Builder(p).build(); @@ -164,8 +154,7 @@ public class EuiccProfileInfoTest { @Test public void testEqualsHashCode() { EuiccProfileInfo p = - new EuiccProfileInfo.Builder() - .setIccid("21430000000000006587") + new EuiccProfileInfo.Builder("21430000000000006587") .setNickname("profile nickname") .setProfileName("profile name") .setServiceProviderName("service provider") @@ -177,10 +166,7 @@ public class EuiccProfileInfoTest { .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED) .setProfileClass(EuiccProfileInfo.PROFILE_STATE_ENABLED) .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE) - .setUiccAccessRule( - new UiccAccessRule[] { - new UiccAccessRule(new byte[0], null, 0) - }) + .setUiccAccessRule(Arrays.asList(new UiccAccessRule(new byte[0], null, 0))) .build(); assertTrue(p.equals(p)); @@ -229,13 +215,13 @@ public class EuiccProfileInfoTest { } @Test(expected = IllegalStateException.class) - public void testBuilderBuild_NoIccid() { - new EuiccProfileInfo.Builder().build(); + public void testBuilderBuild_IllegalIccid() { + new EuiccProfileInfo.Builder("abc").build(); } @Test(expected = IllegalArgumentException.class) public void testBuilderSetOperatorMccMnc_Illegal() { - new EuiccProfileInfo.Builder() + new EuiccProfileInfo.Builder("21430000000000006587") .setCarrierIdentifier(new CarrierIdentifier(new byte[] {1, 2, 3, 4}, null, null)); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 3495b84e0ab9..9d1fdbd157d7 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -176,7 +176,7 @@ applications that come with the platform <permission name="android.permission.UPDATE_LOCK"/> <permission name="android.permission.WRITE_APN_SETTINGS"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> - <permission name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/> + <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/> <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/> <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/> </privapp-permissions> @@ -381,7 +381,7 @@ applications that come with the platform <permission name="android.permission.WRITE_DREAM_STATE"/> <permission name="android.permission.WRITE_MEDIA_STORAGE"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> - <permission name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/> + <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/> </privapp-permissions> <privapp-permissions package="com.android.tv"> diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 2bded9b3a2d8..7e4f755643bc 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -41,8 +41,7 @@ bool AnimatedImageDrawable::start() { return false; } - // This will trigger a reset. - mFinished = true; + mStarting = true; mRunning = true; return true; @@ -125,8 +124,11 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { mDidDraw = true; - bool drewDirectly = false; - if (!mSnapshot.mPic) { + const bool starting = mStarting; + mStarting = false; + + const bool drawDirectly = !mSnapshot.mPic; + if (drawDirectly) { // The image is not animating, and never was. Draw directly from // mSkAnimatedImage. SkAutoCanvasRestore acr(canvas, false); @@ -136,13 +138,14 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { std::unique_lock lock{mImageLock}; mSkAnimatedImage->draw(canvas); - drewDirectly = true; - } - - if (mRunning && mFinished) { + if (!mRunning) { + return; + } + } else if (starting) { + // The image has animated, and now is being reset. Queue up the first + // frame, but keep showing the current frame until the first is ready. auto& thread = uirenderer::AnimatedImageThread::getInstance(); mNextSnapshot = thread.reset(sk_ref_sp(this)); - mFinished = false; } bool finalFrame = false; @@ -154,7 +157,6 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { if (mSnapshot.mDuration == SkAnimatedImage::kFinished) { finalFrame = true; mRunning = false; - mFinished = true; } else { mTimeToShowNextSnapshot += mSnapshot.mDuration; if (mCurrentTime >= mTimeToShowNextSnapshot) { @@ -173,7 +175,7 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { mNextSnapshot = thread.decodeNextFrame(sk_ref_sp(this)); } - if (!drewDirectly) { + if (!drawDirectly) { // No other thread will modify mCurrentSnap so this should be safe to // use without locking. canvas->drawPicture(mSnapshot.mPic, nullptr, lazyPaint.getMaybeNull()); @@ -195,14 +197,29 @@ double AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { canvas->saveLayer(mSkAnimatedImage->getBounds(), &paint); } - if (mFinished && !mRunning) { - // Continue drawing the last frame, and return 0 to indicate no need to - // redraw. + if (!mRunning) { + // Continue drawing the current frame, and return 0 to indicate no need + // to redraw. std::unique_lock lock{mImageLock}; canvas->drawDrawable(mSkAnimatedImage.get()); return 0.0; } + if (mStarting) { + mStarting = false; + double duration = 0.0; + { + std::unique_lock lock{mImageLock}; + mSkAnimatedImage->reset(); + duration = mSkAnimatedImage->currentFrameDuration(); + } + { + std::unique_lock lock{mSwapLock}; + mLastWallTime = 0.0; + mTimeToShowNextSnapshot = duration; + } + } + bool update = false; { const double currentTime = SkTime::GetMSecs(); @@ -211,18 +228,12 @@ double AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { // the current time and avoid updating if (mLastWallTime == 0.0) { mCurrentTime = currentTime; - } else if (mRunning) { - if (mFinished) { - mCurrentTime = currentTime; - { - std::unique_lock lock{mImageLock}; - mSkAnimatedImage->reset(); - } - mTimeToShowNextSnapshot = currentTime + mSkAnimatedImage->currentFrameDuration(); - } else { - mCurrentTime += currentTime - mLastWallTime; - update = mCurrentTime >= mTimeToShowNextSnapshot; - } + // mTimeToShowNextSnapshot is already set to the duration of the + // first frame. + mTimeToShowNextSnapshot += currentTime; + } else if (mRunning && mDidDraw) { + mCurrentTime += currentTime - mLastWallTime; + update = mCurrentTime >= mTimeToShowNextSnapshot; } mLastWallTime = currentTime; } @@ -237,13 +248,20 @@ double AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { canvas->drawDrawable(mSkAnimatedImage.get()); } + mDidDraw = true; + std::unique_lock lock{mSwapLock}; if (update) { if (duration == SkAnimatedImage::kFinished) { mRunning = false; - mFinished = true; - } else { - mTimeToShowNextSnapshot += duration; + return duration; + } + + const double timeToShowCurrentSnapshot = mTimeToShowNextSnapshot; + mTimeToShowNextSnapshot += duration; + if (mCurrentTime >= mTimeToShowNextSnapshot) { + // As in onDraw, prevent speedy catch-up behavior. + mCurrentTime = timeToShowCurrentSnapshot; } } return mTimeToShowNextSnapshot; diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h index 2fd6f40b71b5..07469d288787 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.h +++ b/libs/hwui/hwui/AnimatedImageDrawable.h @@ -102,7 +102,7 @@ protected: private: sk_sp<SkAnimatedImage> mSkAnimatedImage; bool mRunning = false; - bool mFinished = false; + bool mStarting = false; // A snapshot of the current frame to draw. Snapshot mSnapshot; diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 90fcaabd12fa..279e05f57d1c 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -634,8 +634,39 @@ public final class MediaDrm implements AutoCloseable { * @throws ResourceBusyException if required resources are in use */ @NonNull - public native byte[] openSession() throws NotProvisionedException, - ResourceBusyException; + public byte[] openSession() throws NotProvisionedException, + ResourceBusyException { + return openSession(getMaxSecurityLevel()); + } + + /** + * Open a new session at a requested security level. The security level + * represents the robustness of the device's DRM implementation. By default, + * sessions are opened at the native security level of the device. + * Overriding the security level is necessary when the decrypted frames need + * to be manipulated, such as for image compositing. The security level + * parameter must be lower than the native level. Reducing the security + * level will typically limit the content to lower resolutions, as + * determined by the license policy. If the requested level is not + * supported, the next lower supported security level will be set. The level + * can be queried using {@link #getSecurityLevel}. A session + * ID is returned. + * + * @param level the new security level, one of + * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE}, + * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or + * {@link #HW_SECURE_ALL}. + * + * @throws NotProvisionedException if provisioning is needed + * @throws ResourceBusyException if required resources are in use + * @throws IllegalArgumentException if the requested security level is + * higher than the native level or lower than the lowest supported level or + * if the device does not support specifying the security level when opening + * a session + */ + @NonNull + public native byte[] openSession(@SecurityLevel int level) throws + NotProvisionedException, ResourceBusyException; /** * Close a session on the MediaDrm object that was previously opened @@ -1109,7 +1140,7 @@ public final class MediaDrm implements AutoCloseable { */ @Retention(RetentionPolicy.SOURCE) @IntDef({SECURITY_LEVEL_UNKNOWN, SW_SECURE_CRYPTO, SW_SECURE_DECODE, - HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL}) + HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL}) public @interface SecurityLevel {} /** @@ -1119,39 +1150,55 @@ public final class MediaDrm implements AutoCloseable { public static final int SECURITY_LEVEL_UNKNOWN = 0; /** - * Software-based whitebox crypto + * DRM key management uses software-based whitebox crypto. */ public static final int SW_SECURE_CRYPTO = 1; /** - * Software-based whitebox crypto and an obfuscated decoder + * DRM key management and decoding use software-based whitebox crypto. */ - public static final int SW_SECURE_DECODE = 2; + public static final int SW_SECURE_DECODE = 2; /** - * DRM key management and crypto operations are performed within a - * hardware backed trusted execution environment + * DRM key management and crypto operations are performed within a hardware + * backed trusted execution environment. */ public static final int HW_SECURE_CRYPTO = 3; /** - * DRM key management, crypto operations and decoding of content - * are performed within a hardware backed trusted execution environment + * DRM key management, crypto operations and decoding of content are + * performed within a hardware backed trusted execution environment. */ - public static final int HW_SECURE_DECODE = 4; + public static final int HW_SECURE_DECODE = 4; /** * DRM key management, crypto operations, decoding of content and all - * handling of the media (compressed and uncompressed) is handled within - * a hardware backed trusted execution environment. + * handling of the media (compressed and uncompressed) is handled within a + * hardware backed trusted execution environment. */ public static final int HW_SECURE_ALL = 5; /** - * Return the current security level of a session. A session - * has an initial security level determined by the robustness of - * the DRM system's implementation on the device. The security - * level may be adjusted using {@link #setSecurityLevel}. + * The maximum security level supported by the device. This is the default + * security level when a session is opened. + * @hide + */ + public static final int SECURITY_LEVEL_MAX = 6; + + /** + * The maximum security level supported by the device. This is the default + * security level when a session is opened. + */ + @SecurityLevel + public static final int getMaxSecurityLevel() { + return SECURITY_LEVEL_MAX; + } + + /** + * Return the current security level of a session. A session has an initial + * security level determined by the robustness of the DRM system's + * implementation on the device. The security level may be changed at the + * time a session is opened using {@link #openSession}. * @param sessionId the session to query. * <p> * @return one of {@link #SECURITY_LEVEL_UNKNOWN}, @@ -1163,21 +1210,6 @@ public final class MediaDrm implements AutoCloseable { public native int getSecurityLevel(@NonNull byte[] sessionId); /** - * Set the security level of a session. This can be useful if specific - * attributes of a lower security level are needed by an application, - * such as image manipulation or compositing. Reducing the security - * level will typically limit decryption to lower content resolutions, - * depending on the license policy. - * @param sessionId the session to set the security level on. - * @param level the new security level, one of - * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE}, - * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or - * {@link #HW_SECURE_ALL}. - */ - public native void setSecurityLevel(@NonNull byte[] sessionId, - @SecurityLevel int level); - - /** * String property name: identifies the maker of the DRM plugin */ public static final String PROPERTY_VENDOR = "vendor"; diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index 4f06caaea703..d7f51d4e6986 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -145,6 +145,7 @@ struct HDCPLevels { struct SecurityLevels { jint kSecurityLevelUnknown; + jint kSecurityLevelMax; jint kSecurityLevelSwSecureCrypto; jint kSecurityLevelSwSecureDecode; jint kSecurityLevelHwSecureCrypto; @@ -683,6 +684,10 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_ALL", "I"); gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field); + jmethodID getMaxSecurityLevel; + GET_STATIC_METHOD_ID(getMaxSecurityLevel, clazz, "getMaxSecurityLevel", "()I"); + gSecurityLevels.kSecurityLevelMax = env->CallStaticIntMethod(clazz, getMaxSecurityLevel); + FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B"); GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;"); @@ -813,7 +818,7 @@ static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative( } static jbyteArray android_media_MediaDrm_openSession( - JNIEnv *env, jobject thiz) { + JNIEnv *env, jobject thiz, jint jlevel) { sp<IDrm> drm = GetDrm(env, thiz); if (drm == NULL) { @@ -823,7 +828,26 @@ static jbyteArray android_media_MediaDrm_openSession( } Vector<uint8_t> sessionId; - status_t err = drm->openSession(sessionId); + DrmPlugin::SecurityLevel level; + + if (jlevel == gSecurityLevels.kSecurityLevelMax) { + level = DrmPlugin::kSecurityLevelMax; + } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) { + level = DrmPlugin::kSecurityLevelSwSecureCrypto; + } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) { + level = DrmPlugin::kSecurityLevelSwSecureDecode; + } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) { + level = DrmPlugin::kSecurityLevelHwSecureCrypto; + } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) { + level = DrmPlugin::kSecurityLevelHwSecureDecode; + } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) { + level = DrmPlugin::kSecurityLevelHwSecureAll; + } else { + jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level"); + return NULL; + } + + status_t err = drm->openSession(level, sessionId); if (throwExceptionAsNecessary(env, err, "Failed to open session")) { return NULL; @@ -1345,40 +1369,6 @@ static jint android_media_MediaDrm_getSecurityLevel(JNIEnv *env, } -static void android_media_MediaDrm_setSecurityLevel(JNIEnv *env, - jobject thiz, jbyteArray jsessionId, jint jlevel) { - sp<IDrm> drm = GetDrm(env, thiz); - - if (!CheckSession(env, drm, jsessionId)) { - return; - } - - Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); - DrmPlugin::SecurityLevel level; - - if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) { - level = DrmPlugin::kSecurityLevelSwSecureCrypto; - } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) { - level = DrmPlugin::kSecurityLevelSwSecureDecode; - } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) { - level = DrmPlugin::kSecurityLevelHwSecureCrypto; - } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) { - level = DrmPlugin::kSecurityLevelHwSecureDecode; - } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) { - level = DrmPlugin::kSecurityLevelHwSecureAll; - } else { - jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level"); - return; - } - - status_t err = drm->setSecurityLevel(sessionId, level); - - if (throwExceptionAsNecessary(env, err, "Failed to set security level")) { - return; - } -} - - static jstring android_media_MediaDrm_getPropertyString( JNIEnv *env, jobject thiz, jstring jname) { sp<IDrm> drm = GetDrm(env, thiz); @@ -1724,7 +1714,7 @@ static const JNINativeMethod gMethods[] = { { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;)Z", (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative }, - { "openSession", "()[B", + { "openSession", "(I)[B", (void *)android_media_MediaDrm_openSession }, { "closeSession", "([B)V", @@ -1785,9 +1775,6 @@ static const JNINativeMethod gMethods[] = { { "getSecurityLevel", "([B)I", (void *)android_media_MediaDrm_getSecurityLevel }, - { "setSecurityLevel", "([BI)V", - (void *)android_media_MediaDrm_setSecurityLevel }, - { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_MediaDrm_getPropertyString }, diff --git a/packages/CtsShim/Android.mk b/packages/CtsShim/Android.mk index 88b85e078f45..12972f14514b 100644 --- a/packages/CtsShim/Android.mk +++ b/packages/CtsShim/Android.mk @@ -32,9 +32,10 @@ LOCAL_CERTIFICATE := PRESIGNED LOCAL_DEX_PREOPT := false LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64 -my_archs := arm x86 -my_src_arch := $(call get-prebuilt-src-arch, $(my_archs)) -LOCAL_SRC_FILES := apk/$(my_src_arch)/CtsShimPriv.apk +LOCAL_SRC_FILES_arm := apk/arm/CtsShimPriv.apk +LOCAL_SRC_FILES_arm64 := apk/arm/CtsShimPriv.apk +LOCAL_SRC_FILES_x86 := apk/x86/CtsShimPriv.apk +LOCAL_SRC_FILES_x86_64 := apk/x86/CtsShimPriv.apk include $(BUILD_PREBUILT) @@ -53,9 +54,10 @@ LOCAL_CERTIFICATE := PRESIGNED LOCAL_DEX_PREOPT := false LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64 -my_archs := arm x86 -my_src_arch := $(call get-prebuilt-src-arch, $(my_archs)) -LOCAL_SRC_FILES := apk/$(my_src_arch)/CtsShim.apk +LOCAL_SRC_FILES_arm := apk/arm/CtsShim.apk +LOCAL_SRC_FILES_arm64 := apk/arm/CtsShim.apk +LOCAL_SRC_FILES_x86 := apk/x86/CtsShim.apk +LOCAL_SRC_FILES_x86_64 := apk/x86/CtsShim.apk include $(BUILD_PREBUILT) diff --git a/packages/MtpDocumentsProvider/AndroidManifest.xml b/packages/MtpDocumentsProvider/AndroidManifest.xml index c0a59b3badbf..8d79f62f21d7 100644 --- a/packages/MtpDocumentsProvider/AndroidManifest.xml +++ b/packages/MtpDocumentsProvider/AndroidManifest.xml @@ -3,7 +3,6 @@ package="com.android.mtp" android:sharedUserId="android.media"> <uses-feature android:name="android.hardware.usb.host" /> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.MANAGE_USB" /> <application android:label="@string/app_label"> <provider diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index ddb49b6c3874..7b09ef7507f8 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -83,4 +83,12 @@ <dimen name="zen_mode_condition_detail_item_interline_spacing">4dp</dimen> <!-- Zen mode panel: bottom padding, a bit less than qs_panel_padding --> <dimen name="zen_mode_condition_detail_bottom_padding">4dp</dimen> + + <!-- SignalDrawable --> + <dimen name="signal_icon_size">17dp</dimen> + <!-- How far to inset the rounded edges --> + <dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen> + + + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java index 15ef742af02e..846e30d50063 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java +++ b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java @@ -12,7 +12,7 @@ * permissions and limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.settingslib.graph; import android.animation.ArgbEvaluator; import android.annotation.IntRange; @@ -36,7 +36,6 @@ import android.util.LayoutDirection; import com.android.settingslib.R; import com.android.settingslib.Utils; -import com.android.systemui.qs.SlashDrawable; public class SignalDrawable extends Drawable { @@ -458,6 +457,7 @@ public class SignalDrawable extends Drawable { } private final class SlashArtist { + private static final float CORNER_RADIUS = 1f; // These values are derived in un-rotated (vertical) orientation private static final float SLASH_WIDTH = 1.8384776f; private static final float SLASH_HEIGHT = 22f; @@ -478,7 +478,7 @@ public class SignalDrawable extends Drawable { void draw(int height, int width, @NonNull Canvas canvas, Paint paint) { Matrix m = new Matrix(); - final float radius = scale(SlashDrawable.CORNER_RADIUS, width); + final float radius = scale(CORNER_RADIUS, width); updateRect( scale(LEFT, width), scale(TOP, height), diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 4d49899b2986..5c8d74516274 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -44,7 +44,6 @@ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> <uses-permission android:name="android.permission.MANAGE_USB" /> <uses-permission android:name="android.permission.USE_RESERVED_DISK" /> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <!-- System tool permissions granted to the shell. --> <uses-permission android:name="android.permission.REAL_GET_TASKS" /> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index cbb3e8fcab57..35c4d59752d3 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -110,7 +110,7 @@ <uses-permission android:name="com.android.alarm.permission.SET_ALARM" /> <!-- Keyguard --> - <uses-permission android:name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" /> + <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" /> <uses-permission android:name="android.permission.CONTROL_KEYGUARD" /> <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> @@ -381,6 +381,12 @@ android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true"> </activity> + <activity-alias + android:name=".UsbDebuggingActivityAlias" + android:permission="android.permission.DUMP" + android:targetActivity=".usb.UsbDebuggingActivity" + android:exported="true"> + </activity-alias> <activity android:name=".usb.UsbDebuggingSecondaryUserActivity" android:theme="@style/Theme.SystemUI.Dialog.Alert" android:finishOnCloseSystemDialogs="true" diff --git a/packages/SystemUI/res/color/accent_tint_color_selector.xml b/packages/SystemUI/res/color/accent_tint_color_selector.xml new file mode 100644 index 000000000000..85af186ab45e --- /dev/null +++ b/packages/SystemUI/res/color/accent_tint_color_selector.xml @@ -0,0 +1,21 @@ +<?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. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:color="?android:attr/colorButtonNormal"/> + + <item android:color="?android:attr/colorAccent"/> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index bab4eba72822..803659f9c7ae 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -38,59 +38,64 @@ android:layout_height="wrap_content" android:clipChildren="false" android:clipToPadding="false" - android:paddingTop="10dp" - android:paddingBottom="10dp" android:background="@drawable/rounded_bg_full" android:translationZ="@dimen/volume_panel_elevation" android:orientation="horizontal" > <!-- volume rows added and removed here! :-) --> </LinearLayout> - <LinearLayout + <FrameLayout android:id="@+id/footer" android:layout_width="@dimen/volume_dialog_panel_width" android:layout_height="@dimen/volume_dialog_panel_width" - android:clipChildren="false" - android:clipToPadding="false" android:layout_marginTop="6dp" android:layout_marginBottom="6dp" android:layout_below="@id/volume_dialog_rows" - android:background="@drawable/rounded_bg_full" - android:gravity="center" - android:layout_gravity="end" - android:translationZ="@dimen/volume_panel_elevation" - android:clickable="true" - android:orientation="vertical" > + android:background="@drawable/rounded_bg_full"> - <TextView - android:id="@+id/ringer_title" - android:text="@string/ring_toggle_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ellipsize="end" - android:maxLines="1" - android:layout_centerVertical="true" - android:textColor="?android:attr/colorControlNormal" - android:textAppearance="@style/TextAppearance.Volume.Header" /> + <LinearLayout + android:id="@+id/footer_linear_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipChildren="false" + android:clipToPadding="false" + android:gravity="center" + android:layout_gravity="end" + android:translationZ="@dimen/volume_panel_elevation" + android:clickable="true" + android:orientation="vertical" > - <com.android.keyguard.AlphaOptimizedImageButton - android:id="@+id/ringer_icon" - style="@style/VolumeButtons" - android:background="?android:selectableItemBackgroundBorderless" - android:layout_width="@dimen/volume_dialog_panel_width" - android:layout_height="@dimen/volume_button_size" - android:tint="?android:attr/colorAccent" - android:soundEffectsEnabled="false" /> + <TextView + android:id="@+id/ringer_title" + android:text="@string/ring_toggle_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:layout_centerVertical="true" + android:textColor="?android:attr/colorControlNormal" + android:textAppearance="@style/TextAppearance.Volume.Header" /> - <TextView - android:id="@+id/ringer_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ellipsize="end" - android:maxLines="1" - android:textColor="?android:attr/colorControlNormal" - android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" /> + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/ringer_icon" + style="@style/VolumeButtons" + android:background="?android:selectableItemBackgroundBorderless" + android:layout_width="@dimen/volume_dialog_panel_width" + android:layout_height="@dimen/volume_button_size" + android:tint="@color/accent_tint_color_selector" + android:soundEffectsEnabled="false" /> - </LinearLayout> + <TextView + android:id="@+id/ringer_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:textColor="?android:attr/colorControlNormal" + android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" /> + </LinearLayout> + + <include layout="@layout/volume_dnd_icon"/> + </FrameLayout> </LinearLayout> </com.android.systemui.volume.VolumeUiLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml index 70654a803b44..fb9355ad061b 100644 --- a/packages/SystemUI/res/layout/volume_dialog_row.xml +++ b/packages/SystemUI/res/layout/volume_dialog_row.xml @@ -13,89 +13,98 @@ See the License for the specific language governing permissions and limitations under the License. --> -<LinearLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:tag="row" android:layout_height="wrap_content" android:layout_width="@dimen/volume_dialog_panel_width" android:clipChildren="true" android:clipToPadding="true" - android:theme="@style/qs_theme" - android:gravity="center" - android:orientation="vertical" > + android:theme="@style/qs_theme"> <LinearLayout - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp" android:gravity="center" - android:padding="5dp"> - <TextView - android:id="@+id/volume_row_header" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ellipsize="end" - android:maxLength="10" - android:maxLines="1" - android:textColor="?android:attr/colorControlNormal" - android:textAppearance="@style/TextAppearance.Volume.Header" /> + android:orientation="vertical" > + <LinearLayout - android:id="@+id/output_chooser" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:minWidth="48dp" - android:minHeight="48dp" - android:paddingTop="10dp" - android:background="?android:selectableItemBackgroundBorderless" - android:gravity="center"> + android:gravity="center" + android:padding="5dp"> <TextView - android:id="@+id/volume_row_connected_device" - android:visibility="gone" + android:id="@+id/volume_row_header" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:maxLength="10" android:ellipsize="end" + android:maxLength="10" android:maxLines="1" - android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" /> - <com.android.keyguard.AlphaOptimizedImageButton - android:id="@+id/output_chooser_button" - android:layout_width="24dp" - android:layout_height="24dp" + android:textColor="?android:attr/colorControlNormal" + android:textAppearance="@style/TextAppearance.Volume.Header" /> + <LinearLayout + android:id="@+id/output_chooser" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minWidth="48dp" + android:minHeight="48dp" + android:paddingTop="10dp" android:background="?android:selectableItemBackgroundBorderless" - android:contentDescription="@string/accessibility_output_chooser" - style="@style/VolumeButtons" - android:clickable="false" - android:layout_centerVertical="true" - android:src="@drawable/ic_swap" - android:soundEffectsEnabled="false" /> + android:gravity="center"> + <TextView + android:id="@+id/volume_row_connected_device" + android:visibility="gone" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:maxLength="10" + android:ellipsize="end" + android:maxLines="1" + android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" /> + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/output_chooser_button" + android:layout_width="24dp" + android:layout_height="24dp" + android:background="?android:selectableItemBackgroundBorderless" + android:contentDescription="@string/accessibility_output_chooser" + style="@style/VolumeButtons" + android:clickable="false" + android:layout_centerVertical="true" + android:src="@drawable/ic_swap" + android:soundEffectsEnabled="false"/> + </LinearLayout> </LinearLayout> - </LinearLayout> - <FrameLayout - android:id="@+id/volume_row_slider_frame" - android:padding="0dp" - android:layout_width="@dimen/volume_dialog_panel_width" - android:layoutDirection="rtl" - android:layout_height="@dimen/volume_dialog_panel_width"> - <SeekBar - android:id="@+id/volume_row_slider" - android:clickable="true" + <FrameLayout + android:id="@+id/volume_row_slider_frame" android:padding="0dp" - android:layout_margin="0dp" android:layout_width="@dimen/volume_dialog_panel_width" - android:layout_height="@dimen/volume_dialog_panel_width" android:layoutDirection="rtl" - android:layout_gravity="center" - android:rotation="90" /> - </FrameLayout> + android:layout_height="@dimen/volume_dialog_panel_width"> + <SeekBar + android:id="@+id/volume_row_slider" + android:clickable="true" + android:padding="0dp" + android:layout_margin="0dp" + android:layout_width="@dimen/volume_dialog_panel_width" + android:layout_height="@dimen/volume_dialog_panel_width" + android:layoutDirection="rtl" + android:layout_gravity="center" + android:rotation="90" /> + </FrameLayout> + + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/volume_row_icon" + style="@style/VolumeButtons" + android:padding="10dp" + android:layout_width="@dimen/volume_button_size" + android:layout_height="@dimen/volume_button_size" + android:background="?android:selectableItemBackgroundBorderless" + android:soundEffectsEnabled="false" /> + </LinearLayout> - <com.android.keyguard.AlphaOptimizedImageButton - android:id="@+id/volume_row_icon" - style="@style/VolumeButtons" - android:padding="10dp" - android:layout_width="@dimen/volume_button_size" - android:layout_height="@dimen/volume_button_size" - android:background="?android:selectableItemBackgroundBorderless" - android:soundEffectsEnabled="false" /> + <include layout="@layout/volume_dnd_icon"/> -</LinearLayout>
\ No newline at end of file +</FrameLayout> diff --git a/packages/SystemUI/res/layout/volume_dnd_icon.xml b/packages/SystemUI/res/layout/volume_dnd_icon.xml new file mode 100644 index 000000000000..215b2300992a --- /dev/null +++ b/packages/SystemUI/res/layout/volume_dnd_icon.xml @@ -0,0 +1,30 @@ +<!-- + 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. +--> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="14dp" + android:layout_height="14dp" + android:layout_marginTop="6dp" + android:layout_marginRight="6dp" + android:layout_gravity="right|top"> + + <ImageView + android:id="@+id/dnd_icon" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@drawable/ic_dnd" + android:tint="?android:attr/textColorTertiary"/> +</FrameLayout> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index c351b9427e4c..3f6c85fc67ad 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -855,8 +855,6 @@ <dimen name="default_gear_space">18dp</dimen> <dimen name="cell_overlay_padding">18dp</dimen> - <dimen name="signal_icon_size">17dp</dimen> - <dimen name="hwui_edge_margin">16dp</dimen> <dimen name="global_actions_panel_width">120dp</dimen> @@ -885,11 +883,6 @@ <dimen name="nav_quick_scrub_track_edge_padding">42dp</dimen> <dimen name="nav_quick_scrub_track_thickness">2dp</dimen> - <!-- Intended corner radius when drawing the mobile signal --> - <dimen name="stat_sys_mobile_signal_corner_radius">0.75dp</dimen> - <!-- How far to inset the rounded edges --> - <dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen> - <!-- Home button padding for sizing --> <dimen name="home_padding">15dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java index 5f2609380085..e7eefe8d5e56 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java @@ -19,12 +19,12 @@ import android.graphics.drawable.Drawable; import android.service.quicksettings.Tile; import android.widget.ImageView; +import com.android.settingslib.graph.SignalDrawable; import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile.Icon; import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.qs.tileimpl.QSTileImpl; -import com.android.systemui.statusbar.phone.SignalDrawable; import java.util.Objects; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 2607ebbb72ea..7fe9e3584ee5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -131,21 +131,10 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { if (enabled) { if (connected) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connected); + state.icon = new BluetoothConnectedTileIcon(); state.contentDescription = mContext.getString( R.string.accessibility_bluetooth_name, state.label); - final CachedBluetoothDevice lastDevice = mController.getLastDevice(); - if (lastDevice != null) { - final int batteryLevel = lastDevice.getBatteryLevel(); - if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { - state.icon = new BluetoothBatteryTileIcon( - batteryLevel, - mContext.getResources().getFraction( - R.fraction.bt_battery_scale_fraction, 1, 1)); - } - } - state.label = mController.getLastDeviceName(); } else if (state.isTransient) { state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation); @@ -281,6 +270,25 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { } } + + /** + * Bluetooth icon wrapper (when connected with no battery indicator) for Quick Settings. This is + * used instead of {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to + * use a context that reflects dark/light theme attributes. + */ + private class BluetoothConnectedTileIcon extends Icon { + + BluetoothConnectedTileIcon() { + // Do nothing. Default constructor to limit visibility. + } + + @Override + public Drawable getDrawable(Context context) { + // This method returns Pair<Drawable, String> - the first value is the drawable. + return context.getDrawable(R.drawable.ic_qs_bluetooth_connected); + } + } + protected class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback { // We probably won't ever have space in the UI for more than 20 devices, so don't // get info for them. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index a7fb61a1d5ca..cb6e5a6e0bae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -38,9 +38,9 @@ import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; import android.widget.LinearLayout; +import com.android.settingslib.graph.SignalDrawable; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.SignalDrawable; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java index 677fa81a12cd..0304086dbfda 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java @@ -16,10 +16,10 @@ import android.util.Log; import android.util.TypedValue; import android.view.View; import android.widget.ImageView; +import com.android.settingslib.graph.SignalDrawable; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.ScalingDrawableWrapper; -import com.android.systemui.statusbar.phone.SignalDrawable; import com.android.systemui.statusbar.policy.BluetoothController; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 8516278a8891..f0854edeaece 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -35,8 +35,8 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.cdma.EriInfo; +import com.android.settingslib.graph.SignalDrawable; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.SignalDrawable; import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index fb5c447c2592..8881ee94092e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -68,6 +68,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; import android.view.animation.DecelerateInterpolator; import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; @@ -109,7 +110,9 @@ public class VolumeDialogImpl implements VolumeDialog { private ViewGroup mDialogRowsView; private ViewGroup mFooter; private ImageButton mRingerIcon; + private ImageView mZenIcon; private TextView mRingerStatus; + private TextView mRingerTitle; private final List<VolumeRow> mRows = new ArrayList<>(); private ConfigurableTexts mConfigurableTexts; private final SparseBooleanArray mDynamic = new SparseBooleanArray(); @@ -216,6 +219,8 @@ public class VolumeDialogImpl implements VolumeDialog { mFooter = mDialog.findViewById(R.id.footer); mRingerIcon = mFooter.findViewById(R.id.ringer_icon); mRingerStatus = mFooter.findViewById(R.id.ringer_status); + mRingerTitle = mFooter.findViewById(R.id.ringer_title); + mZenIcon = mFooter.findViewById(R.id.dnd_icon); if (mRows.isEmpty()) { addRow(AudioManager.STREAM_MUSIC, @@ -349,6 +354,7 @@ public class VolumeDialogImpl implements VolumeDialog { if (stream == STREAM_ACCESSIBILITY) { row.header.setFilters(new InputFilter[] {new InputFilter.LengthFilter(13)}); } + row.dndIcon = row.view.findViewById(R.id.dnd_icon); row.slider = row.view.findViewById(R.id.volume_row_slider); row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row)); row.anim = null; @@ -559,6 +565,8 @@ public class VolumeDialogImpl implements VolumeDialog { if (ss == null) { return; } + + enableRingerViewsH(mState.zenMode == Global.ZEN_MODE_OFF || !mState.disallowRinger); switch (mState.ringerModeInternal) { case AudioManager.RINGER_MODE_VIBRATE: mRingerStatus.setText(R.string.volume_ringer_status_vibrate); @@ -603,6 +611,28 @@ public class VolumeDialogImpl implements VolumeDialog { } } + /** + * Toggles enable state of views in a VolumeRow (not including seekbar, outputChooser or icon) + * Hides/shows zen icon + * @param enable whether to enable volume row views and hide dnd icon + */ + private void enableVolumeRowViewsH(VolumeRow row, boolean enable) { + row.header.setEnabled(enable); + row.dndIcon.setVisibility(enable ? View.GONE : View.VISIBLE); + } + + /** + * Toggles enable state of footer/ringer views + * Hides/shows zen icon + * @param enable whether to enable ringer views and hide dnd icon + */ + private void enableRingerViewsH(boolean enable) { + mRingerTitle.setEnabled(enable); + mRingerStatus.setEnabled(enable); + mRingerIcon.setEnabled(enable); + mZenIcon.setVisibility(enable ? View.GONE : View.VISIBLE); + } + private void trimObsoleteH() { if (D.BUG) Log.d(TAG, "trimObsoleteH"); for (int i = mRows.size() - 1; i >= 0; i--) { @@ -748,6 +778,7 @@ public class VolumeDialogImpl implements VolumeDialog { if (zenMuted) { row.tracking = false; } + enableVolumeRowViewsH(row, !zenMuted); // update slider final boolean enableSlider = !zenMuted; @@ -1178,5 +1209,6 @@ public class VolumeDialogImpl implements VolumeDialog { private int lastAudibleLevel = 1; private View outputChooser; private TextView connectedDevice; + private ImageView dndIcon; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index f685b1fe6889..8aab837cb7d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -43,9 +43,9 @@ import android.testing.TestableLooper; import android.util.Log; import com.android.internal.telephony.cdma.EriInfo; +import com.android.settingslib.graph.SignalDrawable; import com.android.settingslib.net.DataUsageController; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.phone.SignalDrawable; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java index 3ad107c22245..550f4a79f3c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java @@ -30,9 +30,9 @@ import android.testing.TestableLooper.RunWithLooper; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; +import com.android.settingslib.graph.SignalDrawable; import com.android.settingslib.net.DataUsageController; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.SignalDrawable; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 230f69dd43a0..14404f5d4e4f 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1043,14 +1043,20 @@ public final class ActiveServices { throw new SecurityException("Instant app " + r.appInfo.packageName + " does not have permission to create foreground services"); default: - mAm.enforcePermission( - android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE, - r.app.pid, r.appInfo.uid, "startForeground"); - } - } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) { - mAm.enforcePermission( - android.Manifest.permission.FOREGROUND_SERVICE, - r.app.pid, r.appInfo.uid, "startForeground"); + try { + if (AppGlobals.getPackageManager().checkPermission( + android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE, + r.appInfo.packageName, UserHandle.getUserId(r.appInfo.uid)) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Instant app " + r.appInfo.packageName + + " does not have permission to create foreground" + + "services"); + } + } catch (RemoteException e) { + throw new SecurityException("Failed to check instant app permission." , + e); + } + } } if (r.fgRequired) { if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index cd8b6d73a3bc..99904b5a79a2 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8889,20 +8889,6 @@ public class ActivityManagerService extends IActivityManager.Stub /** * This can be called with or without the global lock held. */ - void enforcePermission(String permission, int pid, int uid, String func) { - if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) { - return; - } - - String msg = "Permission Denial: " + func + " from pid=" + pid + ", uid=" + uid - + " requires " + permission; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - - /** - * This can be called with or without the global lock held. - */ void enforceCallerIsRecentsOrHasPermission(String permission, String func) { if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) { enforceCallingPermission(permission, func); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 47a4fb24201c..be48f69e1545 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -2337,9 +2337,9 @@ public final class JobSchedulerService extends com.android.server.SystemService if (callingUid != Process.SYSTEM_UID) { throw new SecurityException("Job has invalid flags"); } - if (job.hasLateConstraint() || job.hasEarlyConstraint()) { - Slog.wtf(TAG, "Jobs with time-constraints mustn't have" - +" FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job); + if (job.isPeriodic()) { + Slog.wtf(TAG, "Periodic jobs mustn't have" + + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job); } } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 55c0f5a95907..3e43d8ed918d 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -206,7 +206,7 @@ public class GnssLocationProvider implements LocationProviderInterface { private static final int UPDATE_NETWORK_STATE = 4; private static final int INJECT_NTP_TIME = 5; private static final int DOWNLOAD_XTRA_DATA = 6; - private static final int UPDATE_LOCATION = 7; + private static final int UPDATE_LOCATION = 7; // Handle external location from network listener private static final int ADD_LISTENER = 8; private static final int REMOVE_LISTENER = 9; private static final int INJECT_NTP_TIME_FINISHED = 10; @@ -259,6 +259,42 @@ public class GnssLocationProvider implements LocationProviderInterface { } } + // Simple class to hold stats reported in the Extras Bundle + private static class LocationExtras { + private int mSvCount; + private int mMeanCn0; + private int mMaxCn0; + private final Bundle mBundle; + + public LocationExtras() { + mBundle = new Bundle(); + } + + public void set(int svCount, int meanCn0, int maxCn0) { + mSvCount = svCount; + mMeanCn0 = meanCn0; + mMaxCn0 = maxCn0; + setBundle(mBundle); + } + + public void reset() { + set(0,0,0); + } + + // Also used by outside methods to add to other bundles + public void setBundle(Bundle extras) { + if (extras != null) { + extras.putInt("satellites", mSvCount); + extras.putInt("meanCn0", mMeanCn0); + extras.putInt("maxCn0", mMaxCn0); + } + } + + public Bundle getBundle() { + return mBundle; + } + } + private Object mLock = new Object(); // current status @@ -369,7 +405,7 @@ public class GnssLocationProvider implements LocationProviderInterface { private final NtpTrustedTime mNtpTime; private final ILocationManager mILocationManager; private Location mLocation = new Location(LocationManager.GPS_PROVIDER); - private Bundle mLocationExtras = new Bundle(); + private final LocationExtras mLocationExtras = new LocationExtras(); private final GnssStatusListenerHelper mListenerHelper; private final GnssMeasurementsProvider mGnssMeasurementsProvider; private final GnssNavigationMessageProvider mGnssNavigationMessageProvider; @@ -713,7 +749,7 @@ public class GnssLocationProvider implements LocationProviderInterface { mNtpTime = NtpTrustedTime.getInstance(context); mILocationManager = ilocationManager; - mLocation.setExtras(mLocationExtras); + mLocation.setExtras(mLocationExtras.getBundle()); // Create a wake lock mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -1245,29 +1281,17 @@ public class GnssLocationProvider implements LocationProviderInterface { @Override public int getStatus(Bundle extras) { - setLocationExtras(extras); + mLocationExtras.setBundle(extras); return mStatus; } - private void updateStatus(int status, int svCount, int meanCn0, int maxCn0) { - if (status != mStatus || svCount != mSvCount || meanCn0 != mMeanCn0 || maxCn0 != mMaxCn0) { + private void updateStatus(int status) { + if (status != mStatus) { mStatus = status; - mSvCount = svCount; - mMeanCn0 = meanCn0; - mMaxCn0 = maxCn0; - setLocationExtras(mLocationExtras); mStatusUpdateTime = SystemClock.elapsedRealtime(); } } - private void setLocationExtras(Bundle extras) { - if (extras != null) { - extras.putInt("satellites", mSvCount); - extras.putInt("meanCn0", mMeanCn0); - extras.putInt("maxCn0", mMaxCn0); - } - } - @Override public long getStatusUpdateTime() { return mStatusUpdateTime; @@ -1563,7 +1587,8 @@ public class GnssLocationProvider implements LocationProviderInterface { } // reset SV count to zero - updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0, 0, 0); + updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE); + mLocationExtras.reset(); mFixRequestTime = SystemClock.elapsedRealtime(); if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) { // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT @@ -1586,7 +1611,8 @@ public class GnssLocationProvider implements LocationProviderInterface { mLastFixTime = 0; // reset SV count to zero - updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0, 0, 0); + updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE); + mLocationExtras.reset(); } } @@ -1626,7 +1652,7 @@ public class GnssLocationProvider implements LocationProviderInterface { // It would be nice to push the elapsed real-time timestamp // further down the stack, but this is still useful mLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); - mLocation.setExtras(mLocationExtras); + mLocation.setExtras(mLocationExtras.getBundle()); try { mILocationManager.reportLocation(mLocation, false); @@ -1662,8 +1688,9 @@ public class GnssLocationProvider implements LocationProviderInterface { } if (mStarted && mStatus != LocationProvider.AVAILABLE) { - // we want to time out if we do not receive a fix - // within the time out and we are requesting infrequent fixes + // For devices that use framework scheduling, a timer may be set to ensure we don't + // spend too much power searching for a location, when the requested update rate is slow. + // As we just recievied a location, we'll cancel that timer. if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) { mAlarmManager.cancel(mTimeoutIntent); } @@ -1672,7 +1699,7 @@ public class GnssLocationProvider implements LocationProviderInterface { Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION); intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - updateStatus(LocationProvider.AVAILABLE, mSvCount, mMeanCn0, mMaxCn0); + updateStatus(LocationProvider.AVAILABLE); } if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted && @@ -1771,7 +1798,7 @@ public class GnssLocationProvider implements LocationProviderInterface { meanCn0 /= usedInFixCount; } // return number of sats used in fix instead of total reported - updateStatus(mStatus, usedInFixCount, meanCn0, maxCn0); + mLocationExtras.set(usedInFixCount, meanCn0, maxCn0); if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 && SystemClock.elapsedRealtime() - mLastFixTime > RECENT_FIX_TIMEOUT) { @@ -1779,7 +1806,7 @@ public class GnssLocationProvider implements LocationProviderInterface { Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION); intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount, mMeanCn0, mMaxCn0); + updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE); } } @@ -2726,9 +2753,6 @@ public class GnssLocationProvider implements LocationProviderInterface { private float mSvElevations[] = new float[MAX_SVS]; private float mSvAzimuths[] = new float[MAX_SVS]; private float mSvCarrierFreqs[] = new float[MAX_SVS]; - private int mSvCount; - private int mMeanCn0; - private int mMaxCn0; // preallocated to avoid memory allocation in reportNmea() private byte[] mNmeaBuffer = new byte[120]; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 61effb8ca49e..3cd24f967a86 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -19118,8 +19118,21 @@ public class PackageManagerService extends IPackageManager.Stub public void deleteApplicationCacheFilesAsUser(final String packageName, final int userId, final IPackageDataObserver observer) { final int callingUid = Binder.getCallingUid(); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.DELETE_CACHE_FILES, null); + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES) + != PackageManager.PERMISSION_GRANTED) { + // If the caller has the old delete cache permission, silently ignore. Else throw. + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.DELETE_CACHE_FILES) + == PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "Calling uid " + callingUid + " does not have " + + android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES + + ", silently ignoring"); + return; + } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES, null); + } mPermissionManager.enforceCrossUserPermission(callingUid, userId, /* requireFullPermission= */ true, /* checkShell= */ false, "delete application cache files"); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index ef6067a73716..8101b56334e7 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -783,7 +783,9 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { enforceCallingPermission(); if (DEBUG) Slog.d(TAG, "learned that statsdReady"); sayHiToStatsd(); // tell statsd that we're ready too and link to it - mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED), + mContext.sendBroadcastAsUser( + new Intent(StatsManager.ACTION_STATSD_STARTED) + .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND), UserHandle.SYSTEM, android.Manifest.permission.DUMP); } diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java index 0e8d8bc8e411..8f4cada0d0ba 100644 --- a/services/core/java/com/android/server/timezone/PackageTracker.java +++ b/services/core/java/com/android/server/timezone/PackageTracker.java @@ -59,7 +59,7 @@ public class PackageTracker { private static final String TAG = "timezone.PackageTracker"; private final PackageManagerHelper mPackageManagerHelper; - private final IntentHelper mIntentHelper; + private final PackageTrackerIntentHelper mIntentHelper; private final ConfigHelper mConfigHelper; private final PackageStatusStorage mPackageStatusStorage; private final Clock mElapsedRealtimeClock; @@ -103,13 +103,13 @@ public class PackageTracker { helperImpl /* configHelper */, helperImpl /* packageManagerHelper */, new PackageStatusStorage(storageDir), - new IntentHelperImpl(context)); + new PackageTrackerIntentHelperImpl(context)); } // A constructor that can be used by tests to supply mocked / faked dependencies. PackageTracker(Clock elapsedRealtimeClock, ConfigHelper configHelper, PackageManagerHelper packageManagerHelper, PackageStatusStorage packageStatusStorage, - IntentHelper intentHelper) { + PackageTrackerIntentHelper intentHelper) { mElapsedRealtimeClock = elapsedRealtimeClock; mConfigHelper = configHelper; mPackageManagerHelper = packageManagerHelper; diff --git a/services/core/java/com/android/server/timezone/IntentHelper.java b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java index 5de543213009..3753ece03bb0 100644 --- a/services/core/java/com/android/server/timezone/IntentHelper.java +++ b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java @@ -21,7 +21,7 @@ package com.android.server.timezone; * it is not possible to test various cases with the real one because of the need to simulate * receiving and broadcasting intents. */ -interface IntentHelper { +interface PackageTrackerIntentHelper { void initialize(String updateAppPackageName, String dataAppPackageName, PackageTracker packageTracker); diff --git a/services/core/java/com/android/server/timezone/IntentHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java index 6e6259d902dc..4110d881f3fb 100644 --- a/services/core/java/com/android/server/timezone/IntentHelperImpl.java +++ b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java @@ -28,16 +28,16 @@ import android.os.UserHandle; import android.util.Slog; /** - * The bona fide implementation of {@link IntentHelper}. + * The bona fide implementation of {@link PackageTrackerIntentHelper}. */ -final class IntentHelperImpl implements IntentHelper { +final class PackageTrackerIntentHelperImpl implements PackageTrackerIntentHelper { - private final static String TAG = "timezone.IntentHelperImpl"; + private final static String TAG = "timezone.PackageTrackerIntentHelperImpl"; private final Context mContext; private String mUpdaterAppPackageName; - IntentHelperImpl(Context context) { + PackageTrackerIntentHelperImpl(Context context) { mContext = context; } diff --git a/cmds/statsd/src/statsd_internal.proto b/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java index 25aacee2770a..bb317cf2f988 100644 --- a/cmds/statsd/src/statsd_internal.proto +++ b/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java @@ -14,16 +14,22 @@ * limitations under the License. */ -syntax = "proto2"; -option optimize_for = LITE_RUNTIME; +package com.android.server.timezone; -package android.os.statsd; +/** + * An easy-to-mock interface around intent sending / receiving for use by + * {@link RulesManagerService}; it is not possible to test various cases with the real one because + * of the need to simulate broadcasting intents. + */ +interface RulesManagerIntentHelper { -option java_package = "com.android.os"; -option java_outer_classname = "StatsdInternalProto"; + /** + * Send a broadcast informing listeners that a time zone operation is staged. + */ + void sendTimeZoneOperationStaged(); -message Field { - optional int32 field = 1; - optional int32 position_index = 2 [default = -1]; - repeated Field child = 3; + /** + * Send a broadcast informing listeners that a time zone operation is no longer staged. + */ + void sendTimeZoneOperationUnstaged(); } diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java index be9b204721db..872d7237f4f1 100644 --- a/services/core/java/com/android/server/timezone/RulesManagerService.java +++ b/services/core/java/com/android/server/timezone/RulesManagerService.java @@ -99,6 +99,7 @@ public final class RulesManagerService extends IRulesManager.Stub { private final PermissionHelper mPermissionHelper; private final PackageTracker mPackageTracker; private final Executor mExecutor; + private final RulesManagerIntentHelper mIntentHelper; private final TimeZoneDistroInstaller mInstaller; private static RulesManagerService create(Context context) { @@ -106,16 +107,19 @@ public final class RulesManagerService extends IRulesManager.Stub { return new RulesManagerService( helper /* permissionHelper */, helper /* executor */, + helper /* intentHelper */, PackageTracker.create(context), new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR)); } // A constructor that can be used by tests to supply mocked / faked dependencies. - RulesManagerService(PermissionHelper permissionHelper, - Executor executor, PackageTracker packageTracker, + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + RulesManagerService(PermissionHelper permissionHelper, Executor executor, + RulesManagerIntentHelper intentHelper, PackageTracker packageTracker, TimeZoneDistroInstaller timeZoneDistroInstaller) { mPermissionHelper = permissionHelper; mExecutor = executor; + mIntentHelper = intentHelper; mPackageTracker = packageTracker; mInstaller = timeZoneDistroInstaller; } @@ -271,6 +275,10 @@ public final class RulesManagerService extends IRulesManager.Stub { TimeZoneDistro distro = new TimeZoneDistro(is); int installerResult = mInstaller.stageInstallWithErrorCode(distro); + + // Notify interested parties that something is staged. + sendInstallNotificationIntentIfRequired(installerResult); + int resultCode = mapInstallerResultToApiCode(installerResult); EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode); sendFinishedStatus(mCallback, resultCode); @@ -291,6 +299,12 @@ public final class RulesManagerService extends IRulesManager.Stub { } } + private void sendInstallNotificationIntentIfRequired(int installerResult) { + if (installerResult == TimeZoneDistroInstaller.INSTALL_SUCCESS) { + mIntentHelper.sendTimeZoneOperationStaged(); + } + } + private int mapInstallerResultToApiCode(int installerResult) { switch (installerResult) { case TimeZoneDistroInstaller.INSTALL_SUCCESS: @@ -351,6 +365,10 @@ public final class RulesManagerService extends IRulesManager.Stub { boolean packageTrackerStatus = false; try { int uninstallResult = mInstaller.stageUninstall(); + + // Notify interested parties that something is staged. + sendUninstallNotificationIntentIfRequired(uninstallResult); + packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED); @@ -374,6 +392,20 @@ public final class RulesManagerService extends IRulesManager.Stub { mOperationInProgress.set(false); } } + + private void sendUninstallNotificationIntentIfRequired(int uninstallResult) { + switch (uninstallResult) { + case TimeZoneDistroInstaller.UNINSTALL_SUCCESS: + mIntentHelper.sendTimeZoneOperationStaged(); + break; + case TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED: + mIntentHelper.sendTimeZoneOperationUnstaged(); + break; + case TimeZoneDistroInstaller.UNINSTALL_FAIL: + default: + // No-op - unknown or nothing to notify about. + } + } } private void sendFinishedStatus(ICallback callback, int resultCode) { diff --git a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java index e8a401e79235..8f5c7a783304 100644 --- a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java +++ b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java @@ -18,22 +18,20 @@ package com.android.server.timezone; import com.android.internal.util.DumpUtils; +import android.app.timezone.RulesManager; import android.content.Context; -import android.content.pm.PackageManager; +import android.content.Intent; import android.os.AsyncTask; -import android.os.Binder; -import android.os.ParcelFileDescriptor; +import android.os.UserHandle; -import java.io.FileInputStream; -import java.io.IOException; import java.io.PrintWriter; import java.util.concurrent.Executor; -import libcore.io.Streams; /** * A single class that implements multiple helper interfaces for use by {@link RulesManagerService}. */ -final class RulesManagerServiceHelperImpl implements PermissionHelper, Executor { +final class RulesManagerServiceHelperImpl + implements PermissionHelper, Executor, RulesManagerIntentHelper { private final Context mContext; @@ -55,4 +53,22 @@ final class RulesManagerServiceHelperImpl implements PermissionHelper, Executor public void execute(Runnable runnable) { AsyncTask.execute(runnable); } + + @Override + public void sendTimeZoneOperationStaged() { + sendOperationIntent(true /* staged */); + } + + @Override + public void sendTimeZoneOperationUnstaged() { + sendOperationIntent(false /* staged */); + } + + private void sendOperationIntent(boolean staged) { + Intent intent = new Intent(RulesManager.ACTION_RULES_UPDATE_OPERATION); + intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + intent.putExtra(RulesManager.EXTRA_OPERATION_STAGED, staged); + mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); + } + } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java index 1b2f9542df11..98fcb0be935c 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java @@ -169,7 +169,12 @@ class SurfaceAnimationRunner { anim.addUpdateListener(animation -> { synchronized (mCancelLock) { if (!a.mCancelled) { - applyTransformation(a, mFrameTransaction, anim.getCurrentPlayTime()); + final long duration = anim.getDuration(); + long currentPlayTime = anim.getCurrentPlayTime(); + if (currentPlayTime > duration) { + currentPlayTime = duration; + } + applyTransformation(a, mFrameTransaction, currentPlayTime); } } diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 97d6c43e2fd0..5d8aca195ef8 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -60,7 +60,6 @@ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java index 9cf6392cab97..d9f4adfb5e06 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java @@ -31,7 +31,6 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import java.io.File; -import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.time.Clock; @@ -1400,7 +1399,7 @@ public class PackageTrackerTest { /** * A fake IntentHelper implementation for use in tests. */ - private static class FakeIntentHelper implements IntentHelper { + private static class FakeIntentHelper implements PackageTrackerIntentHelper { private PackageTracker mPackageTracker; private String mUpdateAppPackageName; diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java index 1cfae1ef1a6d..f5969f39e87c 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java @@ -68,6 +68,7 @@ public class RulesManagerServiceTest { private FakeExecutor mFakeExecutor; private PermissionHelper mMockPermissionHelper; + private RulesManagerIntentHelper mMockIntentHelper; private PackageTracker mMockPackageTracker; private TimeZoneDistroInstaller mMockTimeZoneDistroInstaller; @@ -77,11 +78,13 @@ public class RulesManagerServiceTest { mMockPackageTracker = mock(PackageTracker.class); mMockPermissionHelper = mock(PermissionHelper.class); + mMockIntentHelper = mock(RulesManagerIntentHelper.class); mMockTimeZoneDistroInstaller = mock(TimeZoneDistroInstaller.class); mRulesManagerService = new RulesManagerService( mMockPermissionHelper, mFakeExecutor, + mMockIntentHelper, mMockPackageTracker, mMockTimeZoneDistroInstaller); } @@ -329,6 +332,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -353,6 +357,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -372,6 +377,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -394,6 +400,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -416,6 +423,7 @@ public class RulesManagerServiceTest { callback.assertNoResultReceived(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); // Set up the installer. configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS); @@ -428,6 +436,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageInstallCalled(); verifyPackageTrackerCalled(token, true /* success */); + verifyStagedOperationIntentSent(); // Check the callback was called. callback.assertResultReceived(Callback.SUCCESS); @@ -450,6 +459,7 @@ public class RulesManagerServiceTest { // Assert nothing has happened yet. verifyNoInstallerCallsMade(); callback.assertNoResultReceived(); + verifyNoIntentsSent(); // Set up the installer. configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS); @@ -462,6 +472,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageInstallCalled(); verifyPackageTrackerCalled(null /* expectedToken */, true /* success */); + verifyStagedOperationIntentSent(); // Check the callback was received. callback.assertResultReceived(Callback.SUCCESS); @@ -486,6 +497,7 @@ public class RulesManagerServiceTest { // Assert nothing has happened yet. verifyNoInstallerCallsMade(); callback.assertNoResultReceived(); + verifyNoIntentsSent(); // Set up the installer. configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR); @@ -502,6 +514,9 @@ public class RulesManagerServiceTest { boolean expectedSuccess = true; verifyPackageTrackerCalled(token, expectedSuccess); + // Nothing should be staged, so no intents sent. + verifyNoIntentsSent(); + // Check the callback was received. callback.assertResultReceived(Callback.ERROR_INSTALL_VALIDATION_ERROR); } @@ -529,6 +544,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -548,6 +564,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -566,6 +583,7 @@ public class RulesManagerServiceTest { mFakeExecutor.assertNothingQueued(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -585,6 +603,7 @@ public class RulesManagerServiceTest { callback.assertNoResultReceived(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); // Set up the installer. configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS); @@ -595,6 +614,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageUninstallCalled(); verifyPackageTrackerCalled(token, true /* success */); + verifyStagedOperationIntentSent(); // Check the callback was called. callback.assertResultReceived(Callback.SUCCESS); @@ -617,6 +637,7 @@ public class RulesManagerServiceTest { callback.assertNoResultReceived(); verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); // Set up the installer. configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED); @@ -627,6 +648,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageUninstallCalled(); verifyPackageTrackerCalled(token, true /* success */); + verifyUnstagedOperationIntentSent(); // Check the callback was called. callback.assertResultReceived(Callback.SUCCESS); @@ -645,6 +667,7 @@ public class RulesManagerServiceTest { // Assert nothing has happened yet. verifyNoInstallerCallsMade(); callback.assertNoResultReceived(); + verifyNoIntentsSent(); // Set up the installer. configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS); @@ -655,6 +678,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageUninstallCalled(); verifyPackageTrackerCalled(null /* expectedToken */, true /* success */); + verifyStagedOperationIntentSent(); // Check the callback was received. callback.assertResultReceived(Callback.SUCCESS); @@ -676,6 +700,7 @@ public class RulesManagerServiceTest { // Assert nothing has happened yet. verifyNoInstallerCallsMade(); callback.assertNoResultReceived(); + verifyNoIntentsSent(); // Set up the installer. configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_FAIL); @@ -686,6 +711,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyStageUninstallCalled(); verifyPackageTrackerCalled(token, false /* success */); + verifyNoIntentsSent(); // Check the callback was received. callback.assertResultReceived(Callback.ERROR_UNKNOWN_FAILURE); @@ -714,6 +740,7 @@ public class RulesManagerServiceTest { // Verify the expected calls were made to other components. verifyPackageTrackerCalled(token, true /* success */); verifyNoInstallerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -734,6 +761,7 @@ public class RulesManagerServiceTest { // Assert no other calls were made. verifyNoInstallerCallsMade(); verifyNoPackageTrackerCallsMade(); + verifyNoIntentsSent(); } @Test @@ -749,6 +777,7 @@ public class RulesManagerServiceTest { // Assert everything required was done. verifyNoInstallerCallsMade(); verifyPackageTrackerCalled(token, false /* success */); + verifyNoIntentsSent(); } @Test @@ -761,6 +790,7 @@ public class RulesManagerServiceTest { // Assert everything required was done. verifyNoInstallerCallsMade(); verifyPackageTrackerCalled(null /* token */, true /* success */); + verifyNoIntentsSent(); } @Test @@ -865,6 +895,21 @@ public class RulesManagerServiceTest { reset(mMockPackageTracker); } + private void verifyNoIntentsSent() { + verifyNoMoreInteractions(mMockIntentHelper); + reset(mMockIntentHelper); + } + + private void verifyStagedOperationIntentSent() { + verify(mMockIntentHelper).sendTimeZoneOperationStaged(); + reset(mMockIntentHelper); + } + + private void verifyUnstagedOperationIntentSent() { + verify(mMockIntentHelper).sendTimeZoneOperationUnstaged(); + reset(mMockIntentHelper); + } + private void configureCallerHasPermission() throws Exception { doNothing() .when(mMockPermissionHelper) diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 38408fe3d0fd..77413d9c730d 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -17,6 +17,7 @@ package android.telephony; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -34,6 +35,8 @@ import android.os.Parcelable; import android.util.DisplayMetrics; import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; /** * A Parcelable class for Subscription Information. @@ -332,12 +335,7 @@ public class SubscriptionInfo implements Parcelable { return this.mCountryIso; } - /** - * @return whether the subscription is an embedded one. - * @hide - * - * TODO(b/35851809): Make this public. - */ + /** @return whether the subscription is an embedded one. */ public boolean isEmbedded() { return this.mIsEmbedded; } @@ -351,9 +349,9 @@ public class SubscriptionInfo implements Parcelable { * @return whether the app is authorized to manage this subscription per its metadata. * @throws UnsupportedOperationException if this subscription is not embedded. * @hide - * - * TODO(b/35851809): Make this public. + * @deprecated - Do not use. */ + @Deprecated public boolean canManageSubscription(Context context) { return canManageSubscription(context, context.getPackageName()); } @@ -367,7 +365,9 @@ public class SubscriptionInfo implements Parcelable { * @return whether the app is authorized to manage this subscription per its metadata. * @throws UnsupportedOperationException if this subscription is not embedded. * @hide + * @deprecated - Do not use. */ + @Deprecated public boolean canManageSubscription(Context context, String packageName) { if (!isEmbedded()) { throw new UnsupportedOperationException("Not an embedded subscription"); @@ -395,14 +395,14 @@ public class SubscriptionInfo implements Parcelable { * @return the {@link UiccAccessRule}s dictating who is authorized to manage this subscription. * @throws UnsupportedOperationException if this subscription is not embedded. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ - public @Nullable UiccAccessRule[] getAccessRules() { + @SystemApi + public @Nullable List<UiccAccessRule> getAccessRules() { if (!isEmbedded()) { throw new UnsupportedOperationException("Not an embedded subscription"); } - return mAccessRules; + if (mAccessRules == null) return null; + return Arrays.asList(mAccessRules); } /** diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index debf43da79b4..11a1984e8934 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -30,6 +30,7 @@ import android.annotation.SystemService; import android.app.BroadcastOptions; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -822,10 +823,13 @@ public class SubscriptionManager { * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex} * then by {@link SubscriptionInfo#getSubscriptionId}. * </ul> - * @hide * - * TODO(b/35851809): Make this a SystemApi. + * <p> + * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required + * for #getAvailableSubscriptionInfoList to be invoked. + * @hide */ + @SystemApi public List<SubscriptionInfo> getAvailableSubscriptionInfoList() { List<SubscriptionInfo> result = null; @@ -863,9 +867,6 @@ public class SubscriptionManager { * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex} * then by {@link SubscriptionInfo#getSubscriptionId}. * </ul> - * @hide - * - * TODO(b/35851809): Make this public. */ public List<SubscriptionInfo> getAccessibleSubscriptionInfoList() { List<SubscriptionInfo> result = null; @@ -891,9 +892,8 @@ public class SubscriptionManager { * * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ + @SystemApi public void requestEmbeddedSubscriptionInfoListRefresh() { try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); @@ -1892,4 +1892,51 @@ public class SubscriptionManager { options.setTemporaryAppWhitelistDuration(TimeUnit.MINUTES.toMillis(1)); mContext.sendBroadcast(intent, null, options.toBundle()); } + + /** + * Checks whether the app with the given context is authorized to manage the given subscription + * according to its metadata. Only supported for embedded subscriptions (if + * {@code SubscriptionInfo#isEmbedded} returns true). + * + * @param info The subscription to check. + * @return whether the app is authorized to manage this subscription per its metadata. + * @throws UnsupportedOperationException if this subscription is not embedded. + */ + public boolean canManageSubscription(SubscriptionInfo info) { + return canManageSubscription(info, mContext.getPackageName()); + } + + /** + * Checks whether the given app is authorized to manage the given subscription according to its + * metadata. Only supported for embedded subscriptions (if {@code SubscriptionInfo#isEmbedded} + * returns true). + * + * @param info The subscription to check. + * @param packageName Package name of the app to check. + * @return whether the app is authorized to manage this subscription per its metadata. + * @throws UnsupportedOperationException if this subscription is not embedded. + * @hide + */ + public boolean canManageSubscription(SubscriptionInfo info, String packageName) { + if (!info.isEmbedded()) { + throw new UnsupportedOperationException("Not an embedded subscription"); + } + if (info.getAccessRules() == null) { + return false; + } + PackageManager packageManager = mContext.getPackageManager(); + PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException("Unknown package: " + packageName, e); + } + for (UiccAccessRule rule : info.getAccessRules()) { + if (rule.getCarrierPrivilegeStatus(packageInfo) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return true; + } + } + return false; + } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 03d8b5c6f39d..7afd28ce181f 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -4801,7 +4801,7 @@ public class TelephonyManager { } /** - * Sets the telephony property with the value specified. + * Sets a per-phone telephony property with the value specified. * * @hide */ @@ -4851,6 +4851,20 @@ public class TelephonyManager { } /** + * Sets a global telephony property with the value specified. + * + * @hide + */ + public static void setTelephonyProperty(String property, String value) { + if (value == null) { + value = ""; + } + Rlog.d(TAG, "setTelephonyProperty: success" + " property=" + + property + " value: " + value); + SystemProperties.set(property, value); + } + + /** * Convenience function for retrieving a value from the secure settings * value list as an integer. Note that internally setting values are * always stored as strings; this function converts the string to an @@ -4939,7 +4953,7 @@ public class TelephonyManager { } /** - * Gets the telephony property. + * Gets a per-phone telephony property. * * @hide */ @@ -4955,6 +4969,19 @@ public class TelephonyManager { return propVal == null ? defaultVal : propVal; } + /** + * Gets a global telephony property. + * + * See also getTelephonyProperty(phoneId, property, defaultVal). Most telephony properties are + * per-phone. + * + * @hide + */ + public static String getTelephonyProperty(String property, String defaultVal) { + String propVal = SystemProperties.get(property); + return propVal == null ? defaultVal : propVal; + } + /** @hide */ public int getSimCount() { // FIXME Need to get it from Telephony Dev Controller when that gets implemented! diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java index 39372019c20f..c3f8a1930409 100644 --- a/telephony/java/android/telephony/UiccAccessRule.java +++ b/telephony/java/android/telephony/UiccAccessRule.java @@ -16,6 +16,7 @@ package android.telephony; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.content.pm.PackageInfo; import android.content.pm.Signature; import android.os.Parcel; @@ -39,9 +40,8 @@ import java.util.Objects; * specification. * * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ +@SystemApi public final class UiccAccessRule implements Parcelable { private static final String TAG = "UiccAccessRule"; diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java index 01041c8b1360..88db22b82c5c 100644 --- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java +++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java @@ -16,18 +16,17 @@ package android.telephony.euicc; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.UiccAccessRule; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import com.android.internal.util.Preconditions; -/** - * Information about a subscription which is available for download. - * - * TODO(b/35851809): Make this public. - * @hide - */ +/** Information about a subscription which is available for download. */ public final class DownloadableSubscription implements Parcelable { public static final Creator<DownloadableSubscription> CREATOR = @@ -46,11 +45,12 @@ public final class DownloadableSubscription implements Parcelable { /** * Activation code. May be null for subscriptions which are not based on activation codes, e.g. * to download a default subscription assigned to this device. + * Should use getEncodedActivationCode() instead. * @hide - * - * TODO(b/35851809): Make this a SystemApi. + * @deprecated - Do not use. This will be private. Use getEncodedActivationCode() instead. */ @Nullable + @Deprecated public final String encodedActivationCode; @Nullable private String confirmationCode; @@ -58,8 +58,16 @@ public final class DownloadableSubscription implements Parcelable { // see getCarrierName and setCarrierName @Nullable private String carrierName; + // see getAccessRules and setAccessRules - private UiccAccessRule[] accessRules; + @Nullable + private List<UiccAccessRule> accessRules; + + /** Gets the activation code. */ + @Nullable + public String getEncodedActivationCode() { + return encodedActivationCode; + } /** @hide */ private DownloadableSubscription(String encodedActivationCode) { @@ -70,7 +78,59 @@ public final class DownloadableSubscription implements Parcelable { encodedActivationCode = in.readString(); confirmationCode = in.readString(); carrierName = in.readString(); - accessRules = in.createTypedArray(UiccAccessRule.CREATOR); + accessRules = new ArrayList<UiccAccessRule>(); + in.readTypedList(accessRules, UiccAccessRule.CREATOR); + } + + private DownloadableSubscription(String encodedActivationCode, String confirmationCode, + String carrierName, List<UiccAccessRule> accessRules) { + this.encodedActivationCode = encodedActivationCode; + this.confirmationCode = confirmationCode; + this.carrierName = carrierName; + this.accessRules = accessRules; + } + + /** @hide */ + @SystemApi + public static final class Builder { + @Nullable private String encodedActivationCode; + @Nullable private String confirmationCode; + @Nullable private String carrierName; + List<UiccAccessRule> accessRules; + + public Builder() {} + + public Builder(DownloadableSubscription baseSubscription) { + encodedActivationCode = baseSubscription.getEncodedActivationCode(); + confirmationCode = baseSubscription.getConfirmationCode(); + carrierName = baseSubscription.getCarrierName(); + accessRules = baseSubscription.getAccessRules(); + } + + public DownloadableSubscription build() { + return new DownloadableSubscription(encodedActivationCode, confirmationCode, + carrierName, accessRules); + } + + public Builder setEncodedActivationCode(String value) { + encodedActivationCode = value; + return this; + } + + public Builder setConfirmationCode(String value) { + confirmationCode = value; + return this; + } + + public Builder setCarrierName(String value) { + carrierName = value; + return this; + } + + public Builder setAccessRules(List<UiccAccessRule> value) { + accessRules = value; + return this; + } } /** @@ -87,7 +147,10 @@ public final class DownloadableSubscription implements Parcelable { /** * Sets the confirmation code. + * @hide + * @deprecated - Do not use. */ + @Deprecated public void setConfirmationCode(String confirmationCode) { this.confirmationCode = confirmationCode; } @@ -103,9 +166,9 @@ public final class DownloadableSubscription implements Parcelable { /** * Set the user-visible carrier name. * @hide - * - * TODO(b/35851809): Make this a SystemApi. + * @deprecated - Do not use. */ + @Deprecated public void setCarrierName(String carrierName) { this.carrierName = carrierName; } @@ -117,44 +180,51 @@ public final class DownloadableSubscription implements Parcelable { * those created with {@link #forActivationCode}). May be populated with * {@link EuiccManager#getDownloadableSubscriptionMetadata}. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ + @SystemApi @Nullable public String getCarrierName() { return carrierName; } /** - * Returns the {@link UiccAccessRule}s dictating access to this subscription. + * Returns the {@link UiccAccessRule}s in list dictating access to this subscription. * * <p>Only present for downloadable subscriptions that were queried from a server (as opposed to * those created with {@link #forActivationCode}). May be populated with * {@link EuiccManager#getDownloadableSubscriptionMetadata}. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ - public UiccAccessRule[] getAccessRules() { + @SystemApi + public List<UiccAccessRule> getAccessRules() { return accessRules; } /** * Set the {@link UiccAccessRule}s dictating access to this subscription. * @hide - * - * TODO(b/35851809): Make this a SystemApi. + * @deprecated - Do not use. */ - public void setAccessRules(UiccAccessRule[] accessRules) { + @Deprecated + public void setAccessRules(List<UiccAccessRule> accessRules) { this.accessRules = accessRules; } + /** + * @hide + * @deprecated - Do not use. + */ + @Deprecated + public void setAccessRules(UiccAccessRule[] accessRules) { + this.accessRules = Arrays.asList(accessRules); + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(encodedActivationCode); dest.writeString(confirmationCode); dest.writeString(carrierName); - dest.writeTypedArray(accessRules, flags); + dest.writeTypedList(accessRules); } @Override diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java index a1a6a5a4d5bb..c3f40074308b 100644 --- a/telephony/java/android/telephony/euicc/EuiccCardManager.java +++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java @@ -17,6 +17,7 @@ package android.telephony.euicc; import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; @@ -49,14 +50,14 @@ import com.android.internal.telephony.euicc.ISwitchToProfileCallback; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import android.annotation.CallbackExecutor; +import java.util.concurrent.Executor; /** * EuiccCardManager is the application interface to an eSIM card. - * * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ +@SystemApi public class EuiccCardManager { private static final String TAG = "EuiccCardManager"; @@ -68,6 +69,7 @@ public class EuiccCardManager { CANCEL_REASON_TIMEOUT, CANCEL_REASON_PPR_NOT_ALLOWED }) + /** @hide */ public @interface CancelReason {} /** @@ -96,6 +98,7 @@ public class EuiccCardManager { RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES, RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS }) + /** @hide */ public @interface ResetOption {} /** Deletes all operational profiles. */ @@ -143,18 +146,20 @@ public class EuiccCardManager { } /** - * Gets all the profiles on eUicc. + * Requests all the profiles on eUicc. * * @param cardId The Id of the eUICC. + * @param executor The executor through which the callback should be invode. * @param callback The callback to get the result code and all the profiles. */ - public void getAllProfiles(String cardId, ResultCallback<EuiccProfileInfo[]> callback) { + public void requestAllProfiles(String cardId, @CallbackExecutor Executor executor, + ResultCallback<EuiccProfileInfo[]> callback) { try { getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(), cardId, new IGetAllProfilesCallback.Stub() { @Override public void onComplete(int resultCode, EuiccProfileInfo[] profiles) { - callback.onComplete(resultCode, profiles); + executor.execute(() -> callback.onComplete(resultCode, profiles)); } }); } catch (RemoteException e) { @@ -164,19 +169,21 @@ public class EuiccCardManager { } /** - * Gets the profile of the given iccid. + * Requests the profile of the given iccid. * * @param cardId The Id of the eUICC. * @param iccid The iccid of the profile. + * @param executor The executor through which the callback should be invode. * @param callback The callback to get the result code and profile. */ - public void getProfile(String cardId, String iccid, ResultCallback<EuiccProfileInfo> callback) { + public void requestProfile(String cardId, String iccid, @CallbackExecutor Executor executor, + ResultCallback<EuiccProfileInfo> callback) { try { getIEuiccCardController().getProfile(mContext.getOpPackageName(), cardId, iccid, new IGetProfileCallback.Stub() { @Override public void onComplete(int resultCode, EuiccProfileInfo profile) { - callback.onComplete(resultCode, profile); + executor.execute(() -> callback.onComplete(resultCode, profile)); } }); } catch (RemoteException e) { @@ -191,16 +198,17 @@ public class EuiccCardManager { * @param cardId The Id of the eUICC. * @param iccid The iccid of the profile. * @param refresh Whether sending the REFRESH command to modem. + * @param executor The executor through which the callback should be invode. * @param callback The callback to get the result code. */ public void disableProfile(String cardId, String iccid, boolean refresh, - ResultCallback<Void> callback) { + @CallbackExecutor Executor executor, ResultCallback<Void> callback) { try { getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid, refresh, new IDisableProfileCallback.Stub() { @Override public void onComplete(int resultCode) { - callback.onComplete(resultCode, null); + executor.execute(() -> callback.onComplete(resultCode, null)); } }); } catch (RemoteException e) { @@ -216,16 +224,17 @@ public class EuiccCardManager { * @param cardId The Id of the eUICC. * @param iccid The iccid of the profile to switch to. * @param refresh Whether sending the REFRESH command to modem. + * @param executor The executor through which the callback should be invode. * @param callback The callback to get the result code and the EuiccProfileInfo enabled. */ public void switchToProfile(String cardId, String iccid, boolean refresh, - ResultCallback<EuiccProfileInfo> callback) { + @CallbackExecutor Executor executor, ResultCallback<EuiccProfileInfo> callback) { try { getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid, refresh, new ISwitchToProfileCallback.Stub() { @Override public void onComplete(int resultCode, EuiccProfileInfo profile) { - callback.onComplete(resultCode, profile); + executor.execute(() -> callback.onComplete(resultCode, profile)); } }); } catch (RemoteException e) { @@ -240,16 +249,17 @@ public class EuiccCardManager { * @param cardId The Id of the eUICC. * @param iccid The iccid of the profile. * @param nickname The nickname of the profile. + * @param executor The executor through which the callback should be invode. * @param callback The callback to get the result code. */ public void setNickname(String cardId, String iccid, String nickname, - ResultCallback<Void> callback) { + @CallbackExecutor Executor executor, ResultCallback<Void> callback) { try { getIEuiccCardController().setNickname(mContext.getOpPackageName(), cardId, iccid, nickname, new ISetNicknameCallback.Stub() { @Override public void onComplete(int resultCode) { - callback.onComplete(resultCode, null); + executor.execute(() -> callback.onComplete(resultCode, null)); } }); } catch (RemoteException e) { @@ -263,15 +273,17 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param iccid The iccid of the profile. + * @param executor The executor through which the callback should be invode. * @param callback The callback to get the result code. */ - public void deleteProfile(String cardId, String iccid, ResultCallback<Void> callback) { + public void deleteProfile(String cardId, String iccid, @CallbackExecutor Executor executor, + ResultCallback<Void> callback) { try { getIEuiccCardController().deleteProfile(mContext.getOpPackageName(), cardId, iccid, new IDeleteProfileCallback.Stub() { @Override public void onComplete(int resultCode) { - callback.onComplete(resultCode, null); + executor.execute(() -> callback.onComplete(resultCode, null)); } }); } catch (RemoteException e) { @@ -286,15 +298,17 @@ public class EuiccCardManager { * @param cardId The Id of the eUICC. * @param options Bits of the options of resetting which parts of the eUICC memory. See * EuiccCard for details. + * @param executor The executor through which the callback should be invode. * @param callback The callback to get the result code. */ - public void resetMemory(String cardId, @ResetOption int options, ResultCallback<Void> callback) { + public void resetMemory(String cardId, @ResetOption int options, + @CallbackExecutor Executor executor, ResultCallback<Void> callback) { try { getIEuiccCardController().resetMemory(mContext.getOpPackageName(), cardId, options, new IResetMemoryCallback.Stub() { @Override public void onComplete(int resultCode) { - callback.onComplete(resultCode, null); + executor.execute(() -> callback.onComplete(resultCode, null)); } }); } catch (RemoteException e) { @@ -304,18 +318,20 @@ public class EuiccCardManager { } /** - * Gets the default SM-DP+ address from eUICC. + * Requests the default SM-DP+ address from eUICC. * * @param cardId The Id of the eUICC. + * @param executor The executor through which the callback should be invode. * @param callback The callback to get the result code and the default SM-DP+ address. */ - public void getDefaultSmdpAddress(String cardId, ResultCallback<String> callback) { + public void requestDefaultSmdpAddress(String cardId, @CallbackExecutor Executor executor, + ResultCallback<String> callback) { try { getIEuiccCardController().getDefaultSmdpAddress(mContext.getOpPackageName(), cardId, new IGetDefaultSmdpAddressCallback.Stub() { @Override public void onComplete(int resultCode, String address) { - callback.onComplete(resultCode, address); + executor.execute(() -> callback.onComplete(resultCode, address)); } }); } catch (RemoteException e) { @@ -325,18 +341,20 @@ public class EuiccCardManager { } /** - * Gets the SM-DS address from eUICC. + * Requests the SM-DS address from eUICC. * * @param cardId The Id of the eUICC. + * @param executor The executor through which the callback should be invode. * @param callback The callback to get the result code and the SM-DS address. */ - public void getSmdsAddress(String cardId, ResultCallback<String> callback) { + public void requestSmdsAddress(String cardId, @CallbackExecutor Executor executor, + ResultCallback<String> callback) { try { getIEuiccCardController().getSmdsAddress(mContext.getOpPackageName(), cardId, new IGetSmdsAddressCallback.Stub() { @Override public void onComplete(int resultCode, String address) { - callback.onComplete(resultCode, address); + executor.execute(() -> callback.onComplete(resultCode, address)); } }); } catch (RemoteException e) { @@ -350,16 +368,18 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param defaultSmdpAddress The default SM-DP+ address to set. + * @param executor The executor through which the callback should be invode. * @param callback The callback to get the result code. */ - public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress, ResultCallback<Void> callback) { + public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress, + @CallbackExecutor Executor executor, ResultCallback<Void> callback) { try { getIEuiccCardController().setDefaultSmdpAddress(mContext.getOpPackageName(), cardId, defaultSmdpAddress, new ISetDefaultSmdpAddressCallback.Stub() { @Override public void onComplete(int resultCode) { - callback.onComplete(resultCode, null); + executor.execute(() -> callback.onComplete(resultCode, null)); } }); } catch (RemoteException e) { @@ -369,18 +389,20 @@ public class EuiccCardManager { } /** - * Gets Rules Authorisation Table. + * Requests Rules Authorisation Table. * * @param cardId The Id of the eUICC. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and the rule authorisation table. */ - public void getRulesAuthTable(String cardId, ResultCallback<EuiccRulesAuthTable> callback) { + public void requestRulesAuthTable(String cardId, @CallbackExecutor Executor executor, + ResultCallback<EuiccRulesAuthTable> callback) { try { getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(), cardId, new IGetRulesAuthTableCallback.Stub() { @Override public void onComplete(int resultCode, EuiccRulesAuthTable rat) { - callback.onComplete(resultCode, rat); + executor.execute(() -> callback.onComplete(resultCode, rat)); } }); } catch (RemoteException e) { @@ -390,18 +412,20 @@ public class EuiccCardManager { } /** - * Gets the eUICC challenge for new profile downloading. + * Requests the eUICC challenge for new profile downloading. * * @param cardId The Id of the eUICC. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and the challenge. */ - public void getEuiccChallenge(String cardId, ResultCallback<byte[]> callback) { + public void requestEuiccChallenge(String cardId, @CallbackExecutor Executor executor, + ResultCallback<byte[]> callback) { try { getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(), cardId, new IGetEuiccChallengeCallback.Stub() { @Override public void onComplete(int resultCode, byte[] challenge) { - callback.onComplete(resultCode, challenge); + executor.execute(() -> callback.onComplete(resultCode, challenge)); } }); } catch (RemoteException e) { @@ -411,18 +435,20 @@ public class EuiccCardManager { } /** - * Gets the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading. + * Requests the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading. * * @param cardId The Id of the eUICC. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and the info1. */ - public void getEuiccInfo1(String cardId, ResultCallback<byte[]> callback) { + public void requestEuiccInfo1(String cardId, @CallbackExecutor Executor executor, + ResultCallback<byte[]> callback) { try { getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(), cardId, new IGetEuiccInfo1Callback.Stub() { @Override public void onComplete(int resultCode, byte[] info) { - callback.onComplete(resultCode, info); + executor.execute(() -> callback.onComplete(resultCode, info)); } }); } catch (RemoteException e) { @@ -435,15 +461,17 @@ public class EuiccCardManager { * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading. * * @param cardId The Id of the eUICC. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and the info2. */ - public void getEuiccInfo2(String cardId, ResultCallback<byte[]> callback) { + public void requestEuiccInfo2(String cardId, @CallbackExecutor Executor executor, + ResultCallback<byte[]> callback) { try { getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(), cardId, new IGetEuiccInfo2Callback.Stub() { @Override public void onComplete(int resultCode, byte[] info) { - callback.onComplete(resultCode, info); + executor.execute(() -> callback.onComplete(resultCode, info)); } }); } catch (RemoteException e) { @@ -466,12 +494,13 @@ public class EuiccCardManager { * GSMA RSP v2.0+. * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by * SM-DP+ server. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and a byte array which represents a * {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+. */ public void authenticateServer(String cardId, String matchingId, byte[] serverSigned1, byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate, - ResultCallback<byte[]> callback) { + @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) { try { getIEuiccCardController().authenticateServer( mContext.getOpPackageName(), @@ -484,7 +513,7 @@ public class EuiccCardManager { new IAuthenticateServerCallback.Stub() { @Override public void onComplete(int resultCode, byte[] response) { - callback.onComplete(resultCode, response); + executor.execute(() -> callback.onComplete(resultCode, response)); } }); } catch (RemoteException e) { @@ -505,11 +534,13 @@ public class EuiccCardManager { * SM-DP+ server. * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned * by SM-DP+ server. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and a byte array which represents a * {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+ */ public void prepareDownload(String cardId, @Nullable byte[] hashCc, byte[] smdpSigned2, - byte[] smdpSignature2, byte[] smdpCertificate, ResultCallback<byte[]> callback) { + byte[] smdpSignature2, byte[] smdpCertificate, @CallbackExecutor Executor executor, + ResultCallback<byte[]> callback) { try { getIEuiccCardController().prepareDownload( mContext.getOpPackageName(), @@ -521,7 +552,7 @@ public class EuiccCardManager { new IPrepareDownloadCallback.Stub() { @Override public void onComplete(int resultCode, byte[] response) { - callback.onComplete(resultCode, response); + executor.execute(() -> callback.onComplete(resultCode, response)); } }); } catch (RemoteException e) { @@ -535,11 +566,12 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and a byte array which represents a * {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+. */ public void loadBoundProfilePackage(String cardId, byte[] boundProfilePackage, - ResultCallback<byte[]> callback) { + @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) { try { getIEuiccCardController().loadBoundProfilePackage( mContext.getOpPackageName(), @@ -548,7 +580,7 @@ public class EuiccCardManager { new ILoadBoundProfilePackageCallback.Stub() { @Override public void onComplete(int resultCode, byte[] response) { - callback.onComplete(resultCode, response); + executor.execute(() -> callback.onComplete(resultCode, response)); } }); } catch (RemoteException e) { @@ -563,11 +595,12 @@ public class EuiccCardManager { * @param cardId The Id of the eUICC. * @param transactionId the transaction ID returned by SM-DP+ server. * @param reason the cancel reason. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and an byte[] which represents a * {@code CancelSessionResponse} defined in GSMA RSP v2.0+. */ public void cancelSession(String cardId, byte[] transactionId, @CancelReason int reason, - ResultCallback<byte[]> callback) { + @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) { try { getIEuiccCardController().cancelSession( mContext.getOpPackageName(), @@ -577,7 +610,7 @@ public class EuiccCardManager { new ICancelSessionCallback.Stub() { @Override public void onComplete(int resultCode, byte[] response) { - callback.onComplete(resultCode, response); + executor.execute(() -> callback.onComplete(resultCode, response)); } }); } catch (RemoteException e) { @@ -591,16 +624,17 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param events bits of the event types ({@link EuiccNotification.Event}) to list. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and the list of notifications. */ public void listNotifications(String cardId, @EuiccNotification.Event int events, - ResultCallback<EuiccNotification[]> callback) { + @CallbackExecutor Executor executor, ResultCallback<EuiccNotification[]> callback) { try { getIEuiccCardController().listNotifications(mContext.getOpPackageName(), cardId, events, new IListNotificationsCallback.Stub() { @Override public void onComplete(int resultCode, EuiccNotification[] notifications) { - callback.onComplete(resultCode, notifications); + executor.execute(() -> callback.onComplete(resultCode, notifications)); } }); } catch (RemoteException e) { @@ -614,16 +648,17 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param events bits of the event types ({@link EuiccNotification.Event}) to list. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and the list of notifications. */ public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events, - ResultCallback<EuiccNotification[]> callback) { + @CallbackExecutor Executor executor, ResultCallback<EuiccNotification[]> callback) { try { getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), cardId, events, new IRetrieveNotificationListCallback.Stub() { @Override public void onComplete(int resultCode, EuiccNotification[] notifications) { - callback.onComplete(resultCode, notifications); + executor.execute(() -> callback.onComplete(resultCode, notifications)); } }); } catch (RemoteException e) { @@ -637,16 +672,17 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param seqNumber the sequence number of the notification. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code and the notification. */ public void retrieveNotification(String cardId, int seqNumber, - ResultCallback<EuiccNotification> callback) { + @CallbackExecutor Executor executor, ResultCallback<EuiccNotification> callback) { try { getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), cardId, seqNumber, new IRetrieveNotificationCallback.Stub() { @Override public void onComplete(int resultCode, EuiccNotification notification) { - callback.onComplete(resultCode, notification); + executor.execute(() -> callback.onComplete(resultCode, notification)); } }); } catch (RemoteException e) { @@ -660,10 +696,11 @@ public class EuiccCardManager { * * @param cardId The Id of the eUICC. * @param seqNumber the sequence number of the notification. + * @param executor The executor through which the callback should be invode. * @param callback the callback to get the result code. */ public void removeNotificationFromList(String cardId, int seqNumber, - ResultCallback<Void> callback) { + @CallbackExecutor Executor executor, ResultCallback<Void> callback) { try { getIEuiccCardController().removeNotificationFromList( mContext.getOpPackageName(), @@ -672,7 +709,7 @@ public class EuiccCardManager { new IRemoveNotificationFromListCallback.Stub() { @Override public void onComplete(int resultCode) { - callback.onComplete(resultCode, null); + executor.execute(() -> callback.onComplete(resultCode, null)); } }); } catch (RemoteException e) { diff --git a/telephony/java/android/telephony/euicc/EuiccInfo.java b/telephony/java/android/telephony/euicc/EuiccInfo.java index 5bfff08f8d45..a4adf0591a00 100644 --- a/telephony/java/android/telephony/euicc/EuiccInfo.java +++ b/telephony/java/android/telephony/euicc/EuiccInfo.java @@ -23,9 +23,6 @@ import android.os.Parcelable; * Information about an eUICC chip/device. * * @see EuiccManager#getEuiccInfo - * @hide - * - * TODO(b/35851809): Make this public. */ // WARNING: Do not add any privacy-sensitive fields to this class (such as an eUICC identifier)! // This API is accessible to all applications. Privacy-sensitive fields should be returned in their @@ -45,12 +42,17 @@ public final class EuiccInfo implements Parcelable { } }; + @Nullable + private final String osVersion; + /** - * Version of the operating system running on the eUICC. This field is hardware-specific and is - * not guaranteed to match any particular format. + * Gets the version of the operating system running on the eUICC. This field is + * hardware-specific and is not guaranteed to match any particular format. */ @Nullable - public final String osVersion; + public String getOsVersion() { + return osVersion; + } public EuiccInfo(@Nullable String osVersion) { this.osVersion = osVersion; diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index 7f913ceb8165..1637c55c0adf 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -42,9 +42,6 @@ import java.lang.annotation.RetentionPolicy; * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}. * * <p>See {@link #isEnabled} before attempting to use these APIs. - * - * TODO(b/35851809): Make this public. - * @hide */ public class EuiccManager { @@ -56,6 +53,8 @@ public class EuiccManager { * * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if * {@link #isEnabled} is false. + * + * This is ued by non-LPA app to bring up LUI. */ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = @@ -69,8 +68,10 @@ public class EuiccManager { * * <p class="note">This is a protected intent that can only be sent * by the system. - * TODO(b/35851809): Make this a SystemApi. + * + * @hide */ + @SystemApi @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED"; @@ -78,12 +79,10 @@ public class EuiccManager { /** * Broadcast Action: The action sent to carrier app so it knows the carrier setup is not * completed. - * - * TODO(b/35851809): Make this a public API. */ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_NOTIFY_CARRIER_SETUP = - "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP"; + public static final String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE = + "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE"; /** * Intent action to provision an embedded subscription. @@ -95,8 +94,9 @@ public class EuiccManager { * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if * {@link #isEnabled} is false. * - * TODO(b/35851809): Make this a SystemApi. + * @hide */ + @SystemApi @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION"; @@ -143,9 +143,8 @@ public class EuiccManager { * Key for an extra set on {@link #getDownloadableSubscriptionMetadata} PendingIntent result * callbacks providing the downloadable subscription metadata. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ + @SystemApi public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION"; @@ -153,9 +152,8 @@ public class EuiccManager { * Key for an extra set on {@link #getDefaultDownloadableSubscriptionList} PendingIntent result * callbacks providing the list of available downloadable subscriptions. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ + @SystemApi public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS"; @@ -201,6 +199,7 @@ public class EuiccManager { * Euicc OTA update status which can be got by {@link #getOtaStatus} * @hide */ + @SystemApi @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"EUICC_OTA_"}, value = { EUICC_OTA_IN_PROGRESS, @@ -215,15 +214,37 @@ public class EuiccManager { /** * An OTA is in progress. During this time, the eUICC is not available and the user may lose * network access. + * @hide */ + @SystemApi public static final int EUICC_OTA_IN_PROGRESS = 1; - /** The OTA update failed. */ + + /** + * The OTA update failed. + * @hide + */ + @SystemApi public static final int EUICC_OTA_FAILED = 2; - /** The OTA update finished successfully. */ + + /** + * The OTA update finished successfully. + * @hide + */ + @SystemApi public static final int EUICC_OTA_SUCCEEDED = 3; - /** The OTA update not needed since current eUICC OS is latest. */ + + /** + * The OTA update not needed since current eUICC OS is latest. + * @hide + */ + @SystemApi public static final int EUICC_OTA_NOT_NEEDED = 4; - /** The OTA status is unavailable since eUICC service is unavailable. */ + + /** + * The OTA status is unavailable since eUICC service is unavailable. + * @hide + */ + @SystemApi public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; private final Context mContext; @@ -276,8 +297,10 @@ public class EuiccManager { * * @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready, * {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned. - * TODO(b/35851809): Make this a SystemApi. + * + * @hide */ + @SystemApi public int getOtaStatus() { if (!isEnabled()) { return EUICC_OTA_STATUS_UNAVAILABLE; @@ -292,7 +315,7 @@ public class EuiccManager { /** * Attempt to download the given {@link DownloadableSubscription}. * - * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, + * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, * or the calling app must be authorized to manage both the currently-active subscription and * the subscription to be downloaded according to the subscription metadata. Without the former, * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback @@ -354,14 +377,16 @@ public class EuiccManager { * * <p>To be called by the LUI upon completion of a resolvable error flow. * + * <p>Requires that the calling app has the + * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. + * * @param resolutionIntent The original intent used to start the LUI. * @param resolutionExtras Resolution-specific extras depending on the result of the resolution. * For example, this may indicate whether the user has consented or may include the input * they provided. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ + @SystemApi public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) { if (!isEnabled()) { PendingIntent callbackIntent = @@ -395,9 +420,8 @@ public class EuiccManager { * @param subscription the subscription which needs metadata filled in * @param callbackIntent a PendingIntent to launch when the operation completes. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ + @SystemApi public void getDownloadableSubscriptionMetadata( DownloadableSubscription subscription, PendingIntent callbackIntent) { if (!isEnabled()) { @@ -426,9 +450,8 @@ public class EuiccManager { * * @param callbackIntent a PendingIntent to launch when the operation completes. * @hide - * - * TODO(b/35851809): Make this a SystemApi. */ + @SystemApi public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) { if (!isEnabled()) { sendUnavailableError(callbackIntent); @@ -468,7 +491,7 @@ public class EuiccManager { * * <p>Requires that the calling app has carrier privileges according to the metadata of the * profile to be deleted, or the - * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. + * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. * * @param subscriptionId the ID of the subscription to delete. * @param callbackIntent a PendingIntent to launch when the operation completes. @@ -489,7 +512,7 @@ public class EuiccManager { /** * Switch to (enable) the given subscription. * - * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, + * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, * or the calling app must be authorized to manage both the currently-active subscription and * the subscription to be enabled according to the subscription metadata. Without the former, * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback @@ -599,11 +622,7 @@ public class EuiccManager { } } - /** - * @hide - */ - @TestApi - protected IEuiccController getIEuiccController() { + private static IEuiccController getIEuiccController() { return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller")); } } diff --git a/telephony/java/android/telephony/euicc/EuiccNotification.java b/telephony/java/android/telephony/euicc/EuiccNotification.java index ef3c1ce8cf3b..43a770748260 100644 --- a/telephony/java/android/telephony/euicc/EuiccNotification.java +++ b/telephony/java/android/telephony/euicc/EuiccNotification.java @@ -17,6 +17,7 @@ package android.telephony.euicc; import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -31,10 +32,9 @@ import java.util.Objects; * disabling, or deleting). * * @hide - * - * TODO(b/35851809): Make this a @SystemApi. */ -public class EuiccNotification implements Parcelable { +@SystemApi +public final class EuiccNotification implements Parcelable { /** Event */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "EVENT_" }, value = { @@ -43,6 +43,7 @@ public class EuiccNotification implements Parcelable { EVENT_DISABLE, EVENT_DELETE }) + /** @hide */ public @interface Event {} /** A profile is downloaded and installed. */ @@ -57,7 +58,7 @@ public class EuiccNotification implements Parcelable { /** A profile is deleted. */ public static final int EVENT_DELETE = 1 << 3; - /** Value of the bits of all above events */ + /** Value of the bits of all the events including install, enable, disable and delete. */ @Event public static final int ALL_EVENTS = EVENT_INSTALL | EVENT_ENABLE | EVENT_DISABLE | EVENT_DELETE; diff --git a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java index 7efe04364280..67ae983efeb0 100644 --- a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java +++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java @@ -16,6 +16,7 @@ package android.telephony.euicc; import android.annotation.IntDef; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.service.carrier.CarrierIdentifier; @@ -27,20 +28,21 @@ import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; /** * This represents the RAT (Rules Authorisation Table) stored on eUICC. - * * @hide - * - * TODO(b/35851809): Make this a @SystemApi. */ +@SystemApi public final class EuiccRulesAuthTable implements Parcelable { /** Profile policy rule flags */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = { POLICY_RULE_FLAG_CONSENT_REQUIRED }) + /** @hide */ public @interface PolicyRuleFlag {} /** User consent is required to install the profile. */ @@ -89,12 +91,14 @@ public final class EuiccRulesAuthTable implements Parcelable { * @throws ArrayIndexOutOfBoundsException If the {@code mPosition} is larger than the size * this table. */ - public Builder add(int policyRules, CarrierIdentifier[] carrierId, int policyRuleFlags) { + public Builder add(int policyRules, List<CarrierIdentifier> carrierId, int policyRuleFlags) { if (mPosition >= mPolicyRules.length) { throw new ArrayIndexOutOfBoundsException(mPosition); } mPolicyRules[mPosition] = policyRules; - mCarrierIds[mPosition] = carrierId; + if (carrierId != null && carrierId.size() > 0) { + mCarrierIds[mPosition] = carrierId.toArray(new CarrierIdentifier[carrierId.size()]); + } mPolicyRuleFlags[mPosition] = policyRuleFlags; mPosition++; return this; diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml index ec5a9c63e9c8..23a151c0350a 100644 --- a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml +++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml @@ -32,5 +32,8 @@ <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> + <service + android:name=".TestService" + android:exported="true" /> </application> </manifest> diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java index 1f061218a10c..4e7bb4cc101e 100644 --- a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java +++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java @@ -18,7 +18,6 @@ package com.android.frameworks.perftests.amteststestapp; import android.app.Activity; import android.os.Looper; -import android.os.MessageQueue; import com.android.frameworks.perftests.am.util.Constants; import com.android.frameworks.perftests.am.util.Utils; diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestService.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestService.java new file mode 100644 index 000000000000..b6534fc87ae3 --- /dev/null +++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestService.java @@ -0,0 +1,38 @@ +/* + * 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.frameworks.perftests.amteststestapp; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; + +import com.android.frameworks.perftests.am.util.Constants; +import com.android.frameworks.perftests.am.util.Utils; + +public class TestService extends Service { + @Override + public IBinder onBind(Intent intent) { + return new Binder(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Utils.sendTime(intent, Constants.TYPE_SERVICE_START); + return super.onStartCommand(intent, flags, startId); + } +} diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java index 661abe91337f..cf175e00b217 100644 --- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java @@ -16,8 +16,11 @@ package com.android.frameworks.perftests.am.tests; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; import android.perftests.utils.ManualBenchmarkState; import android.perftests.utils.PerfManualStatusReporter; import android.support.test.InstrumentationRegistry; @@ -26,13 +29,17 @@ import com.android.frameworks.perftests.am.util.TargetPackageUtils; import com.android.frameworks.perftests.am.util.TimeReceiver; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.function.LongSupplier; public class BasePerfTest { private static final String TAG = BasePerfTest.class.getSimpleName(); + private static final long AWAIT_SERVICE_CONNECT_MS = 2000; private TimeReceiver mTimeReceiver; @@ -52,14 +59,70 @@ public class BasePerfTest { TargetPackageUtils.killTargetPackage(mContext); } - protected Intent createIntent(String action) { + protected void addReceivedTimeNs(String type) { + mTimeReceiver.addTimeForTypeToQueue(type, System.nanoTime()); + } + + protected Intent createServiceIntent() { + final Intent intent = new Intent(); + intent.setClassName(TargetPackageUtils.PACKAGE_NAME, + TargetPackageUtils.SERVICE_NAME); + putTimeReceiverBinderExtra(intent); + return intent; + } + + protected ServiceConnection bindAndWaitForConnectedService() { + return bindAndWaitForConnectedService(0); + } + + protected ServiceConnection bindAndWaitForConnectedService(int flags) { + CountDownLatch countDownLatch = new CountDownLatch(1); + final ServiceConnection serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + countDownLatch.countDown(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + }; + + final Intent intent = createServiceIntent(); + final boolean success = mContext.bindService(intent, serviceConnection, + Context.BIND_AUTO_CREATE | flags); + Assert.assertTrue("Could not bind to service", success); + + try { + boolean connectedSuccess = countDownLatch.await(AWAIT_SERVICE_CONNECT_MS, + TimeUnit.MILLISECONDS); + Assert.assertTrue("Timeout when waiting for ServiceConnection.onServiceConnected()", + connectedSuccess); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + return serviceConnection; + } + + protected void unbindFromService(ServiceConnection serviceConnection) { + if (serviceConnection != null) { + mContext.unbindService(serviceConnection); + } + } + + protected Intent createBroadcastIntent(String action) { final Intent intent = new Intent(action); intent.addFlags( Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - intent.putExtras(mTimeReceiver.createReceiveTimeExtraBinder()); + putTimeReceiverBinderExtra(intent); return intent; } + protected void putTimeReceiverBinderExtra(Intent intent) { + intent.putExtras(mTimeReceiver.createReceiveTimeExtraBinder()); + } + private void setUpIteration() { mTimeReceiver.clear(); TargetPackageUtils.killTargetPackage(mContext); diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java index 795f49866f84..f7dab03f10ee 100644 --- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java @@ -33,7 +33,8 @@ public class BroadcastPerfTest extends BasePerfTest { runPerfFunction(() -> { startTargetPackage(); - final Intent intent = createIntent(Constants.ACTION_BROADCAST_MANIFEST_RECEIVE); + final Intent intent = createBroadcastIntent( + Constants.ACTION_BROADCAST_MANIFEST_RECEIVE); final long startTime = System.nanoTime(); @@ -48,7 +49,8 @@ public class BroadcastPerfTest extends BasePerfTest { @Test public void manifestBroadcastNotRunning() { runPerfFunction(() -> { - final Intent intent = createIntent(Constants.ACTION_BROADCAST_MANIFEST_RECEIVE); + final Intent intent = createBroadcastIntent( + Constants.ACTION_BROADCAST_MANIFEST_RECEIVE); final long startTime = System.nanoTime(); @@ -65,7 +67,8 @@ public class BroadcastPerfTest extends BasePerfTest { runPerfFunction(() -> { startTargetPackage(); - final Intent intent = createIntent(Constants.ACTION_BROADCAST_REGISTERED_RECEIVE); + final Intent intent = createBroadcastIntent( + Constants.ACTION_BROADCAST_REGISTERED_RECEIVE); final long startTime = System.nanoTime(); diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java new file mode 100644 index 000000000000..6d2935a148fd --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.frameworks.perftests.am.tests; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.frameworks.perftests.am.util.Constants; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ServiceBindPerfTest extends BasePerfTest { + /** + * Create and return a ServiceConnection that will add the current time with type + * Constants.TYPE_SERVICE_CONNECTED. + */ + private ServiceConnection createServiceConnectionReportTime() { + return new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + addReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + }; + } + + /** + * Try to bind to the service with the input parameters, throwing a RuntimeException with the + * errorMessage on failure. + */ + private void bindService(Intent intent, ServiceConnection serviceConnection, int flags) { + final boolean success = mContext.bindService(intent, serviceConnection, flags); + Assert.assertTrue("Could not bind to service", success); + } + + /** + * Benchmark time from Context.bindService() to Service.onBind() when target package is not + * running. + */ + @Test + public void bindServiceNotRunning() { + runPerfFunction(() -> { + final Intent intent = createServiceIntent(); + final ServiceConnection serviceConnection = createServiceConnectionReportTime(); + + final long startTimeNs = System.nanoTime(); + bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + try { + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED); + return endTimeNs - startTimeNs; + } finally { + unbindFromService(serviceConnection); + } + }); + } + + /** + * Benchmark time from Context.bindService() to Service.onBind() when target package is running. + */ + @Test + public void bindServiceRunning() { + runPerfFunction(() -> { + startTargetPackage(); + + final Intent intent = createServiceIntent(); + final ServiceConnection serviceConnection = createServiceConnectionReportTime(); + + final long startTimeNs = System.nanoTime(); + bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + try { + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED); + return endTimeNs - startTimeNs; + } finally { + unbindFromService(serviceConnection); + } + }); + } + + /** + * Benchmark time from Context.bindService() to Service.onBind() when service is already bound + * to. + */ + @Test + public void bindServiceAlreadyBound() { + runPerfFunction(() -> { + startTargetPackage(); + + final Intent intent = createServiceIntent(); + final ServiceConnection alreadyBoundServiceConnection = bindAndWaitForConnectedService(); + + try { + final ServiceConnection serviceConnection = createServiceConnectionReportTime(); + + final long startTimeNs = System.nanoTime(); + try { + bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED); + return endTimeNs - startTimeNs; + } finally { + unbindFromService(serviceConnection); + } + } finally { + unbindFromService(alreadyBoundServiceConnection); + } + }); + } + + /** + * Benchmark time from Context.bindService() (without BIND_ALLOW_OOM_MANAGEMENT) to + * Service.onBind() when service is already bound to with BIND_ALLOW_OOM_MANAGEMENT. + */ + @Test + public void bindServiceAllowOomManagement() { + runPerfFunction(() -> { + final Intent intentNoOom = createServiceIntent(); + final ServiceConnection serviceConnectionOom = bindAndWaitForConnectedService( + Context.BIND_ALLOW_OOM_MANAGEMENT); + + try { + final ServiceConnection serviceConnectionNoOom = + createServiceConnectionReportTime(); + try { + final long startTimeNs = System.nanoTime(); + bindService(intentNoOom, serviceConnectionNoOom, Context.BIND_AUTO_CREATE); + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED); + + return endTimeNs - startTimeNs; + } finally { + unbindFromService(serviceConnectionNoOom); + } + } finally { + unbindFromService(serviceConnectionOom); + } + }); + } +} diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java new file mode 100644 index 000000000000..626ee020542d --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java @@ -0,0 +1,127 @@ +/* + * 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.frameworks.perftests.am.tests; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.ServiceConnection; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.frameworks.perftests.am.util.Constants; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ServiceStartPerfTest extends BasePerfTest { + + /** + * Tries to start the service with the given intent, throwing a RuntimeException with the + * errorMessage on failure. + */ + private void startService(Intent intent) { + final ComponentName componentName = mContext.startService(intent); + Assert.assertNotNull("Could not start service", componentName); + } + + /** + * Benchmark time from Context.startService() to Service.onStartCommand() when target process is + * not running. + */ + @Test + public void startServiceNotRunning() { + runPerfFunction(() -> { + final Intent intent = createServiceIntent(); + + final long startTimeNs = System.nanoTime(); + + startService(intent); + + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START); + return endTimeNs - startTimeNs; + }); + } + + /** + * Benchmark time from Context.startService() to Service.onStartCommand() when target process is + * running. + */ + @Test + public void startServiceProcessRunning() { + runPerfFunction(() -> { + startTargetPackage(); + + final Intent intent = createServiceIntent(); + + final long startTimeNs = System.nanoTime(); + startService(intent); + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START); + + return endTimeNs - startTimeNs; + }); + } + + /** + * Benchmark time from Context.startService() to Service.onStartCommand() when service is + * already bound to. + */ + @Test + public void startServiceAlreadyBound() { + runPerfFunction(() -> { + final ServiceConnection alreadyBoundServiceConnection = + bindAndWaitForConnectedService(); + try { + final Intent intent = createServiceIntent(); + + final long startTimeNs = System.nanoTime(); + startService(intent); + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START); + + return endTimeNs - startTimeNs; + } finally { + unbindFromService(alreadyBoundServiceConnection); + } + }); + } + + /** + * Benchmark time from Context.startService() with FLAG_GRANT_READ_URI_PERMISSION to + * Service.onStartCommand() when target process is running. + */ + @Test + public void startServiceProcessRunningReadUriPermission() { + runPerfFunction(() -> { + final ServiceConnection alreadyBoundServiceConnection = + bindAndWaitForConnectedService(); + try { + final Intent intent = createServiceIntent(); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + final long startTimeNs = System.nanoTime(); + startService(intent); + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START); + + return endTimeNs - startTimeNs; + } finally { + unbindFromService(alreadyBoundServiceConnection); + } + }); + } +} diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java index 26a8e7b8945e..3db8abce90da 100644 --- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java @@ -27,6 +27,7 @@ public class TargetPackageUtils { public static final String PACKAGE_NAME = "com.android.frameworks.perftests.amteststestapp"; public static final String ACTIVITY_NAME = PACKAGE_NAME + ".TestActivity"; + public static final String SERVICE_NAME = PACKAGE_NAME + ".TestService"; private static final long WAIT_TIME_MS = 100L; diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java index 9cf6ee7c91d5..a86a5c7ac3d4 100644 --- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java @@ -45,20 +45,23 @@ public class TimeReceiver { } } + public void addTimeForTypeToQueue(String type, long timeNs) { + if (type == null) { + throw new IllegalArgumentException("type is null when adding time to queue"); + } + if (timeNs < 0) { + throw new RuntimeException( + "time is negative/non-existant (" + timeNs + ") when adding time to queue"); + } + mQueue.add(new ReceivedMessage(type, timeNs)); + } + public Bundle createReceiveTimeExtraBinder() { Bundle extras = new Bundle(); extras.putBinder(Constants.EXTRA_RECEIVER_CALLBACK, new ITimeReceiverCallback.Stub() { @Override public void sendTime(String type, long timeNs) throws RemoteException { - if (type == null) { - throw new RuntimeException("receivedType is null"); - } - if (timeNs < 0) { - throw new RuntimeException( - "receivedTime is negative/non-existant: " + timeNs); - } - Log.i(TAG, type + " " + timeNs); - mQueue.add(new ReceivedMessage(type, timeNs)); + addTimeForTypeToQueue(type, timeNs); } }); return extras; diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java index f35c2fd38dbc..ffb3f84cee51 100644 --- a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java +++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java @@ -19,6 +19,9 @@ package com.android.frameworks.perftests.am.util; public class Constants { public static final String TYPE_TARGET_PACKAGE_START = "target_package_start"; public static final String TYPE_BROADCAST_RECEIVE = "broadcast_receive"; + public static final String TYPE_SERVICE_BIND = "service_bind"; + public static final String TYPE_SERVICE_START = "service_start"; + public static final String TYPE_SERVICE_CONNECTED = "service_connection_connect"; public static final String ACTION_BROADCAST_MANIFEST_RECEIVE = "com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE"; diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml index d62ef9ec210c..2591aaf8f1a6 100644 --- a/tests/FrameworkPerf/AndroidManifest.xml +++ b/tests/FrameworkPerf/AndroidManifest.xml @@ -1,6 +1,5 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworkperf"> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-sdk android:minSdkVersion="5" /> diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml index 8697f1b085bf..c6824ecea976 100644 --- a/tests/OneMedia/AndroidManifest.xml +++ b/tests/OneMedia/AndroidManifest.xml @@ -5,7 +5,6 @@ android:versionName="1.0" > <uses-sdk android:minSdkVersion="19"/> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |