diff options
298 files changed, 6489 insertions, 2066 deletions
diff --git a/Android.bp b/Android.bp index 7bdedc79943d..8947cfae00bb 100644 --- a/Android.bp +++ b/Android.bp @@ -602,6 +602,7 @@ java_defaults { "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl", "telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl", "telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl", + "telephony/java/com/android/internal/telephony/ISetOpportunisticDataCallback.aidl", "telephony/java/com/android/internal/telephony/ISms.aidl", "telephony/java/com/android/internal/telephony/ISub.aidl", "telephony/java/com/android/internal/telephony/IOns.aidl", @@ -741,10 +742,8 @@ java_defaults { static_libs: [ "apex_aidl_interface-java", - "networkstack-aidl-interfaces-java", "framework-protos", "game-driver-protos", - "mediaplayer2-protos", "android.hidl.base-V1.0-java", "android.hardware.cas-V1.1-java", "android.hardware.cas-V1.0-java", @@ -804,6 +803,7 @@ filegroup { srcs: [ "core/java/android/os/IStatsCompanionService.aidl", "core/java/android/os/IStatsManager.aidl", + "core/java/android/os/IStatsPullerCallback.aidl", ], } @@ -1187,52 +1187,6 @@ packages_to_document = [ "org/apache/http/params", ] -// The since flag (-since N.xml API_LEVEL) is used to add API Level information -// to the reference documentation. Must be in order of oldest to newest. -// -// Conscrypt (com.android.org.conscrypt) is an implementation detail and should -// not be referenced in the documentation. -framework_docs_args = "-android -manifest $(location core/res/AndroidManifest.xml) " + - "-hidePackage com.android.internal " + - "-hidePackage com.android.internal.util " + - "-hidePackage com.android.okhttp " + - "-hidePackage com.android.org.conscrypt " + - "-hidePackage com.android.server " + - "-since $(location 1/public/api/android.xml) 1 " + - "-since $(location 2/public/api/android.xml) 2 " + - "-since $(location 3/public/api/android.xml) 3 " + - "-since $(location 4/public/api/android.xml) 4 " + - "-since $(location 5/public/api/android.xml) 5 " + - "-since $(location 6/public/api/android.xml) 6 " + - "-since $(location 7/public/api/android.xml) 7 " + - "-since $(location 8/public/api/android.xml) 8 " + - "-since $(location 9/public/api/android.xml) 9 " + - "-since $(location 10/public/api/android.xml) 10 " + - "-since $(location 11/public/api/android.xml) 11 " + - "-since $(location 12/public/api/android.xml) 12 " + - "-since $(location 13/public/api/android.xml) 13 " + - "-since $(location 14/public/api/android.txt) 14 " + - "-since $(location 15/public/api/android.txt) 15 " + - "-since $(location 16/public/api/android.txt) 16 " + - "-since $(location 17/public/api/android.txt) 17 " + - "-since $(location 18/public/api/android.txt) 18 " + - "-since $(location 19/public/api/android.txt) 19 " + - "-since $(location 20/public/api/android.txt) 20 " + - "-since $(location 21/public/api/android.txt) 21 " + - "-since $(location 22/public/api/android.txt) 22 " + - "-since $(location 23/public/api/android.txt) 23 " + - "-since $(location 24/public/api/android.txt) 24 " + - "-since $(location 25/public/api/android.txt) 25 " + - "-since $(location 26/public/api/android.txt) 26 " + - "-since $(location 27/public/api/android.txt) 27 " + - "-since $(location 28/public/api/android.txt) 28 " + - "-since $(location api/current.txt) Q " + - "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + - "-overview $(location core/java/overview.html) " + - // Federate Support Library references against local API file. - "-federate SupportLib https://developer.android.com " + - "-federationapi SupportLib $(location current/support-api.txt) " - framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " + "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + "-overview $(location core/java/overview.html) " + diff --git a/Android.mk b/Android.mk index 9c65948f4838..9a91dd1c491a 100644 --- a/Android.mk +++ b/Android.mk @@ -72,6 +72,9 @@ $(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip $(hide) mkdir -p $(OUT_DOCS)/offline-sdk ( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1 +.PHONY: docs offline-sdk-docs +docs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp + # Run this for checkbuild checkbuild: doc-comment-check-docs # Check comment when you are updating the API diff --git a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java index ec46a75bd807..c506aec624d1 100644 --- a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java +++ b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java @@ -64,7 +64,7 @@ public class TextClassifierPerfTest { Context context = InstrumentationRegistry.getTargetContext(); TextClassificationManager textClassificationManager = context.getSystemService(TextClassificationManager.class); - mTextClassifier = textClassificationManager.getTextClassifier(); + mTextClassifier = textClassificationManager.getLocalTextClassifier(); } @Test diff --git a/api/current.txt b/api/current.txt index 0002a0cba9cf..16bc8c8cf804 100644 --- a/api/current.txt +++ b/api/current.txt @@ -80,7 +80,6 @@ package android { field public static final String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE"; field public static final String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; - field public static final String GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY = "android.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY"; field public static final String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; field @Deprecated public static final String GET_TASKS = "android.permission.GET_TASKS"; field public static final String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH"; @@ -131,6 +130,7 @@ package android { field public static final String REQUEST_DELETE_PACKAGES = "android.permission.REQUEST_DELETE_PACKAGES"; field public static final String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES"; + field public static final String REQUEST_SCREEN_LOCK_COMPLEXITY = "android.permission.REQUEST_SCREEN_LOCK_COMPLEXITY"; field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES"; field public static final String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE"; field public static final String SEND_SMS = "android.permission.SEND_SMS"; @@ -6646,7 +6646,7 @@ package android.app.admin { method @Nullable public CharSequence getOrganizationName(@NonNull android.content.ComponentName); method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(@NonNull android.content.ComponentName); method @NonNull public android.app.admin.DevicePolicyManager getParentProfileInstance(@NonNull android.content.ComponentName); - method @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) public int getPasswordComplexity(); + method @RequiresPermission(android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY) public int getPasswordComplexity(); method public long getPasswordExpiration(@Nullable android.content.ComponentName); method public long getPasswordExpirationTimeout(@Nullable android.content.ComponentName); method public int getPasswordHistoryLength(@Nullable android.content.ComponentName); @@ -6840,7 +6840,7 @@ package android.app.admin { field public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION"; field public static final String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES"; field public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN"; - field @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY"; + field @RequiresPermission(android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY"; field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE"; field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME"; @@ -9517,7 +9517,6 @@ package android.content { method public static void cancelSync(android.content.SyncRequest); method @Nullable public final android.net.Uri canonicalize(@NonNull android.net.Uri); method public final int delete(@RequiresPermission.Write @NonNull android.net.Uri, @Nullable String, @Nullable String[]); - method public android.os.Bundle getCache(android.net.Uri); method @Deprecated public static android.content.SyncInfo getCurrentSync(); method public static java.util.List<android.content.SyncInfo> getCurrentSyncs(); method public static int getIsSyncable(android.accounts.Account, String); @@ -9548,7 +9547,6 @@ package android.content { method @Nullable public final android.content.res.AssetFileDescriptor openTypedAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException; method @Nullable public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle) throws java.io.FileNotFoundException; method @Nullable public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException; - method public void putCache(android.net.Uri, android.os.Bundle); method @Nullable public final android.database.Cursor query(@RequiresPermission.Read @NonNull android.net.Uri, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String); method @Nullable public final android.database.Cursor query(@RequiresPermission.Read @NonNull android.net.Uri, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable android.os.CancellationSignal); method @Nullable public final android.database.Cursor query(@RequiresPermission.Read @NonNull android.net.Uri, @Nullable String[], @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal); @@ -11482,7 +11480,6 @@ package android.content.pm { method public void setOriginatingUri(@Nullable android.net.Uri); method public void setReferrerUri(@Nullable android.net.Uri); method public void setSize(long); - method public void setStaged(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionParams> CREATOR; field public static final int MODE_FULL_INSTALL = 1; // 0x1 @@ -15409,28 +15406,38 @@ package android.graphics.drawable { method public float getGradientCenterY(); method public float getGradientRadius(); method public int getGradientType(); + method public int getInnerRadius(); + method public float getInnerRadiusRatio(); method public int getOpacity(); method public android.graphics.drawable.GradientDrawable.Orientation getOrientation(); method public int getShape(); + method public int getThickness(); + method public float getThicknessRatio(); method public boolean getUseLevel(); method public void setAlpha(int); method public void setColor(@ColorInt int); method public void setColor(@Nullable android.content.res.ColorStateList); method public void setColorFilter(@Nullable android.graphics.ColorFilter); method public void setColors(@ColorInt int[]); + method public void setColors(@ColorInt int[], @Nullable float[]); method public void setCornerRadii(@Nullable float[]); method public void setCornerRadius(float); method public void setDither(boolean); method public void setGradientCenter(float, float); method public void setGradientRadius(float); method public void setGradientType(int); + method public void setInnerRadius(int); + method public void setInnerRadiusRatio(float); method public void setOrientation(android.graphics.drawable.GradientDrawable.Orientation); + method public void setPadding(int, int, int, int); method public void setShape(int); method public void setSize(int, int); method public void setStroke(int, @ColorInt int); method public void setStroke(int, android.content.res.ColorStateList); method public void setStroke(int, @ColorInt int, float, float); method public void setStroke(int, android.content.res.ColorStateList, float, float); + method public void setThickness(int); + method public void setThicknessRatio(float); method public void setUseLevel(boolean); field public static final int LINE = 2; // 0x2 field public static final int LINEAR_GRADIENT = 0; // 0x0 @@ -22667,6 +22674,7 @@ package android.location { field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1 field public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2; // 0x2 field public static final int MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0 + field public static final int STATE_2ND_CODE_LOCK = 65536; // 0x10000 field public static final int STATE_BDS_D2_BIT_SYNC = 256; // 0x100 field public static final int STATE_BDS_D2_SUBFRAME_SYNC = 512; // 0x200 field public static final int STATE_BIT_SYNC = 2; // 0x2 @@ -30111,10 +30119,14 @@ package android.net.wifi.aware { method public void onIdentityChanged(byte[]); } - public final class PeerHandle implements android.os.Parcelable { + public final class ParcelablePeerHandle extends android.net.wifi.aware.PeerHandle implements android.os.Parcelable { + ctor public ParcelablePeerHandle(android.net.wifi.aware.PeerHandle); method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.net.wifi.aware.PeerHandle> CREATOR; + field public static final android.os.Parcelable.Creator<android.net.wifi.aware.ParcelablePeerHandle> CREATOR; + } + + public class PeerHandle { } public final class PublishConfig implements android.os.Parcelable { @@ -44369,6 +44381,7 @@ package android.telephony { method public int getChannelNumber(); method public String getMccString(); method public String getMncString(); + method public long getNci(); method public int getPci(); method public int getTac(); method public void writeToParcel(android.os.Parcel, int); @@ -45229,6 +45242,9 @@ package android.telephony { field public static final int PHONE_TYPE_GSM = 1; // 0x1 field public static final int PHONE_TYPE_NONE = 0; // 0x0 field public static final int PHONE_TYPE_SIP = 3; // 0x3 + field public static final int SET_OPPORTUNISTIC_SUB_INVALID_PARAMETER = 2; // 0x2 + field public static final int SET_OPPORTUNISTIC_SUB_SUCCESS = 0; // 0x0 + field public static final int SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED = 1; // 0x1 field public static final int SIM_STATE_ABSENT = 1; // 0x1 field public static final int SIM_STATE_CARD_IO_ERROR = 8; // 0x8 field public static final int SIM_STATE_CARD_RESTRICTED = 9; // 0x9 @@ -45593,6 +45609,483 @@ package android.telephony.gsm { } +package android.telephony.ims { + + public class Rcs1To1Thread extends android.telephony.ims.RcsThread { + method @WorkerThread public long getFallbackThreadId() throws android.telephony.ims.RcsMessageStoreException; + method @NonNull @WorkerThread public android.telephony.ims.RcsParticipant getRecipient() throws android.telephony.ims.RcsMessageStoreException; + method public boolean isGroup(); + method @WorkerThread public void setFallbackThreadId(long) throws android.telephony.ims.RcsMessageStoreException; + } + + public abstract class RcsEvent { + ctor protected RcsEvent(long); + method public long getTimestamp(); + } + + public final class RcsEventQueryParams implements android.os.Parcelable { + method public int describeContents(); + method @android.telephony.ims.RcsEventQueryParams.EventType public int getEventType(); + method public int getLimit(); + method public boolean getSortDirection(); + method public int getSortingProperty(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int ALL_EVENTS = -1; // 0xffffffff + field public static final int ALL_GROUP_THREAD_EVENTS = 0; // 0x0 + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryParams> CREATOR; + field public static final int GROUP_THREAD_ICON_CHANGED_EVENT = 8; // 0x8 + field public static final int GROUP_THREAD_NAME_CHANGED_EVENT = 16; // 0x10 + field public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT = 2; // 0x2 + field public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT = 4; // 0x4 + field public static final int PARTICIPANT_ALIAS_CHANGED_EVENT = 1; // 0x1 + field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 + field public static final int SORT_BY_TIMESTAMP = 1; // 0x1 + } + + public static class RcsEventQueryParams.Builder { + ctor public RcsEventQueryParams.Builder(); + method public android.telephony.ims.RcsEventQueryParams build(); + method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setEventType(@android.telephony.ims.RcsEventQueryParams.EventType int); + method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setGroupThread(@NonNull android.telephony.ims.RcsGroupThread); + method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; + method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortDirection(boolean); + method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortProperty(@android.telephony.ims.RcsEventQueryParams.SortingProperty int); + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.ALL_EVENTS, android.telephony.ims.RcsEventQueryParams.ALL_GROUP_THREAD_EVENTS, android.telephony.ims.RcsEventQueryParams.PARTICIPANT_ALIAS_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_JOINED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_LEFT_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_NAME_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_ICON_CHANGED_EVENT}) public static @interface RcsEventQueryParams.EventType { + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsEventQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsEventQueryParams.SortingProperty { + } + + public final class RcsEventQueryResult implements android.os.Parcelable { + method public int describeContents(); + method public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); + method public java.util.List<android.telephony.ims.RcsEvent> getEvents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryResult> CREATOR; + } + + public final class RcsFileTransferCreationParams implements android.os.Parcelable { + method public int describeContents(); + method public String getContentMimeType(); + method public android.net.Uri getContentUri(); + method public long getFileSize(); + method @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus(); + method public int getHeight(); + method public long getMediaDuration(); + method public String getPreviewMimeType(); + method public android.net.Uri getPreviewUri(); + method public String getRcsFileTransferSessionId(); + method public long getTransferOffset(); + method public int getWidth(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsFileTransferCreationParams> CREATOR; + } + + public class RcsFileTransferCreationParams.Builder { + ctor public RcsFileTransferCreationParams.Builder(); + method public android.telephony.ims.RcsFileTransferCreationParams build(); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentMimeType(String); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentUri(android.net.Uri); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileSize(long); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferSessionId(String); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setHeight(int); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setMediaDuration(long); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewMimeType(String); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewUri(android.net.Uri); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setTransferOffset(long); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setWidth(int); + } + + public class RcsFileTransferPart { + method @WorkerThread @Nullable public String getContentMimeType() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public android.net.Uri getContentUri() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public long getFileSize() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public String getFileTransferSessionId() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public int getHeight() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public long getLength() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public String getPreviewMimeType() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public android.net.Uri getPreviewUri() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public long getTransferOffset() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public int getWidth() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setContentMimeType(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setContentUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setFileSize(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setFileTransferSessionId(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setHeight(int) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setLength(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setPreviewMimeType(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setPreviewUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setTransferOffset(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setWidth(int) throws android.telephony.ims.RcsMessageStoreException; + field public static final int DOWNLOADING = 6; // 0x6 + field public static final int DOWNLOADING_CANCELLED = 9; // 0x9 + field public static final int DOWNLOADING_FAILED = 8; // 0x8 + field public static final int DOWNLOADING_PAUSED = 7; // 0x7 + field public static final int DRAFT = 1; // 0x1 + field public static final int NOT_SET = 0; // 0x0 + field public static final int SENDING = 2; // 0x2 + field public static final int SENDING_CANCELLED = 5; // 0x5 + field public static final int SENDING_FAILED = 4; // 0x4 + field public static final int SENDING_PAUSED = 3; // 0x3 + field public static final int SUCCEEDED = 10; // 0xa + } + + @IntDef({android.telephony.ims.RcsFileTransferPart.DRAFT, android.telephony.ims.RcsFileTransferPart.SENDING, android.telephony.ims.RcsFileTransferPart.SENDING_PAUSED, android.telephony.ims.RcsFileTransferPart.SENDING_FAILED, android.telephony.ims.RcsFileTransferPart.SENDING_CANCELLED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_PAUSED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_FAILED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_CANCELLED, android.telephony.ims.RcsFileTransferPart.SUCCEEDED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsFileTransferPart.RcsFileTransferStatus { + } + + public class RcsGroupThread extends android.telephony.ims.RcsThread { + method @WorkerThread public void addParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public android.net.Uri getConferenceUri() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable public android.net.Uri getGroupIcon() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public String getGroupName() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public android.telephony.ims.RcsParticipant getOwner() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public java.util.Set<android.telephony.ims.RcsParticipant> getParticipants() throws android.telephony.ims.RcsMessageStoreException; + method public boolean isGroup(); + method @WorkerThread public void removeParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public void setConferenceUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setGroupIcon(@Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setGroupName(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setOwner(@Nullable android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; + } + + public abstract class RcsGroupThreadEvent extends android.telephony.ims.RcsEvent { + method @NonNull public android.telephony.ims.RcsParticipant getOriginatingParticipant(); + method @NonNull public android.telephony.ims.RcsGroupThread getRcsGroupThread(); + } + + public final class RcsGroupThreadIconChangedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable { + ctor public RcsGroupThreadIconChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable android.net.Uri); + method public int describeContents(); + method @Nullable public android.net.Uri getNewIcon(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadIconChangedEvent> CREATOR; + } + + public final class RcsGroupThreadNameChangedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable { + ctor public RcsGroupThreadNameChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable String); + method public int describeContents(); + method @Nullable public String getNewName(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadNameChangedEvent> CREATOR; + } + + public final class RcsGroupThreadParticipantJoinedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable { + ctor public RcsGroupThreadParticipantJoinedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant); + method public int describeContents(); + method public android.telephony.ims.RcsParticipant getJoinedParticipant(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadParticipantJoinedEvent> CREATOR; + } + + public final class RcsGroupThreadParticipantLeftEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable { + ctor public RcsGroupThreadParticipantLeftEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant); + method public int describeContents(); + method @NonNull public android.telephony.ims.RcsParticipant getLeavingParticipantId(); + method public void persist() throws android.telephony.ims.RcsMessageStoreException; + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadParticipantLeftEvent> CREATOR; + } + + public class RcsIncomingMessage extends android.telephony.ims.RcsMessage { + method @WorkerThread public long getArrivalTimestamp() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public android.telephony.ims.RcsParticipant getSenderParticipant() throws android.telephony.ims.RcsMessageStoreException; + method public boolean isIncoming(); + method @WorkerThread public void setArrivalTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; + } + + public final class RcsIncomingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable { + method public int describeContents(); + method public long getArrivalTimestamp(); + method public long getSeenTimestamp(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsIncomingMessageCreationParams> CREATOR; + } + + public static class RcsIncomingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder { + ctor public RcsIncomingMessageCreationParams.Builder(long, long, int); + method public android.telephony.ims.RcsIncomingMessageCreationParams build(); + method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setArrivalTimestamp(long); + method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSeenTimestamp(long); + method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSenderParticipant(android.telephony.ims.RcsParticipant); + } + + public class RcsManager { + method public android.telephony.ims.RcsMessageStore getRcsMessageStore(); + } + + public abstract class RcsMessage { + method @NonNull @WorkerThread public java.util.Set<android.telephony.ims.RcsFileTransferPart> getFileTransferParts() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public double getLatitude() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public double getLongitude() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public long getOriginationTimestamp() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public String getRcsMessageId() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException; + method public int getSubscriptionId() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public String getText() throws android.telephony.ims.RcsMessageStoreException; + method @NonNull @WorkerThread public android.telephony.ims.RcsFileTransferPart insertFileTransfer(android.telephony.ims.RcsFileTransferCreationParams) throws android.telephony.ims.RcsMessageStoreException; + method public abstract boolean isIncoming(); + method @WorkerThread public void removeFileTransferPart(@NonNull android.telephony.ims.RcsFileTransferPart) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setLatitude(double) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setLongitude(double) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setOriginationTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setRcsMessageId(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setSubscriptionId(int) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setText(String) throws android.telephony.ims.RcsMessageStoreException; + field public static final int DRAFT = 1; // 0x1 + field public static final int FAILED = 6; // 0x6 + field public static final double LOCATION_NOT_SET = 4.9E-324; + field public static final int NOT_SET = 0; // 0x0 + field public static final int QUEUED = 2; // 0x2 + field public static final int RECEIVED = 7; // 0x7 + field public static final int RETRYING = 5; // 0x5 + field public static final int SEEN = 9; // 0x9 + field public static final int SENDING = 3; // 0x3 + field public static final int SENT = 4; // 0x4 + } + + @IntDef({android.telephony.ims.RcsMessage.DRAFT, android.telephony.ims.RcsMessage.QUEUED, android.telephony.ims.RcsMessage.SENDING, android.telephony.ims.RcsMessage.SENT, android.telephony.ims.RcsMessage.RETRYING, android.telephony.ims.RcsMessage.FAILED, android.telephony.ims.RcsMessage.RECEIVED, android.telephony.ims.RcsMessage.SEEN}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsMessage.RcsMessageStatus { + } + + public class RcsMessageCreationParams { + ctor protected RcsMessageCreationParams(android.telephony.ims.RcsMessageCreationParams.Builder); + method public double getLatitude(); + method public double getLongitude(); + method public int getMessageStatus(); + method public long getOriginationTimestamp(); + method @Nullable public String getRcsMessageGlobalId(); + method public int getSubId(); + method @Nullable public String getText(); + } + + public static class RcsMessageCreationParams.Builder { + method public android.telephony.ims.RcsMessageCreationParams build(); + method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLatitude(double); + method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLongitude(double); + method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setRcsMessageId(String); + method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int); + method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setText(String); + } + + public final class RcsMessageQueryParams implements android.os.Parcelable { + method public int describeContents(); + method public int getFileTransferPresence(); + method public int getLimit(); + method public String getMessageLike(); + method public int getMessageType(); + method public boolean getSortDirection(); + method @android.telephony.ims.RcsMessageQueryParams.SortingProperty public int getSortingProperty(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryParams> CREATOR; + field public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 8; // 0x8 + field public static final int MESSAGES_WITH_FILE_TRANSFERS = 4; // 0x4 + field public static final int MESSAGE_TYPE_INCOMING = 1; // 0x1 + field public static final int MESSAGE_TYPE_OUTGOING = 2; // 0x2 + field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 + field public static final int SORT_BY_TIMESTAMP = 1; // 0x1 + } + + public static class RcsMessageQueryParams.Builder { + ctor public RcsMessageQueryParams.Builder(); + method public android.telephony.ims.RcsMessageQueryParams build(); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setFileTransferPresence(int); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageLike(String); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageType(int); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortDirection(boolean); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortProperty(@android.telephony.ims.RcsMessageQueryParams.SortingProperty int); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setThread(@Nullable android.telephony.ims.RcsThread); + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsMessageQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsMessageQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsMessageQueryParams.SortingProperty { + } + + public final class RcsMessageQueryResult implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); + method @NonNull public java.util.List<android.telephony.ims.RcsMessage> getMessages(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryResult> CREATOR; + } + + public final class RcsMessageSnippet implements android.os.Parcelable { + method public int describeContents(); + method @android.telephony.ims.RcsMessage.RcsMessageStatus public int getSnippetStatus(); + method @Nullable public String getSnippetText(); + method public long getSnippetTimestamp(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageSnippet> CREATOR; + } + + public class RcsMessageStore { + ctor public RcsMessageStore(); + method @WorkerThread @NonNull public android.telephony.ims.RcsGroupThread createGroupThread(@Nullable java.util.List<android.telephony.ims.RcsParticipant>, @Nullable String, @Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.Rcs1To1Thread createRcs1To1Thread(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsParticipant createRcsParticipant(String, @Nullable String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void deleteThread(@NonNull android.telephony.ims.RcsThread) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@Nullable android.telephony.ims.RcsEventQueryParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@Nullable android.telephony.ims.RcsMessageQueryParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@Nullable android.telephony.ims.RcsParticipantQueryParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@Nullable android.telephony.ims.RcsThreadQueryParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public void persistRcsEvent(android.telephony.ims.RcsEvent) throws android.telephony.ims.RcsMessageStoreException; + } + + public class RcsMessageStoreException extends java.lang.Exception { + ctor public RcsMessageStoreException(String); + } + + public class RcsOutgoingMessage extends android.telephony.ims.RcsMessage { + method @NonNull @WorkerThread public java.util.List<android.telephony.ims.RcsOutgoingMessageDelivery> getOutgoingDeliveries() throws android.telephony.ims.RcsMessageStoreException; + method public boolean isIncoming(); + } + + public final class RcsOutgoingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsOutgoingMessageCreationParams> CREATOR; + } + + public static class RcsOutgoingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder { + ctor public RcsOutgoingMessageCreationParams.Builder(long, int); + method public android.telephony.ims.RcsOutgoingMessageCreationParams build(); + } + + public class RcsOutgoingMessageDelivery { + method @WorkerThread public long getDeliveredTimestamp() throws android.telephony.ims.RcsMessageStoreException; + method @NonNull public android.telephony.ims.RcsOutgoingMessage getMessage(); + method @NonNull public android.telephony.ims.RcsParticipant getRecipient(); + method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setDeliveredTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException; + } + + public class RcsParticipant { + method @Nullable @WorkerThread public String getAlias() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public String getCanonicalAddress() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public String getContactId() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setAlias(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setContactId(String) throws android.telephony.ims.RcsMessageStoreException; + } + + public final class RcsParticipantAliasChangedEvent extends android.telephony.ims.RcsEvent implements android.os.Parcelable { + ctor public RcsParticipantAliasChangedEvent(long, @NonNull android.telephony.ims.RcsParticipant, @Nullable String); + method public int describeContents(); + method @Nullable public String getNewAlias(); + method @NonNull public android.telephony.ims.RcsParticipant getParticipantId(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantAliasChangedEvent> CREATOR; + } + + public final class RcsParticipantQueryParams implements android.os.Parcelable { + method public int describeContents(); + method public String getAliasLike(); + method public String getCanonicalAddressLike(); + method public int getLimit(); + method public boolean getSortDirection(); + method public int getSortingProperty(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryParams> CREATOR; + field public static final int SORT_BY_ALIAS = 1; // 0x1 + field public static final int SORT_BY_CANONICAL_ADDRESS = 2; // 0x2 + field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 + } + + public static class RcsParticipantQueryParams.Builder { + ctor public RcsParticipantQueryParams.Builder(); + method public android.telephony.ims.RcsParticipantQueryParams build(); + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setAliasLike(String); + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setCanonicalAddressLike(String); + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortDirection(boolean); + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortProperty(@android.telephony.ims.RcsParticipantQueryParams.SortingProperty int); + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setThread(android.telephony.ims.RcsThread); + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_ALIAS, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS}) public static @interface RcsParticipantQueryParams.SortingProperty { + } + + public final class RcsParticipantQueryResult implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); + method @NonNull public java.util.List<android.telephony.ims.RcsParticipant> getParticipants(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryResult> CREATOR; + } + + public final class RcsQueryContinuationToken implements android.os.Parcelable { + method public int describeContents(); + method @android.telephony.ims.RcsQueryContinuationToken.ContinuationTokenType public int getQueryType(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsQueryContinuationToken> CREATOR; + field public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0; // 0x0 + field public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1; // 0x1 + field public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2; // 0x2 + field public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3; // 0x3 + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsQueryContinuationToken.EVENT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.THREAD_QUERY_CONTINUATION_TOKEN_TYPE}) public static @interface RcsQueryContinuationToken.ContinuationTokenType { + } + + public abstract class RcsThread { + method @WorkerThread @NonNull public android.telephony.ims.RcsIncomingMessage addIncomingMessage(@NonNull android.telephony.ims.RcsIncomingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsOutgoingMessage addOutgoingMessage(@NonNull android.telephony.ims.RcsOutgoingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void deleteMessage(@NonNull android.telephony.ims.RcsMessage) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getMessages() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsMessageSnippet getSnippet() throws android.telephony.ims.RcsMessageStoreException; + method public abstract boolean isGroup(); + } + + public final class RcsThreadQueryParams implements android.os.Parcelable { + method public int describeContents(); + method public int getLimit(); + method public boolean getSortDirection(); + method @android.telephony.ims.RcsThreadQueryParams.SortingProperty public int getSortingProperty(); + method public int getThreadType(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryParams> CREATOR; + field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 + field public static final int SORT_BY_TIMESTAMP = 1; // 0x1 + field public static final int THREAD_TYPE_1_TO_1 = 2; // 0x2 + field public static final int THREAD_TYPE_GROUP = 1; // 0x1 + } + + public static class RcsThreadQueryParams.Builder { + ctor public RcsThreadQueryParams.Builder(); + method public android.telephony.ims.RcsThreadQueryParams build(); + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipant(@NonNull android.telephony.ims.RcsParticipant); + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipants(@NonNull java.util.List<android.telephony.ims.RcsParticipant>); + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortDirection(boolean); + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortProperty(@android.telephony.ims.RcsThreadQueryParams.SortingProperty int); + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setThreadType(int); + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsThreadQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsThreadQueryParams.SortingProperty { + } + + public final class RcsThreadQueryResult implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); + method @NonNull public java.util.List<android.telephony.ims.RcsThread> getThreads(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryResult> CREATOR; + } + +} + package android.telephony.mbms { public class DownloadProgressListener { @@ -56210,6 +56703,8 @@ package android.widget { method public int getMaxValue(); method public int getMinValue(); method public int getSelectionDividerHeight(); + method @ColorInt public int getTextColor(); + method @FloatRange(from=0.0, fromInclusive=false) public float getTextSize(); method public int getValue(); method public boolean getWrapSelectorWheel(); method public void setDisplayedValues(String[]); @@ -56220,6 +56715,8 @@ package android.widget { method public void setOnScrollListener(android.widget.NumberPicker.OnScrollListener); method public void setOnValueChangedListener(android.widget.NumberPicker.OnValueChangeListener); method public void setSelectionDividerHeight(@IntRange(from=0) @Px int); + method public void setTextColor(@ColorInt int); + method public void setTextSize(@FloatRange(from=0.0, fromInclusive=false) float); method public void setValue(int); method public void setWrapSelectorWheel(boolean); } diff --git a/api/system-current.txt b/api/system-current.txt index 7babf568bdb9..48a450a441a9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -641,7 +641,7 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String); method @Deprecated @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException; method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied(); - method public void setProfileOwnerCanAccessDeviceIdsForUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); + method @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIdsForUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED"; field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION"; @@ -1283,7 +1283,9 @@ package android.content { } public abstract class ContentResolver implements android.content.ContentInterface { + method public android.os.Bundle getCache(android.net.Uri); method public android.graphics.drawable.Drawable getTypeDrawable(String); + method public void putCache(android.net.Uri, android.os.Bundle); } public abstract class Context { @@ -1535,6 +1537,7 @@ package android.content.pm { method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]); method public void setInstallAsInstantApp(boolean); method public void setInstallAsVirtualPreload(); + method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged(); } public class PackageItemInfo { @@ -5779,6 +5782,16 @@ package android.provider { field public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"; } + public static interface DeviceConfig.Rollback { + field public static final String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout"; + field public static final String NAMESPACE = "rollback"; + } + + public static interface DeviceConfig.Runtime { + field public static final String NAMESPACE = "runtime"; + field public static final String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled"; + } + public static interface DeviceConfig.RuntimeNative { field public static final String NAMESPACE = "runtime_native"; } @@ -7024,6 +7037,7 @@ package android.telephony { method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers(); method public int getMultiSimPolicy(); method public boolean isAllCarriersAllowed(); + method public java.util.List<java.lang.Boolean> isCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>); method public void writeToParcel(android.os.Parcel, int); field public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1; // 0x1 field public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0; // 0x0 @@ -7753,6 +7767,7 @@ package android.telephony { method public void requestEmbeddedSubscriptionInfoListRefresh(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @NonNull java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean); field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI; field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff @@ -7839,6 +7854,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMultisimCarrierRestricted(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRebootRequiredForModemConfigChange(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle); diff --git a/api/test-current.txt b/api/test-current.txt index 695401164089..1a7e4cb83c52 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1785,12 +1785,19 @@ package android.provider { } public final class DeviceConfig { + method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static void addOnPropertyChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertyChangedListener); method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(String, String); + method public static void removeOnPropertyChangedListener(android.provider.DeviceConfig.OnPropertyChangedListener); method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static void resetToDefaults(int, @Nullable String); method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static boolean setProperty(String, String, String, boolean); + field public static final String NAMESPACE_AUTOFILL = "autofill"; field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture"; } + public static interface DeviceConfig.OnPropertyChangedListener { + method public void onPropertyChanged(String, String, String); + } + public static interface DeviceConfig.Privacy { field public static final String NAMESPACE = "privacy"; field public static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled"; @@ -2694,6 +2701,7 @@ package android.view.autofill { public final class AutofillManager { method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes"; + field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0 field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1 field public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 120000; // 0x1d4c0 } diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc index 1b3c32b20503..469c9646a4aa 100644 --- a/cmds/bootanimation/bootanim.rc +++ b/cmds/bootanimation/bootanim.rc @@ -2,7 +2,6 @@ service bootanim /system/bin/bootanimation class core animation user graphics group graphics audio - updatable disabled oneshot writepid /dev/stune/top-app/tasks diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index d6f045ea43fd..faf2053835fa 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -71,6 +71,7 @@ cc_defaults { "src/external/Perfetto.cpp", "src/external/Perfprofd.cpp", "src/external/StatsPuller.cpp", + "src/external/StatsCallbackPuller.cpp", "src/external/StatsCompanionServicePuller.cpp", "src/external/SubsystemSleepStatePuller.cpp", "src/external/PowerStatsPuller.cpp", diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp index 067b6eddf254..cca6d525ea16 100644 --- a/cmds/statsd/benchmark/metric_util.cpp +++ b/cmds/statsd/benchmark/metric_util.cpp @@ -367,7 +367,8 @@ sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const Stat sp<AlarmMonitor> periodicAlarmMonitor; sp<StatsLogProcessor> processor = new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; }); + timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; }, + [](const int&, const vector<int64_t>&) { return true; }); processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config); return processor; } @@ -393,4 +394,4 @@ int64_t StringToId(const string& str) { } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index dd18bd4cc8ad..653ef2ec9869 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -88,12 +88,15 @@ StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const std::function<bool(const ConfigKey&)>& sendBroadcast) + const std::function<bool(const ConfigKey&)>& sendBroadcast, + const std::function<bool( + const int&, const vector<int64_t>&)>& activateBroadcast) : mUidMap(uidMap), mPullerManager(pullerManager), mAnomalyAlarmMonitor(anomalyAlarmMonitor), mPeriodicAlarmMonitor(periodicAlarmMonitor), mSendBroadcast(sendBroadcast), + mSendActivationBroadcast(activateBroadcast), mTimeBaseNs(timeBaseNs), mLargestTimestampSeen(0), mLastTimestampSeen(0) { @@ -223,11 +226,73 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { mapIsolatedUidToHostUidIfNecessaryLocked(event); } + std::unordered_set<int> uidsWithActiveConfigsChanged; + std::unordered_map<int, std::vector<int64_t>> activeConfigsPerUid; // pass the event to metrics managers. for (auto& pair : mMetricsManagers) { + int uid = pair.first.GetUid(); + int64_t configId = pair.first.GetId(); + bool isPrevActive = pair.second->isActive(); pair.second->onLogEvent(*event); + bool isCurActive = pair.second->isActive(); + // Map all active configs by uid. + if (isCurActive) { + auto activeConfigs = activeConfigsPerUid.find(uid); + if (activeConfigs != activeConfigsPerUid.end()) { + activeConfigs->second.push_back(configId); + } else { + vector<int64_t> newActiveConfigs; + newActiveConfigs.push_back(configId); + activeConfigsPerUid[uid] = newActiveConfigs; + } + } + // The activation state of this config changed. + if (isPrevActive != isCurActive) { + VLOG("Active status changed for uid %d", uid); + uidsWithActiveConfigsChanged.insert(uid); + StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive); + } flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second)); } + + for (int uid : uidsWithActiveConfigsChanged) { + // Send broadcast so that receivers can pull data. + auto lastBroadcastTime = mLastActivationBroadcastTimes.find(uid); + if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) { + if (currentTimestampNs - lastBroadcastTime->second < + StatsdStats::kMinActivationBroadcastPeriodNs) { + VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us."); + return; + } + } + auto activeConfigs = activeConfigsPerUid.find(uid); + if (activeConfigs != activeConfigsPerUid.end()) { + if (mSendActivationBroadcast(uid, activeConfigs->second)) { + VLOG("StatsD sent activation notice for uid %d", uid); + mLastActivationBroadcastTimes[uid] = currentTimestampNs; + } + } else { + std::vector<int64_t> emptyActiveConfigs; + if (mSendActivationBroadcast(uid, emptyActiveConfigs)) { + VLOG("StatsD sent EMPTY activation notice for uid %d", uid); + mLastActivationBroadcastTimes[uid] = currentTimestampNs; + } + } + } +} + +void StatsLogProcessor::GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + GetActiveConfigsLocked(uid, outActiveConfigs); +} + +void StatsLogProcessor::GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs) { + outActiveConfigs.clear(); + for (auto& pair : mMetricsManagers) { + if (pair.first.GetUid() == uid && pair.second->isActive()) { + outActiveConfigs.push_back(pair.first.GetId()); + } + } } void StatsLogProcessor::OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, @@ -444,6 +509,18 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { mLastBroadcastTimes.erase(key); + int uid = key.GetUid(); + bool lastConfigForUid = true; + for (auto it : mMetricsManagers) { + if (it.first.GetUid() == uid) { + lastConfigForUid = false; + break; + } + } + if (lastConfigForUid) { + mLastActivationBroadcastTimes.erase(uid); + } + if (mMetricsManagers.empty()) { mPullerManager->ForceClearPullerCache(); } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index caf1a713986d..ea9c6e704017 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -49,7 +49,9 @@ public: const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor, const int64_t timeBaseNs, - const std::function<bool(const ConfigKey&)>& sendBroadcast); + const std::function<bool(const ConfigKey&)>& sendBroadcast, + const std::function<bool(const int&, + const vector<int64_t>&)>& sendActivationBroadcast); virtual ~StatsLogProcessor(); void OnLogEvent(LogEvent* event); @@ -60,6 +62,8 @@ public: size_t GetMetricsSize(const ConfigKey& key) const; + void GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs); + void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, const bool include_current_partial_bucket, const bool erase_data, const DumpReportReason dumpReportReason, vector<uint8_t>* outData); @@ -125,6 +129,9 @@ private: std::unordered_map<ConfigKey, long> mLastBroadcastTimes; + // Last time we sent a broadcast to this uid that the active configs had changed. + std::unordered_map<int, long> mLastActivationBroadcastTimes; + // Tracks when we last checked the bytes consumed for each config key. std::unordered_map<ConfigKey, long> mLastByteSizeTimes; @@ -144,6 +151,8 @@ private: void OnConfigUpdatedLocked( const int64_t currentTimestampNs, const ConfigKey& key, const StatsdConfig& config); + void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs); + void WriteDataToDiskLocked(const DumpReportReason dumpReportReason); void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs, const DumpReportReason dumpReportReason); @@ -174,6 +183,10 @@ private: // to retrieve the stored data. std::function<bool(const ConfigKey& key)> mSendBroadcast; + // Function used to send a broadcast so that receiver can be notified of which configs + // are currently active. + std::function<bool(const int& uid, const vector<int64_t>& configIds)> mSendActivationBroadcast; + const int64_t mTimeBaseNs; // Largest timestamp of the events that we have processed. diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 86bf3eca3ee6..b478fed49a54 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -176,6 +176,21 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key)); return true; } + }, + [this](const int& uid, const vector<int64_t>& activeConfigs) { + auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid); + sp<IStatsCompanionService> sc = getStatsCompanionService(); + if (sc == nullptr) { + VLOG("Could not access statsCompanion"); + return false; + } else if (receiver == nullptr) { + VLOG("Could not find receiver for uid %d", uid); + return false; + } else { + sc->sendActiveConfigsChangedBroadcast(receiver, activeConfigs); + VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid); + return true; + } }); mConfigManager->AddListener(mProcessor); @@ -357,6 +372,9 @@ status_t StatsService::command(int in, int out, int err, Vector<String8>& args, if (!args[0].compare(String8("print-logs"))) { return cmd_print_logs(out, args); } + if (!args[0].compare(String8("send-active-configs"))) { + return cmd_trigger_active_config_broadcast(out, args); + } if (!args[0].compare(String8("data-subscribe"))) { if (mShellSubscriber == nullptr) { mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); @@ -449,6 +467,19 @@ void StatsService::print_cmd_help(int out) { dprintf(out, " NAME The name of the configuration\n"); dprintf(out, "\n"); dprintf(out, "\n"); + dprintf(out, + "usage: adb shell cmd stats send-active-configs [--uid=UID] [--configs] " + "[NAME1] [NAME2] [NAME3..]\n"); + dprintf(out, " Send a broadcast that informs the subscriber of the current active configs.\n"); + dprintf(out, " --uid=UID The uid of the configurations. It is only possible to pass\n"); + dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); + dprintf(out, " calling uid is used.\n"); + dprintf(out, " --configs Send the list of configs in the name list instead of\n"); + dprintf(out, " the currently active configs\n"); + dprintf(out, " NAME LIST List of configuration names to be included in the broadcast.\n"); + + dprintf(out, "\n"); + dprintf(out, "\n"); dprintf(out, "usage: adb shell cmd stats print-stats\n"); dprintf(out, " Prints some basic stats.\n"); dprintf(out, " --proto Print proto binary instead of string format.\n"); @@ -499,6 +530,59 @@ status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) { return NO_ERROR; } +status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector<String8>& args) { + const int argCount = args.size(); + int uid; + vector<int64_t> configIds; + if (argCount == 1) { + // Automatically pick the uid and send a broadcast that has no active configs. + uid = IPCThreadState::self()->getCallingUid(); + mProcessor->GetActiveConfigs(uid, configIds); + } else { + int curArg = 1; + if(args[curArg].find("--uid=") == 0) { + string uidArgStr(args[curArg].c_str()); + string uidStr = uidArgStr.substr(6); + if (!getUidFromString(uidStr.c_str(), uid)) { + dprintf(out, "Invalid UID. Note that the config can only be set for " + "other UIDs on eng or userdebug builds.\n"); + return UNKNOWN_ERROR; + } + curArg++; + } else { + uid = IPCThreadState::self()->getCallingUid(); + } + if (curArg == argCount || args[curArg] != "--configs") { + VLOG("Reached end of args, or specify configs not set. Sending actual active configs,"); + mProcessor->GetActiveConfigs(uid, configIds); + } else { + // Flag specified, use the given list of configs. + curArg++; + for (int i = curArg; i < argCount; i++) { + char* endp; + int64_t configID = strtoll(args[i].c_str(), &endp, 10); + if (endp == args[i].c_str() || *endp != '\0') { + dprintf(out, "Error parsing config ID.\n"); + return UNKNOWN_ERROR; + } + VLOG("Adding config id %ld", static_cast<long>(configID)); + configIds.push_back(configID); + } + } + } + auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid); + sp<IStatsCompanionService> sc = getStatsCompanionService(); + if (sc == nullptr) { + VLOG("Could not access statsCompanion"); + } else if (receiver == nullptr) { + VLOG("Could not find receiver for uid %d", uid); + } else { + sc->sendActiveConfigsChangedBroadcast(receiver, configIds); + VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid); + } + return NO_ERROR; +} + status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& args) { const int argCount = args.size(); if (argCount >= 2) { @@ -762,7 +846,10 @@ status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) { } bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) { - const char* s = args[uidArgIndex].c_str(); + return getUidFromString(args[uidArgIndex].c_str(), uid); +} + +bool StatsService::getUidFromString(const char* s, int32_t& uid) { if (*s == '\0') { return false; } @@ -998,8 +1085,13 @@ Status StatsService::setActiveConfigsChangedOperation(const sp<android::IBinder> ENFORCE_DUMP_AND_USAGE_STATS(packageName); IPCThreadState* ipc = IPCThreadState::self(); - mConfigManager->SetActiveConfigsChangedReceiver(ipc->getCallingUid(), intentSender); - //TODO: Return the list of configs that are already active + int uid = ipc->getCallingUid(); + mConfigManager->SetActiveConfigsChangedReceiver(uid, intentSender); + if (output != nullptr) { + mProcessor->GetActiveConfigs(uid, *output); + } else { + ALOGW("StatsService::setActiveConfigsChanged output was nullptr"); + } return Status::ok(); } @@ -1057,6 +1149,24 @@ Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) { return Status::ok(); } +Status StatsService::registerPullerCallback(int32_t atomTag, + const sp<android::os::IStatsPullerCallback>& pullerCallback, + const String16& packageName) { + ENFORCE_DUMP_AND_USAGE_STATS(packageName); + + VLOG("StatsService::registerPullerCallback called."); + mPullerManager->RegisterPullerCallback(atomTag, pullerCallback); + return Status::ok(); +} + +Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& packageName) { + ENFORCE_DUMP_AND_USAGE_STATS(packageName); + + VLOG("StatsService::unregisterPullerCallback called."); + mPullerManager->UnregisterPullerCallback(atomTag); + return Status::ok(); +} + hardware::Return<void> StatsService::reportSpeakerImpedance( const SpeakerImpedance& speakerImpedance) { LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index cdff50fcb62e..7f10d74ec7d6 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -173,6 +173,19 @@ public: virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override; /** + * Binder call to register a callback function for a vendor pulled atom. + * Note: this atom must NOT have uid as a field. + */ + virtual Status registerPullerCallback(int32_t atomTag, + const sp<android::os::IStatsPullerCallback>& pullerCallback, + const String16& packageName) override; + + /** + * Binder call to unregister any existing callback function for a vendor pulled atom. + */ + virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override; + + /** * Binder call to get SpeakerImpedance atom. */ virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override; @@ -262,6 +275,12 @@ private: */ status_t cmd_trigger_broadcast(int outFd, Vector<String8>& args); + + /** + * Trigger an active configs changed broadcast. + */ + status_t cmd_trigger_active_config_broadcast(int outFd, Vector<String8>& args); + /** * Handle the config sub-command. */ @@ -328,6 +347,15 @@ private: bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid); /** + * Writes the value of uidSting into uid. + * Returns whether the uid is reasonable (type uid_t) and whether + * 1. it is equal to the calling uid, or + * 2. the device is mEngBuild, or + * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL). + */ + bool getUidFromString(const char* uidString, int32_t& uid); + + /** * Adds a configuration after checking permissions and obtaining UID from binder call. */ bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 458e0f5788dc..29f67c7e6f9b 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -31,6 +31,7 @@ import "frameworks/base/core/proto/android/bluetooth/hfp/enums.proto"; import "frameworks/base/core/proto/android/bluetooth/smp/enums.proto"; import "frameworks/base/core/proto/android/debug/enums.proto"; import "frameworks/base/core/proto/android/hardware/biometrics/enums.proto"; +import "frameworks/base/core/proto/android/hardware/sensor/assist/enums.proto"; import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; import "frameworks/base/core/proto/android/os/enums.proto"; import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto"; @@ -241,6 +242,9 @@ message Atom { BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171; DeviceIdentifierAccessDenied device_identifier_access_denied = 172; BubbleDeveloperErrorReported bubble_developer_error_reported = 173; + AssistGestureStageReported assist_gesture_stage_reported = 174; + AssistGestureFeedbackReported assist_gesture_feedback_reported = 175; + AssistGestureProgressReported assist_gesture_progress_reported = 176; } // Pulled events will start at field 10000. @@ -2177,6 +2181,7 @@ message HardwareFailed { SPEAKER_SHORT = 3; FINGERPRINT_SENSOR_BROKEN = 4; FINGERPRINT_TOO_MANY_DEAD_PIXELS = 5; + DEGRADE = 6; } optional int32 failure_code = 3; } @@ -5521,3 +5526,35 @@ message TrainInfo { optional TrainExperimentIds train_experiment_id = 2; } + +/** + * Logs the gesture stage changed event. + * + * Logged from: + * frameworks/base/packages/SystemUI/ + */ +message AssistGestureStageReported { + optional android.hardware.sensor.assist.AssistGestureStageEnum gesture_stage = 1; +} + +/** + * Logs the feedback type. + * + * Logged from: + * frameworks/base/packages/SystemUI/ + */ +message AssistGestureFeedbackReported { + // Whether or not the gesture was used. + optional android.hardware.sensor.assist.AssistGestureFeedbackEnum feedback_type = 1; +} + +/** + * Logs the progress. + * + * Logged from: + * frameworks/base/packages/SystemUI/ + */ +message AssistGestureProgressReported { + // [0,100] progress for the assist gesture. + optional int32 progress = 1; +} diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index aa22333ab26c..fc949b494194 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -144,10 +144,20 @@ void ConfigManager::RemoveConfig(const ConfigKey& key) { { lock_guard <mutex> lock(mMutex); - auto uidIt = mConfigs.find(key.GetUid()); + auto uid = key.GetUid(); + auto uidIt = mConfigs.find(uid); if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) { // Remove from map uidIt->second.erase(key); + + // No more configs for this uid, lets remove the active configs callback. + if (uidIt->second.empty()) { + auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid); + if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) { + mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver); + } + } + for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp new file mode 100644 index 000000000000..d718273e9b85 --- /dev/null +++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include <android/os/IStatsPullerCallback.h> + +#include "StatsCallbackPuller.h" +#include "logd/LogEvent.h" +#include "stats_log_util.h" + +using namespace android::binder; + +namespace android { +namespace os { +namespace statsd { + +StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IStatsPullerCallback>& callback) : + StatsPuller(tagId), mCallback(callback) { + VLOG("StatsCallbackPuller created for tag %d", tagId); +} + +bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { + VLOG("StatsCallbackPuller called for tag %d", mTagId) + if(mCallback == nullptr) { + ALOGW("No callback registered"); + return false; + } + int64_t wallClockTimeNs = getWallClockNs(); + int64_t elapsedTimeNs = getElapsedRealtimeNs(); + vector<StatsLogEventWrapper> returned_value; + Status status = mCallback->pullData(mTagId, elapsedTimeNs, wallClockTimeNs, &returned_value); + if (!status.isOk()) { + ALOGW("StatsCallbackPuller::pull failed for %d", mTagId); + return false; + } + data->clear(); + for (const StatsLogEventWrapper& it: returned_value) { + LogEvent::createLogEvents(it, *data); + } + VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId); + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h new file mode 100644 index 000000000000..c4bfa89ba9a7 --- /dev/null +++ b/cmds/statsd/src/external/StatsCallbackPuller.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/os/IStatsPullerCallback.h> +#include <utils/String16.h> +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +class StatsCallbackPuller : public StatsPuller { +public: + explicit StatsCallbackPuller(int tagId, const sp<IStatsPullerCallback>& callback); + +private: + bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override; + const sp<IStatsPullerCallback> mCallback; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp index c7c22ee85f0f..9552c0a5e35e 100644 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ b/cmds/statsd/src/external/StatsPuller.cpp @@ -33,7 +33,7 @@ sp<UidMap> StatsPuller::mUidMap = nullptr; void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; } StatsPuller::StatsPuller(const int tagId) - : mTagId(tagId) { + : mTagId(tagId), mLastPullTimeNs(0) { } bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) { diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index ecdcd21d44dd..ed72b2914a34 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include <android/os/IStatsCompanionService.h> +#include <android/os/IStatsPullerCallback.h> #include <cutils/log.h> #include <math.h> #include <stdint.h> @@ -29,6 +30,7 @@ #include "PowerStatsPuller.h" #include "ResourceHealthManagerPuller.h" #include "StatsCompanionServicePuller.h" +#include "StatsCallbackPuller.h" #include "StatsPullerManager.h" #include "SubsystemSleepStatePuller.h" #include "statslog.h" @@ -49,7 +51,7 @@ namespace statsd { // Values smaller than this may require to update the alarm. const int64_t NO_ALARM_UPDATE = INT64_MAX; -const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { +std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // wifi_bytes_transfer {android::util::WIFI_BYTES_TRANSFER, {.additiveFields = {2, 3, 4, 5}, @@ -420,6 +422,30 @@ int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) { return totalCleared; } +void StatsPullerManager::RegisterPullerCallback(int32_t atomTag, + const sp<IStatsPullerCallback>& callback) { + AutoMutex _l(mLock); + // Platform pullers cannot be changed. + if (atomTag < StatsdStats::kMaxPlatformAtomTag) { + VLOG("RegisterPullerCallback: atom tag %d is less than min tag %d", + atomTag, StatsdStats::kMaxPlatformAtomTag); + return; + } + VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag); + StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true); + kAllPullAtomInfo[atomTag] = {.puller = new StatsCallbackPuller(atomTag, callback)}; +} + +void StatsPullerManager::UnregisterPullerCallback(int32_t atomTag) { + AutoMutex _l(mLock); + // Platform pullers cannot be changed. + if (atomTag < StatsdStats::kMaxPlatformAtomTag) { + return; + } + StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false); + kAllPullAtomInfo.erase(atomTag); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h index 807e4af03dae..45f6b3568143 100644 --- a/cmds/statsd/src/external/StatsPullerManager.h +++ b/cmds/statsd/src/external/StatsPullerManager.h @@ -17,6 +17,7 @@ #pragma once #include <android/os/IStatsCompanionService.h> +#include <android/os/IStatsPullerCallback.h> #include <binder/IServiceManager.h> #include <utils/RefBase.h> #include <utils/threads.h> @@ -91,7 +92,12 @@ public: void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService); - const static std::map<int, PullAtomInfo> kAllPullAtomInfo; + void RegisterPullerCallback(int32_t atomTag, + const sp<IStatsPullerCallback>& callback); + + void UnregisterPullerCallback(int32_t atomTag); + + static std::map<int, PullAtomInfo> kAllPullAtomInfo; private: sp<IStatsCompanionService> mStatsCompanionService = nullptr; diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index c4034ffeee22..b433c41518cc 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -82,6 +82,8 @@ const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15; const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16; const int FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS = 17; const int FIELD_ID_CONFIG_STATS_ANNOTATION = 18; +const int FIELD_ID_CONFIG_STATS_ACTIVATION = 22; +const int FIELD_ID_CONFIG_STATS_DEACTIVATION = 23; const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT64 = 1; const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT32 = 2; @@ -206,6 +208,25 @@ void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { it->second->broadcast_sent_time_sec.push_back(timeSec); } +void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated) { + noteActiveStatusChanged(key, activated, getWallClockSec()); +} + +void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated, int32_t timeSec) { + lock_guard<std::mutex> lock(mLock); + auto it = mConfigStats.find(key); + if (it == mConfigStats.end()) { + ALOGE("Config key %s not found!", key.ToString().c_str()); + return; + } + auto& vec = activated ? it->second->activation_time_sec + : it->second->deactivation_time_sec; + if (vec.size() == kMaxTimestampCount) { + vec.pop_front(); + } + vec.push_back(timeSec); +} + void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes) { noteDataDropped(key, totalBytes, getWallClockSec()); } @@ -423,6 +444,15 @@ void StatsdStats::noteEmptyData(int atomId) { mPulledAtomStats[atomId].emptyData++; } +void StatsdStats::notePullerCallbackRegistrationChanged(int atomId, bool registered) { + lock_guard<std::mutex> lock(mLock); + if (registered) { + mPulledAtomStats[atomId].registeredCount++; + } else { + mPulledAtomStats[atomId].unregisteredCount++; + } +} + void StatsdStats::noteHardDimensionLimitReached(int64_t metricId) { lock_guard<std::mutex> lock(mLock); getAtomMetricStats(metricId).hardDimensionLimitReached++; @@ -492,6 +522,8 @@ void StatsdStats::resetInternalLocked() { mLogLossStats.clear(); for (auto& config : mConfigStats) { config.second->broadcast_sent_time_sec.clear(); + config.second->activation_time_sec.clear(); + config.second->deactivation_time_sec.clear(); config.second->data_drop_time_sec.clear(); config.second->data_drop_bytes.clear(); config.second->dump_report_stats.clear(); @@ -514,6 +546,8 @@ void StatsdStats::resetInternalLocked() { pullStats.second.dataError = 0; pullStats.second.pullTimeout = 0; pullStats.second.pullExceedMaxDelay = 0; + pullStats.second.registeredCount = 0; + pullStats.second.unregisteredCount = 0; } mAtomMetricStats.clear(); } @@ -547,6 +581,14 @@ void StatsdStats::dumpStats(int out) const { dprintf(out, "\tbroadcast time: %d\n", broadcastTime); } + for (const int& activationTime : configStats->activation_time_sec) { + dprintf(out, "\tactivation time: %d\n", activationTime); + } + + for (const int& deactivationTime : configStats->deactivation_time_sec) { + dprintf(out, "\tdeactivation time: %d\n", deactivationTime); + } + auto dropTimePtr = configStats->data_drop_time_sec.begin(); auto dropBytesPtr = configStats->data_drop_bytes.begin(); for (int i = 0; i < (int)configStats->data_drop_time_sec.size(); @@ -575,6 +617,14 @@ void StatsdStats::dumpStats(int out) const { (long long)broadcastTime); } + for (const int& activationTime : configStats->activation_time_sec) { + dprintf(out, "\tactivation time: %d\n", activationTime); + } + + for (const int& deactivationTime : configStats->deactivation_time_sec) { + dprintf(out, "\tdeactivation time: %d\n", deactivationTime); + } + auto dropTimePtr = configStats->data_drop_time_sec.begin(); auto dropBytesPtr = configStats->data_drop_bytes.begin(); for (int i = 0; i < (int)configStats->data_drop_time_sec.size(); @@ -625,12 +675,14 @@ void StatsdStats::dumpStats(int out) const { " (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay " "nanos)%lld, " " (max pull delay nanos)%lld, (data error)%ld\n" - " (pull timeout)%ld, (pull exceed max delay)%ld\n", + " (pull timeout)%ld, (pull exceed max delay)%ld\n" + " (registered count) %ld, (unregistered count) %ld\n", (int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec, (long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs, (long long)pair.second.avgPullDelayNs, (long long)pair.second.maxPullDelayNs, pair.second.dataError, - pair.second.pullTimeout, pair.second.pullExceedMaxDelay); + pair.second.pullTimeout, pair.second.pullExceedMaxDelay, + pair.second.registeredCount, pair.second.unregisteredCount); } if (mAnomalyAlarmRegisteredStats > 0) { @@ -683,6 +735,16 @@ void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* pr broadcast); } + for (const auto& activation : configStats.activation_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ACTIVATION | FIELD_COUNT_REPEATED, + activation); + } + + for (const auto& deactivation : configStats.deactivation_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DEACTIVATION | FIELD_COUNT_REPEATED, + deactivation); + } + for (const auto& drop_time : configStats.data_drop_time_sec) { proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP_TIME | FIELD_COUNT_REPEATED, drop_time); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index ea3f3b32b0c4..5275c8f34fd0 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -42,6 +42,13 @@ struct ConfigStats { bool is_valid; std::list<int32_t> broadcast_sent_time_sec; + + // Times at which this config is activated. + std::list<int32_t> activation_time_sec; + + // Times at which this config is deactivated. + std::list<int32_t> deactivation_time_sec; + std::list<int32_t> data_drop_time_sec; // Number of bytes dropped at corresponding time. std::list<int64_t> data_drop_bytes; @@ -132,6 +139,9 @@ public: /* Min period between two checks of byte size per config key in nanoseconds. */ static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC; + /* Minimum period between two activation broadcasts in nanoseconds. */ + static const int64_t kMinActivationBroadcastPeriodNs = 10 * NS_PER_SEC; + // Maximum age (30 days) that files on disk can exist in seconds. static const int kMaxAgeSecond = 60 * 60 * 24 * 30; @@ -146,6 +156,10 @@ public: // Max time to do a pull. static const int64_t kPullMaxDelayNs = 10 * NS_PER_SEC; + + // Max platform atom tag number. + static const int32_t kMaxPlatformAtomTag = 100000; + /** * Report a new config has been received and report the static stats about the config. * @@ -171,6 +185,13 @@ public: void noteBroadcastSent(const ConfigKey& key); /** + * Report that a config has become activated or deactivated. + * This can be different from whether or not a broadcast is sent if the + * guardrail prevented the broadcast from being sent. + */ + void noteActiveStatusChanged(const ConfigKey& key, bool activate); + + /** * Report a config's metrics data has been dropped. */ void noteDataDropped(const ConfigKey& key, const size_t totalBytes); @@ -340,6 +361,13 @@ public: void noteEmptyData(int atomId); /** + * Records that a puller callback for the given atomId was registered or unregistered. + * + * @param registered True if the callback was registered, false if was unregistered. + */ + void notePullerCallbackRegistrationChanged(int atomId, bool registered); + + /** * Hard limit was reached in the cardinality of an atom */ void noteHardDimensionLimitReached(int64_t metricId); @@ -416,6 +444,8 @@ public: long statsCompanionPullFailed = 0; long statsCompanionPullBinderTransactionFailed = 0; long emptyData = 0; + long registeredCount = 0; + long unregisteredCount = 0; } PulledAtomStats; typedef struct { @@ -494,6 +524,8 @@ private: void noteBroadcastSent(const ConfigKey& key, int32_t timeSec); + void noteActiveStatusChanged(const ConfigKey& key, bool activate, int32_t timeSec); + void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats); /** diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 495138ee9b77..11075685b7fa 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -94,7 +94,7 @@ void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds) { std::lock_guard<std::mutex> lock(mMutex); // When a metric producer does not depend on any activation, its mIsActive is true. - // Therefor, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not + // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not // change. if (mEventActivationMap.empty()) { mIsActive = false; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 6ed6ab500597..4851a8d40baa 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -118,6 +118,16 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, ALOGE("This config has too many alerts! Reject!"); mConfigValid = false; } + + mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) || + (mAllMetricProducers.size() == 0); + bool isActive = mIsAlwaysActive; + for (int metric : mMetricIndexesWithActivation) { + isActive |= mAllMetricProducers[metric]->isActive(); + } + mIsActive = isActive; + VLOG("mIsActive is initialized to %d", mIsActive) + // no matter whether this config is valid, log it in the stats. StatsdStats::getInstance().noteConfigReceived( key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(), @@ -332,12 +342,14 @@ void MetricsManager::onLogEvent(const LogEvent& event) { int tagId = event.GetTagId(); int64_t eventTimeNs = event.GetElapsedTimestampNs(); - bool isActive = false; + bool isActive = mIsAlwaysActive; for (int metric : mMetricIndexesWithActivation) { mAllMetricProducers[metric]->flushIfExpire(eventTimeNs); isActive |= mAllMetricProducers[metric]->isActive(); } + mIsActive = isActive; + if (mTagIds.find(tagId) == mTagIds.end()) { // not interesting... return; diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index cb1cefbf2063..eab1f762b390 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -128,6 +128,8 @@ public: // Does not change the state. virtual size_t byteSize(); + // Returns whether or not this config is active. + // The config is active if any metric in the config is active. inline bool isActive() const { return mIsActive; } @@ -241,9 +243,12 @@ private: // The metrics that don't need to be uploaded or even reported. std::set<int64_t> mNoReportMetricIds; - // Any metric active means the config is active. + // The config is active if any metric in the config is active. bool mIsActive; + // The config is always active if any metric in the config does not have an activation signal. + bool mIsAlwaysActive; + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index ac6c27adceaa..851ae9962d09 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -450,6 +450,17 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log } mMatchedMetricDimensionKeys.clear(); mHasGlobalBase = true; + + // If we reach the guardrail, we might have dropped some data which means the bucket is + // incomplete. + // + // The base also needs to be reset. If we do not have the full data, we might + // incorrectly compute the diff when mUseZeroDefaultBase is true since an existing key + // might be missing from mCurrentSlicedBucket. + if (hasReachedGuardRailLimit()) { + invalidateCurrentBucket(); + mCurrentSlicedBucket.clear(); + } } void ValueMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { @@ -471,6 +482,10 @@ void ValueMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { } } +bool ValueMetricProducer::hasReachedGuardRailLimit() const { + return mCurrentSlicedBucket.size() >= mDimensionHardLimit; +} + bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { // ===========GuardRail============== // 1. Report the tuple count if the tuple count > soft limit @@ -481,7 +496,7 @@ bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { size_t newTupleCount = mCurrentSlicedBucket.size() + 1; StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > mDimensionHardLimit) { + if (hasReachedGuardRailLimit()) { ALOGE("ValueMetric %lld dropping data for dimension key %s", (long long)mMetricId, newKey.toString().c_str()); StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index d1c2315b28be..f26ad85acf05 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -158,6 +158,7 @@ private: // Util function to check whether the specified dimension hits the guardrail. bool hitGuardRailLocked(const MetricDimensionKey& newKey); + bool hasReachedGuardRailLimit() const; bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey); @@ -244,6 +245,7 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket); FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate); FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed); + FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit); FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed); FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed); FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded); diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 6a07a3f169e0..f7428a50b3da 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -367,6 +367,8 @@ message StatsdStatsReport { optional int32 field_int32 = 2; } repeated Annotation annotation = 18; + repeated int32 activation_time_sec = 22; + repeated int32 deactivation_time_sec = 23; } repeated ConfigStats config_stats = 3; @@ -407,6 +409,8 @@ message StatsdStatsReport { optional int64 stats_companion_pull_failed = 13; optional int64 stats_companion_pull_binder_transaction_failed = 14; optional int64 empty_data = 15; + optional int64 registered_count = 16; + optional int64 unregistered_count = 17; } repeated PulledAtomStats pulled_atom_stats = 10; diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index aa8cfc508861..0ebf2ca4e035 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -70,6 +70,8 @@ const int FIELD_ID_PULL_FAILED = 12; const int FIELD_ID_STATS_COMPANION_FAILED = 13; const int FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED = 14; const int FIELD_ID_EMPTY_DATA = 15; +const int FIELD_ID_PULL_REGISTERED_COUNT = 16; +const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17; // for AtomMetricStats proto const int FIELD_ID_ATOM_METRIC_STATS = 17; const int FIELD_ID_METRIC_ID = 1; @@ -480,6 +482,10 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> (long long)pair.second.statsCompanionPullBinderTransactionFailed); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA, (long long)pair.second.emptyData); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT, + (long long) pair.second.registeredCount); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT, + (long long) pair.second.unregisteredCount); protoOutput->end(token); } diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 60df165f102c..5f3aae3ab93a 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -65,7 +65,8 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { sp<AlarmMonitor> periodicAlarmMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }); + [](const ConfigKey& key) { return true; }, + [](const int&, const vector<int64_t>&) {return true;}); MockMetricsManager mockMetricsManager; @@ -88,7 +89,8 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); MockMetricsManager mockMetricsManager; @@ -118,7 +120,8 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); MockMetricsManager mockMetricsManager; @@ -162,7 +165,8 @@ TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); ConfigKey key(3, 4); StatsdConfig config = MakeConfig(true); p.OnConfigUpdated(0, key, config); @@ -192,7 +196,8 @@ TEST(StatsLogProcessorTest, TestEmptyConfigHasNoUidMap) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); ConfigKey key(3, 4); StatsdConfig config = MakeConfig(false); p.OnConfigUpdated(0, key, config); @@ -218,7 +223,8 @@ TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); ConfigKey key(3, 4); StatsdConfig config; auto annotation = config.add_annotation(); diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index f0d9cf188661..c04a40cfebd9 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -45,7 +45,8 @@ TEST(UidMapTest, TestIsolatedUID) { sp<AlarmMonitor> subscriberAlarmMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }); + [](const ConfigKey& key) { return true; }, + [](const int&, const vector<int64_t>&) {return true;}); LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1); addEvent.write(100); // parent UID addEvent.write(101); // isolated UID diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index 92aa998d9720..1ff7982e1232 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -133,6 +133,13 @@ TEST(StatsdStatsTest, TestSubStats) { stats.noteMetricsReportSent(key, 0); stats.noteMetricsReportSent(key, 0); + // activation_time_sec -> 2 + stats.noteActiveStatusChanged(key, true); + stats.noteActiveStatusChanged(key, true); + + // deactivation_time_sec -> 1 + stats.noteActiveStatusChanged(key, false); + vector<uint8_t> output; stats.dumpStats(&output, true); // Dump and reset stats StatsdStatsReport report; @@ -146,6 +153,8 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ(123, configReport.data_drop_bytes(0)); EXPECT_EQ(3, configReport.dump_report_time_sec_size()); EXPECT_EQ(3, configReport.dump_report_data_size_size()); + EXPECT_EQ(2, configReport.activation_time_sec_size()); + EXPECT_EQ(1, configReport.deactivation_time_sec_size()); EXPECT_EQ(1, configReport.annotation_size()); EXPECT_EQ(123, configReport.annotation(0).field_int64()); EXPECT_EQ(456, configReport.annotation(0).field_int32()); @@ -259,6 +268,10 @@ TEST(StatsdStatsTest, TestPullAtomStats) { stats.notePullDelay(android::util::DISK_SPACE, 3335L); stats.notePull(android::util::DISK_SPACE); stats.notePullFromCache(android::util::DISK_SPACE); + stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, true); + stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, false); + stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, true); + vector<uint8_t> output; stats.dumpStats(&output, false); @@ -276,6 +289,8 @@ TEST(StatsdStatsTest, TestPullAtomStats) { EXPECT_EQ(3333L, report.pulled_atom_stats(0).max_pull_time_nanos()); EXPECT_EQ(2223L, report.pulled_atom_stats(0).average_pull_delay_nanos()); EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos()); + EXPECT_EQ(2L, report.pulled_atom_stats(0).registered_count()); + EXPECT_EQ(1L, report.pulled_atom_stats(0).unregistered_count()); } TEST(StatsdStatsTest, TestAtomMetricsStats) { @@ -338,6 +353,8 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { stats.noteDataDropped(key, timestamps[i]); stats.noteBroadcastSent(key, timestamps[i]); stats.noteMetricsReportSent(key, 0, timestamps[i]); + stats.noteActiveStatusChanged(key, true, timestamps[i]); + stats.noteActiveStatusChanged(key, false, timestamps[i]); } int32_t newTimestamp = 10000; @@ -346,6 +363,8 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { stats.noteDataDropped(key, 123, 10000); stats.noteBroadcastSent(key, 10000); stats.noteMetricsReportSent(key, 0, 10000); + stats.noteActiveStatusChanged(key, true, 10000); + stats.noteActiveStatusChanged(key, false, 10000); EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end()); const auto& configStats = stats.mConfigStats[key]; @@ -354,17 +373,23 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size()); EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size()); EXPECT_EQ(maxCount, configStats->dump_report_stats.size()); + EXPECT_EQ(maxCount, configStats->activation_time_sec.size()); + EXPECT_EQ(maxCount, configStats->deactivation_time_sec.size()); // the oldest timestamp is the second timestamp in history EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); - EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); - EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); + EXPECT_EQ(1, configStats->data_drop_bytes.front()); + EXPECT_EQ(1, configStats->dump_report_stats.front().first); + EXPECT_EQ(1, configStats->activation_time_sec.front()); + EXPECT_EQ(1, configStats->deactivation_time_sec.front()); // the last timestamp is the newest timestamp. EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back()); EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back()); EXPECT_EQ(123, configStats->data_drop_bytes.back()); EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first); + EXPECT_EQ(newTimestamp, configStats->activation_time_sec.back()); + EXPECT_EQ(newTimestamp, configStats->deactivation_time_sec.back()); } TEST(StatsdStatsTest, TestSystemServerCrash) { diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index ae3cdbcb5eb4..572b1991f426 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -2390,6 +2390,51 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) { EXPECT_EQ(true, valueProducer.mHasGlobalBase); } +TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.mutable_dimensions_in_what()->set_field(tagId); + metric.mutable_dimensions_in_what()->add_child()->set_field(1); + metric.set_condition(StringToId("SCREEN_ON")); + metric.set_max_pull_delay_sec(INT_MAX); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // First onConditionChanged + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + for (int i = 0; i < 2000; i++) { + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + event->write(i); + event->write(i); + event->init(); + data->push_back(event); + } + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + valueProducer.mCondition = false; + valueProducer.onConditionChanged(true, bucket2StartTimeNs + 2); + EXPECT_EQ(true, valueProducer.mCurrentBucketIsInvalid); + EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); +} + TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) { ValueMetric metric; metric.set_id(metricId); diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index b8b1a1db2c12..2c4f3c7692c9 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -461,7 +461,8 @@ sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const in [](const sp<IStatsCompanionService>&){}); sp<StatsLogProcessor> processor = new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, [](const ConfigKey&) { return true; }); + timeBaseNs, [](const ConfigKey&) { return true; }, + [](const int&, const vector<int64_t>&) {return true;}); processor->OnConfigUpdated(currentTimeNs, key, config); return processor; } @@ -826,4 +827,4 @@ void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list) { } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index 010e4478459d..b72ce8977128 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -2439,7 +2439,6 @@ Lcom/android/internal/telephony/cat/CatService;->isStkAppInstalled()Z Lcom/android/internal/telephony/cat/CatService;->mCmdIf:Lcom/android/internal/telephony/CommandsInterface; Lcom/android/internal/telephony/cat/CatService;->mContext:Landroid/content/Context; Lcom/android/internal/telephony/cat/CatService;->mCurrntCmd:Lcom/android/internal/telephony/cat/CatCmdMessage; -Lcom/android/internal/telephony/cat/CatService;->mHandlerThread:Landroid/os/HandlerThread; Lcom/android/internal/telephony/cat/CatService;->mMenuCmd:Lcom/android/internal/telephony/cat/CatCmdMessage; Lcom/android/internal/telephony/cat/CatService;->mMsgDecoder:Lcom/android/internal/telephony/cat/RilMessageDecoder; Lcom/android/internal/telephony/cat/CatService;->mSlotId:I diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS new file mode 100644 index 000000000000..265674a74b7e --- /dev/null +++ b/core/java/android/accessibilityservice/OWNERS @@ -0,0 +1,3 @@ +svetoslavganov@google.com +pweaver@google.com +rhedjao@google.com diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 0eadd1dcd903..5f778da44a1c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -65,6 +65,7 @@ import android.net.Uri; import android.os.BadParcelableException; import android.os.Build; import android.os.Bundle; +import android.os.GraphicsEnvironment; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -7708,6 +7709,8 @@ public class Activity extends ContextThemeWrapper } } + GraphicsEnvironment.getInstance().showAngleInUseDialogBox(this); + mActivityTransitionState.enterReady(this); dispatchActivityPostStarted(); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index db3b720642aa..ca3c72627e3b 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2019,8 +2019,9 @@ public class ActivityManager { return getTaskService().isActivityStartAllowedOnDisplay(displayId, intent, intent.resolveTypeIfNeeded(context.getContentResolver()), context.getUserId()); } catch (RemoteException e) { - throw new RuntimeException("Failure from system", e); + e.rethrowFromSystemServer(); } + return false; } /** diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index db6ad3df17b0..2b765b2284e7 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -247,8 +247,20 @@ interface IActivityTaskManager { boolean preserveWindows, boolean animate, int animationDuration); boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop, boolean animate, in Rect initialBounds, boolean showRecents); - - + /** + * Use the offset to adjust the stack boundary with animation. + * + * @param stackId Id of the stack to adjust. + * @param compareBounds Offset is only applied if the current pinned stack bounds is equal to + * the compareBounds. + * @param xOffset The horizontal offset. + * @param yOffset The vertical offset. + * @param animationDuration The duration of the resize animation in milliseconds or -1 if the + * default animation duration should be used. + * @throws RemoteException + */ + void offsetPinnedStackBounds(int stackId, in Rect compareBounds, int xOffset, int yOffset, + int animationDuration); /** * Removes stacks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index d46dbedfe8c2..5c4c0052cfbb 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -870,8 +870,9 @@ public final class LoadedApk { } } - // /vendor/lib, /odm/lib and /product/lib are added to the native lib search - // paths of the classloader. Note that this is done AFTER the classloader is + // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib + // are added to the native lib search paths of the classloader. + // Note that this is done AFTER the classloader is // created by ApplicationLoaders.getDefault().getClassLoader(...). The // reason is because if we have added the paths when creating the classloader // above, the paths are also added to the search path of the linker namespace @@ -888,8 +889,11 @@ public final class LoadedApk { // System.loadLibrary(). In order to prevent the problem, we explicitly // add the paths only to the classloader, and not to the native loader // (linker namespace). - List<String> extraLibPaths = new ArrayList<>(3); + List<String> extraLibPaths = new ArrayList<>(4); String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : ""; + if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) { + extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix); + } if (!defaultSearchPaths.contains("/vendor/lib")) { extraLibPaths.add("/vendor/lib" + abiSuffix); } diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 3119b37a686e..7746148d325a 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.content.Context; import android.os.IBinder; import android.os.IStatsManager; +import android.os.IStatsPullerCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.util.AndroidException; @@ -408,6 +409,39 @@ public final class StatsManager { } } + /** + * Registers a callback for an atom when that atom is to be pulled. The stats service will + * invoke pullData in the callback when the stats service determines that this atom needs to be + * pulled. Currently, this only works for atoms with tags above 100,000 that do not have a uid. + * + * @param atomTag The tag of the atom for this puller callback. Must be at least 100000. + * @param callback The callback to be invoked when the stats service pulls the atom. + * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service + * + * @hide + */ + @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) + public void setPullerCallback(int atomTag, IStatsPullerCallback callback) + throws StatsUnavailableException { + synchronized (this) { + try { + IStatsManager service = getIStatsManagerLocked(); + if (callback == null) { + service.unregisterPullerCallback(atomTag, mContext.getOpPackageName()); + } else { + service.registerPullerCallback(atomTag, callback, + mContext.getOpPackageName()); + } + + } catch (RemoteException e) { + Slog.e(TAG, "Failed to connect to statsd when registering data listener."); + throw new StatsUnavailableException("could not connect", e); + } catch (SecurityException e) { + throw new StatsUnavailableException(e.getMessage(), e); + } + } + } + private class StatsdDeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 077b177be930..dd00e5a74382 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -97,6 +97,12 @@ public class TaskInfo { public long lastActiveTime; /** + * The id of the display this task is associated with. + * @hide + */ + public int displayId; + + /** * The recent activity values for the highest activity in the stack to have set the values. * {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}. */ @@ -152,6 +158,7 @@ public class TaskInfo { userId = source.readInt(); stackId = source.readInt(); taskId = source.readInt(); + displayId = source.readInt(); isRunning = source.readBoolean(); baseIntent = source.readInt() != 0 ? Intent.CREATOR.createFromParcel(source) @@ -179,6 +186,7 @@ public class TaskInfo { dest.writeInt(userId); dest.writeInt(stackId); dest.writeInt(taskId); + dest.writeInt(displayId); dest.writeBoolean(isRunning); if (baseIntent != null) { @@ -209,6 +217,7 @@ public class TaskInfo { @Override public String toString() { return "TaskInfo{userId=" + userId + " stackId=" + stackId + " taskId=" + taskId + + " displayId=" + displayId + " isRunning=" + isRunning + " baseIntent=" + baseIntent + " baseActivity=" + baseActivity + " topActivity=" + topActivity + " origActivity=" + origActivity diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 886115103ccf..2f70c9d24936 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1376,7 +1376,7 @@ public class DevicePolicyManager { * complexity, and use this activity with extra {@link #EXTRA_PASSWORD_COMPLEXITY} to suggest * to users how complex the app wants the new screen lock to be. Note that both {@link * #getPasswordComplexity()} and the extra {@link #EXTRA_PASSWORD_COMPLEXITY} require the - * calling app to have the permission {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY}. + * calling app to have the permission {@link permission#REQUEST_SCREEN_LOCK_COMPLEXITY}. * * <p>If the intent is launched from within a managed profile with a profile * owner built against {@link android.os.Build.VERSION_CODES#M} or before, @@ -1404,7 +1404,7 @@ public class DevicePolicyManager { * * <p>If an invalid value is used, it will be treated as {@link #PASSWORD_COMPLEXITY_NONE}. */ - @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) + @RequiresPermission(android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY"; @@ -3346,10 +3346,10 @@ public class DevicePolicyManager { * * @throws IllegalStateException if the user is not unlocked. * @throws SecurityException if the calling application does not have the permission - * {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY} + * {@link permission#REQUEST_SCREEN_LOCK_COMPLEXITY} */ @PasswordComplexity - @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) + @RequiresPermission(android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY) public int getPasswordComplexity() { throwIfParentInstance("getPasswordComplexity"); if (mService == null) { @@ -10537,6 +10537,8 @@ public class DevicePolicyManager { * @hide */ @SystemApi + @RequiresPermission(value = android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, + conditional = true) public void setProfileOwnerCanAccessDeviceIdsForUser( @NonNull ComponentName who, @NonNull UserHandle userHandle) { if (mService == null) { diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index f1bfe8671eec..7672ccfcd995 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -2998,7 +2998,9 @@ public abstract class ContentResolver implements ContentInterface { * * @param key the key to add * @param value the value to add + * {@hide} */ + @SystemApi public void putCache(Uri key, Bundle value) { try { getContentService().putCache(mContext.getPackageName(), key, value, @@ -3014,7 +3016,9 @@ public abstract class ContentResolver implements ContentInterface { * @param key the key to get the value * @return the matched value. If the key doesn't exist, will return null. * @see #putCache(Uri, Bundle) + * {@hide} */ + @SystemApi public Bundle getCache(Uri key) { try { final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key, diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 47034a6df8a7..7cc439138527 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1029,10 +1029,18 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { } /** + * Returns true if the activity has maximum or minimum aspect ratio. + * @hide + */ + public boolean hasFixedAspectRatio() { + return maxAspectRatio != 0 || minAspectRatio != 0; + } + + /** * Returns true if the activity's orientation is fixed. * @hide */ - boolean isFixedOrientation() { + public boolean isFixedOrientation() { return isFixedOrientationLandscape() || isFixedOrientationPortrait() || screenOrientation == SCREEN_ORIENTATION_LOCKED; } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 0ac4f6481fc4..80954731bffb 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -791,11 +791,6 @@ public class PackageInstaller { * individual session IDs can be added with {@link #addChildSessionId(int)} * and commit of the multi-package session will result in all child sessions * being committed atomically. - * <p> - * If a package requires to be installed only at reboot, the session should - * be marked as a staged session by calling {@link SessionParams#setStaged()} - * with {@code true}. This can also apply to a multi-package session, in - * which case all the packages in the session will be applied at reboot. */ public static class Session implements Closeable { /** {@hide} */ @@ -1539,7 +1534,11 @@ public class PackageInstaller { * Staged sessions are scheduled to be installed at next reboot. Staged sessions can also be * multi-package. In that case, if any of the children sessions fail to install at reboot, * all the other children sessions are aborted as well. + * + * {@hide} */ + @SystemApi + @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) public void setStaged() { this.isStaged = true; } diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index fe68b8a048c2..b1553250d638 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -2273,7 +2273,8 @@ public final class ShortcutInfo implements Parcelable { CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName, Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras, long lastChangedTimestamp, - int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason) { + int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason, + Person[] persons) { mUserId = userId; mId = id; mPackageName = packageName; @@ -2299,5 +2300,6 @@ public final class ShortcutInfo implements Parcelable { mIconResName = iconResName; mBitmapPath = bitmapPath; mDisabledReason = disabledReason; + mPersons = persons; } } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index f3ebd7f36fd6..ac44fe93ac31 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -16,6 +16,8 @@ package android.hardware.display; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ParceledListSlice; @@ -229,7 +231,17 @@ public final class DisplayManagerGlobal { return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); } - public void registerDisplayListener(DisplayListener listener, Handler handler) { + /** + * Register a listener for display-related changes. + * + * @param listener The listener that will be called when display changes occur. + * @param handler Handler for the thread that will be receiving the callbacks. May be null. + * If null, listener will use the handler for the current thread, and if still null, + * the handler for the main thread. + * If that is still null, a runtime exception will be thrown. + */ + public void registerDisplayListener(@NonNull DisplayListener listener, + @Nullable Handler handler) { if (listener == null) { throw new IllegalArgumentException("listener must not be null"); } @@ -237,7 +249,8 @@ public final class DisplayManagerGlobal { synchronized (mLock) { int index = findDisplayListenerLocked(listener); if (index < 0) { - mDisplayListeners.add(new DisplayListenerDelegate(listener, handler)); + Looper looper = getLooperForHandler(handler); + mDisplayListeners.add(new DisplayListenerDelegate(listener, looper)); registerCallbackIfNeededLocked(); } } @@ -258,6 +271,17 @@ public final class DisplayManagerGlobal { } } + private static Looper getLooperForHandler(@Nullable Handler handler) { + Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); + if (looper == null) { + looper = Looper.getMainLooper(); + } + if (looper == null) { + throw new RuntimeException("Could not get Looper for the UI thread."); + } + return looper; + } + private int findDisplayListenerLocked(DisplayListener listener) { final int numListeners = mDisplayListeners.size(); for (int i = 0; i < numListeners; i++) { @@ -636,8 +660,8 @@ public final class DisplayManagerGlobal { private static final class DisplayListenerDelegate extends Handler { public final DisplayListener mListener; - public DisplayListenerDelegate(DisplayListener listener, Handler handler) { - super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); + DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper) { + super(looper, null, true /*async*/); mListener = listener; } diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl index a8682f9ddd3b..514658549651 100644 --- a/core/java/android/net/INetworkMonitorCallbacks.aidl +++ b/core/java/android/net/INetworkMonitorCallbacks.aidl @@ -24,7 +24,7 @@ oneway interface INetworkMonitorCallbacks { void onNetworkMonitorCreated(in INetworkMonitor networkMonitor); void notifyNetworkTested(int testResult, @nullable String redirectUrl); void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config); - void showProvisioningNotification(String action); + void showProvisioningNotification(String action, String packageName); void hideProvisioningNotification(); void logCaptivePortalLoginEvent(int eventId, String packageName); }
\ No newline at end of file diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index b6cd6359384a..ca49438390e9 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -15,6 +15,7 @@ */ package android.net; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; @@ -27,6 +28,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IIpClientCallbacks; @@ -63,9 +65,6 @@ public class NetworkStack { public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK"; - /** @hide */ - public static final String NETWORKSTACK_PACKAGE_NAME = "com.android.mainline.networkstack"; - private static final int NETWORKSTACK_TIMEOUT_MS = 10_000; @NonNull @@ -204,7 +203,33 @@ public class NetworkStack { final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !context.bindServiceAsUser(intent, new NetworkStackConnection(), + if (comp == null) { + Slog.wtf(TAG, "Could not resolve the network stack with " + intent); + // TODO: crash/reboot system server ? + return; + } + + final PackageManager pm = context.getPackageManager(); + int uid = -1; + try { + uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM); + } catch (PackageManager.NameNotFoundException e) { + Slog.wtf("Network stack package not found", e); + // Fall through + } + + if (uid != Process.NETWORK_STACK_UID) { + throw new SecurityException("Invalid network stack UID: " + uid); + } + + final int hasPermission = + pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName()); + if (hasPermission != PERMISSION_GRANTED) { + throw new SecurityException( + "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK); + } + + if (!context.bindServiceAsUser(intent, new NetworkStackConnection(), Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { Slog.wtf(TAG, "Could not bind to network stack in-process, or in app with " + intent); diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 1f3369376b10..a851e04e78ec 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -21,13 +21,14 @@ import android.annotation.Nullable; import android.annotation.WorkerThread; import java.util.ArrayDeque; -import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.FutureTask; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -190,13 +191,19 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class AsyncTask<Params, Progress, Result> { private static final String LOG_TAG = "AsyncTask"; - private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); - // We want at least 2 threads and at most 4 threads in the core pool, - // preferring to have 1 less than the CPU count to avoid saturating - // the CPU with background work - private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); - private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; - private static final int KEEP_ALIVE_SECONDS = 30; + // We keep only a single pool thread around all the time. + // We let the pool grow to a fairly large number of threads if necessary, + // but let them time out quickly. In the unlikely case that we run out of threads, + // we fall back to a simple unbounded-queue executor. + // This combination ensures that: + // 1. We normally keep few threads (1) around. + // 2. We queue only after launching a significantly larger, but still bounded, set of threads. + // 3. We keep the total number of threads bounded, but still allow an unbounded set + // of tasks to be queued. + private static final int CORE_POOL_SIZE = 1; + private static final int MAXIMUM_POOL_SIZE = 20; + private static final int BACKUP_POOL_SIZE = 5; + private static final int KEEP_ALIVE_SECONDS = 3; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); @@ -206,8 +213,29 @@ public abstract class AsyncTask<Params, Progress, Result> { } }; - private static final BlockingQueue<Runnable> sPoolWorkQueue = - new LinkedBlockingQueue<Runnable>(128); + // Used only for rejected executions. + // Initialization protected by sRunOnSerialPolicy lock. + private static ThreadPoolExecutor sBackupExecutor; + private static LinkedBlockingQueue<Runnable> sBackupExecutorQueue; + + private static final RejectedExecutionHandler sRunOnSerialPolicy = + new RejectedExecutionHandler() { + public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { + android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size"); + // As a last ditch fallback, run it on an executor with an unbounded queue. + // Create this executor lazily, hopefully almost never. + synchronized (this) { + if (sBackupExecutor == null) { + sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>(); + sBackupExecutor = new ThreadPoolExecutor( + BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS, + TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory); + sBackupExecutor.allowCoreThreadTimeOut(true); + } + } + sBackupExecutor.execute(r); + } + }; /** * An {@link Executor} that can be used to execute tasks in parallel. @@ -217,8 +245,8 @@ public abstract class AsyncTask<Params, Progress, Result> { static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, - sPoolWorkQueue, sThreadFactory); - threadPoolExecutor.allowCoreThreadTimeOut(true); + new SynchronousQueue<Runnable>(), sThreadFactory); + threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy); THREAD_POOL_EXECUTOR = threadPoolExecutor; } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 269c781397ad..8813e400bee0 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -16,6 +16,7 @@ package android.os; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -30,6 +31,7 @@ import android.opengl.EGL14; import android.provider.Settings; import android.util.Base64; import android.util.Log; +import android.widget.Toast; import com.android.framework.protobuf.InvalidProtocolBufferException; @@ -222,9 +224,17 @@ public class GraphicsEnvironment { } - private static List<String> getGlobalSettingsString(Bundle bundle, String globalSetting) { - List<String> valueList = null; - final String settingsValue = bundle.getString(globalSetting); + private static List<String> getGlobalSettingsString(ContentResolver contentResolver, + Bundle bundle, + String globalSetting) { + final List<String> valueList; + final String settingsValue; + + if (bundle != null) { + settingsValue = bundle.getString(globalSetting); + } else { + settingsValue = Settings.Global.getString(contentResolver, globalSetting); + } if (settingsValue != null) { valueList = new ArrayList<>(Arrays.asList(settingsValue.split(","))); @@ -246,17 +256,27 @@ public class GraphicsEnvironment { return -1; } - private static String getDriverForPkg(Bundle bundle, String packageName) { - final String allUseAngle = - bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE); + private static String getDriverForPkg(Context context, Bundle bundle, String packageName) { + final String allUseAngle; + if (bundle != null) { + allUseAngle = + bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE); + } else { + ContentResolver contentResolver = context.getContentResolver(); + allUseAngle = Settings.Global.getString(contentResolver, + Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE); + } if ((allUseAngle != null) && allUseAngle.equals("1")) { return sDriverMap.get(OpenGlDriverChoice.ANGLE); } - final List<String> globalSettingsDriverPkgs = getGlobalSettingsString( - bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS); - final List<String> globalSettingsDriverValues = getGlobalSettingsString( - bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES); + final ContentResolver contentResolver = context.getContentResolver(); + final List<String> globalSettingsDriverPkgs = + getGlobalSettingsString(contentResolver, bundle, + Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS); + final List<String> globalSettingsDriverValues = + getGlobalSettingsString(contentResolver, bundle, + Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES); // Make sure we have a good package name if ((packageName == null) || (packageName.isEmpty())) { @@ -308,7 +328,7 @@ public class GraphicsEnvironment { * True: Temporary rules file was loaded. * False: Temporary rules file was *not* loaded. */ - private boolean setupAngleWithTempRulesFile(Context context, + private static boolean setupAngleWithTempRulesFile(Context context, String packageName, String paths, String devOptIn) { @@ -372,7 +392,7 @@ public class GraphicsEnvironment { * True: APK rules file was loaded. * False: APK rules file was *not* loaded. */ - private boolean setupAngleRulesApk(String anglePkgName, + private static boolean setupAngleRulesApk(String anglePkgName, ApplicationInfo angleInfo, PackageManager pm, String packageName, @@ -405,23 +425,32 @@ public class GraphicsEnvironment { /** * Pull ANGLE whitelist from GlobalSettings and compare against current package */ - private boolean checkAngleWhitelist(Bundle bundle, String packageName) { + private static boolean checkAngleWhitelist(Context context, Bundle bundle, String packageName) { + final ContentResolver contentResolver = context.getContentResolver(); final List<String> angleWhitelist = - getGlobalSettingsString(bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST); + getGlobalSettingsString(contentResolver, bundle, + Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST); return angleWhitelist.contains(packageName); } /** * Pass ANGLE details down to trigger enable logic + * + * @param context + * @param bundle + * @param packageName + * @return true: ANGLE setup successfully + * false: ANGLE not setup (not on whitelist, ANGLE not present, etc.) */ - public void setupAngle(Context context, Bundle bundle, PackageManager pm, String packageName) { + public boolean setupAngle(Context context, Bundle bundle, PackageManager pm, + String packageName) { if (packageName.isEmpty()) { Log.v(TAG, "No package name available yet, skipping ANGLE setup"); - return; + return false; } - final String devOptIn = getDriverForPkg(bundle, packageName); + final String devOptIn = getDriverForPkg(context, bundle, packageName); if (DEBUG) { Log.v(TAG, "ANGLE Developer option for '" + packageName + "' " + "set to: '" + devOptIn + "'"); @@ -439,11 +468,11 @@ public class GraphicsEnvironment { // load a driver, GraphicsEnv::shouldUseAngle() has seen the package name before // and can confidently answer yes/no based on the previously set developer // option value. - final boolean whitelisted = checkAngleWhitelist(bundle, packageName); + final boolean whitelisted = checkAngleWhitelist(context, bundle, packageName); final boolean defaulted = devOptIn.equals(sDriverMap.get(OpenGlDriverChoice.DEFAULT)); final boolean rulesCheck = (whitelisted || !defaulted); if (!rulesCheck) { - return; + return false; } if (whitelisted) { @@ -456,7 +485,7 @@ public class GraphicsEnvironment { final String anglePkgName = getAnglePackageName(pm); if (anglePkgName.isEmpty()) { Log.e(TAG, "Failed to find ANGLE package."); - return; + return false; } final ApplicationInfo angleInfo; @@ -464,7 +493,7 @@ public class GraphicsEnvironment { angleInfo = pm.getApplicationInfo(anglePkgName, PackageManager.MATCH_SYSTEM_ONLY); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed"); - return; + return false; } final String abi = chooseAbi(angleInfo); @@ -480,12 +509,62 @@ public class GraphicsEnvironment { if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) { // We setup ANGLE with a temp rules file, so we're done here. - return; + return true; } if (setupAngleRulesApk(anglePkgName, angleInfo, pm, packageName, paths, devOptIn)) { // We setup ANGLE with rules from the APK, so we're done here. - return; + return true; + } + + return false; + } + + /** + * Determine if the "ANGLE In Use" dialog box should be shown. + */ + private boolean shouldShowAngleInUseDialogBox(Context context) { + try { + ContentResolver contentResolver = context.getContentResolver(); + final int showDialogBox = Settings.Global.getInt(contentResolver, + Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX); + + return (showDialogBox == 1); + } catch (Settings.SettingNotFoundException | SecurityException e) { + // Do nothing and move on + } + + // No setting, so assume false + return false; + } + + /** + * Determine if ANGLE should be used. + */ + private boolean shouldUseAngle(Context context, String packageName) { + // Need to make sure we are evaluating ANGLE usage for the correct circumstances + if (!setupAngle(context, null, context.getPackageManager(), packageName)) { + Log.v(TAG, "Package '" + packageName + "' should use not ANGLE"); + return false; + } + + final boolean useAngle = getShouldUseAngle(packageName); + Log.v(TAG, "Package '" + packageName + "' should use ANGLE = '" + useAngle + "'"); + + return useAngle; + } + + /** + * Show the ANGLE in Use Dialog Box + * @param context + */ + public void showAngleInUseDialogBox(Context context) { + final String packageName = context.getPackageName(); + + if (shouldShowAngleInUseDialogBox(context) && shouldUseAngle(context, packageName)) { + final String toastMsg = packageName + " is using ANGLE"; + final Toast toast = Toast.makeText(context, toastMsg, Toast.LENGTH_LONG); + toast.show(); } } @@ -541,19 +620,19 @@ public class GraphicsEnvironment { if (gameDriverAllApps != 1) { // GAME_DRIVER_OPT_OUT_APPS has higher priority than GAME_DRIVER_OPT_IN_APPS - if (getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_OUT_APPS) - .contains(packageName)) { + if (getGlobalSettingsString(null, coreSettings, + Settings.Global.GAME_DRIVER_OPT_OUT_APPS).contains(packageName)) { if (DEBUG) { Log.w(TAG, packageName + " opts out from Game Driver."); } return false; } final boolean isOptIn = - getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS) - .contains(packageName); + getGlobalSettingsString(null, coreSettings, + Settings.Global.GAME_DRIVER_OPT_IN_APPS).contains(packageName); if (!isOptIn - && !getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_WHITELIST) - .contains(packageName)) { + && !getGlobalSettingsString(null, coreSettings, + Settings.Global.GAME_DRIVER_WHITELIST).contains(packageName)) { if (DEBUG) { Log.w(TAG, packageName + " is not on the whitelist."); } @@ -660,4 +739,5 @@ public class GraphicsEnvironment { long driverVersionCode, String appPackageName); private static native void setAngleInfo(String path, String appPackage, String devOptIn, FileDescriptor rulesFd, long rulesOffset, long rulesLength); + private static native boolean getShouldUseAngle(String packageName); } diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl index dde46cd93d5f..0751b964f85e 100644 --- a/core/java/android/os/IStatsCompanionService.aidl +++ b/core/java/android/os/IStatsCompanionService.aidl @@ -69,6 +69,12 @@ interface IStatsCompanionService { oneway void sendDataBroadcast(in IBinder intentSender, long lastReportTimeNs); /** + * Send a broadcast to the specified PendingIntent's as IBinder notifying it that the list + * of active configs has changed. + */ + oneway void sendActiveConfigsChangedBroadcast(in IBinder intentSender, in long[] configIds); + + /** * Requests StatsCompanionService to send a broadcast using the given intentSender * (which should cast to an IIntentSender), along with the other information specified. */ diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index 93d6f4c12128..f1bba1ab2977 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -16,6 +16,8 @@ package android.os; +import android.os.IStatsPullerCallback; + /** * Binder interface to communicate with the statistics management service. * {@hide} @@ -178,4 +180,20 @@ interface IStatsManager { * this label. This allows building custom metrics and predicates. */ void sendAppBreadcrumbAtom(int label, int state); + + /** + * Registers a puller callback function that, when invoked, pulls the data + * for the specified vendor atom tag. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS + */ + oneway void registerPullerCallback(int atomTag, IStatsPullerCallback pullerCallback, + String packageName); + + /** + * Unregisters a puller callback function for the given vendor atom. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS + */ + oneway void unregisterPullerCallback(int atomTag, String packageName); } diff --git a/core/java/android/os/IStatsPullerCallback.aidl b/core/java/android/os/IStatsPullerCallback.aidl new file mode 100644 index 000000000000..1684aeb0d666 --- /dev/null +++ b/core/java/android/os/IStatsPullerCallback.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.StatsLogEventWrapper; + +/** + * Binder interface to pull atoms for the stats service. + * {@hide} + */ +interface IStatsPullerCallback { + /** + * Pull data for the specified atom tag. Returns an array of StatsLogEventWrapper containing + * the data. + * + * Note: These pulled atoms should not have uid/attribution chain. Additionally, the event + * timestamps will be truncated to the nearest 5 minutes. + */ + StatsLogEventWrapper[] pullData(int atomTag, long elapsedNanos, long wallClocknanos); + +} diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl deleted file mode 100644 index c07b98055d5a..000000000000 --- a/core/java/android/os/ParcelFileDescriptor.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* //device/java/android/android/os/ParcelFileDescriptor.aidl -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -package android.os; - -parcelable ParcelFileDescriptor cpp_header "binder/ParcelFileDescriptor.h"; diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java index acb9eac3572c..23342426693a 100644 --- a/core/java/android/os/StatsLogEventWrapper.java +++ b/core/java/android/os/StatsLogEventWrapper.java @@ -57,20 +57,18 @@ public final class StatsLogEventWrapper implements Parcelable { public static final Parcelable.Creator<StatsLogEventWrapper> CREATOR = new Parcelable.Creator<StatsLogEventWrapper>() { public StatsLogEventWrapper createFromParcel(Parcel in) { - android.util.EventLog.writeEvent(0x534e4554, "112550251", - android.os.Binder.getCallingUid(), ""); - // Purposefully leaving this method not implemented. - throw new RuntimeException("Not implemented"); + return new StatsLogEventWrapper(in); } public StatsLogEventWrapper[] newArray(int size) { - android.util.EventLog.writeEvent(0x534e4554, "112550251", - android.os.Binder.getCallingUid(), ""); - // Purposefully leaving this method not implemented. - throw new RuntimeException("Not implemented"); + return new StatsLogEventWrapper[size]; } }; + private StatsLogEventWrapper(Parcel in) { + readFromParcel(in); + } + /** * Set work source if any. */ @@ -197,6 +195,70 @@ public final class StatsLogEventWrapper implements Parcelable { } /** + * Reads from parcel and appropriately fills member fields. + */ + public void readFromParcel(Parcel in) { + mTypes = new ArrayList<>(); + mValues = new ArrayList<>(); + mWorkSource = null; + + mTag = in.readInt(); + mElapsedTimeNs = in.readLong(); + mWallClockTimeNs = in.readLong(); + + // Clear any data. + if (DEBUG) { + Slog.d(TAG, "Reading " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs); + } + // Set up worksource if present. + int numWorkChains = in.readInt(); + if (numWorkChains > 0) { + mWorkSource = new WorkSource(); + for (int i = 0; i < numWorkChains; i++) { + android.os.WorkSource.WorkChain workChain = mWorkSource.createWorkChain(); + int workChainSize = in.readInt(); + for (int j = 0; j < workChainSize; j++) { + int uid = in.readInt(); + String tag = in.readString(); + workChain.addNode(uid, tag); + } + } + } + + // Do the rest of the types. + int numTypes = in.readInt(); + if (DEBUG) { + Slog.d(TAG, "Reading " + numTypes + " elements"); + } + for (int i = 0; i < numTypes; i++) { + int type = in.readInt(); + mTypes.add(type); + switch (type) { + case EVENT_TYPE_INT: + mValues.add(in.readInt()); + break; + case EVENT_TYPE_LONG: + mValues.add(in.readLong()); + break; + case EVENT_TYPE_FLOAT: + mValues.add(in.readFloat()); + break; + case EVENT_TYPE_DOUBLE: + mValues.add(in.readDouble()); + break; + case EVENT_TYPE_STRING: + mValues.add(in.readString()); + break; + case EVENT_TYPE_STORAGE: + mValues.add(in.createByteArray()); + break; + default: + break; + } + } + } + + /** * Boilerplate for Parcel. */ public int describeContents() { diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 735f4f253594..43c906495cb6 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1545,7 +1545,7 @@ public class StorageManager { public static boolean hasIsolatedStorage() { // Prefer to use snapshot for current boot when available return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT, - SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false)); + SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, true)); } /** diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 8254e33bcadd..104b61dc410d 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -69,6 +69,7 @@ public final class DeviceConfig { * @hide */ @SystemApi + @TestApi public static final String NAMESPACE_AUTOFILL = "autofill"; /** @@ -122,6 +123,21 @@ public final class DeviceConfig { } /** + * Namespace for all runtime related features. + * + * @hide + */ + @SystemApi + public interface Runtime { + String NAMESPACE = "runtime"; + + /** + * Whether or not we use the precompiled layout. + */ + String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled"; + } + + /** * Namespace for all runtime native related features. * * @hide @@ -292,6 +308,21 @@ public final class DeviceConfig { } /** + * Namespace for Rollback. + * + * @hide + */ + @SystemApi + public interface Rollback { + String NAMESPACE = "rollback"; + + /** + * Timeout in milliseconds for enabling package rollback. + */ + String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout"; + } + + /** * Namespace for storage-related features. * * @hide @@ -418,6 +449,7 @@ public final class DeviceConfig { * @see #removeOnPropertyChangedListener(OnPropertyChangedListener) */ @SystemApi + @TestApi @RequiresPermission(READ_DEVICE_CONFIG) public static void addOnPropertyChangedListener( @NonNull String namespace, @@ -451,6 +483,7 @@ public final class DeviceConfig { * @see #addOnPropertyChangedListener(String, Executor, OnPropertyChangedListener) */ @SystemApi + @TestApi public static void removeOnPropertyChangedListener( OnPropertyChangedListener onPropertyChangedListener) { synchronized (sLock) { @@ -547,6 +580,7 @@ public final class DeviceConfig { * @hide */ @SystemApi + @TestApi public interface OnPropertyChangedListener { /** * Called when a property has changed. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 3747ec6f4003..2b638f6e283f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12318,6 +12318,14 @@ public final class Settings { "angle_whitelist"; /** + * Show the "ANGLE In Use" dialog box to the user when ANGLE is the OpenGL driver. + * The value is a boolean (1 or 0). + * @hide + */ + public static final String GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX = + "show_angle_in_use_dialog_box"; + + /** * Game Driver global preference for all Apps. * 0 = Default * 1 = All Apps use Game Driver @@ -12347,6 +12355,12 @@ public final class Settings { public static final String GAME_DRIVER_BLACKLIST = "game_driver_blacklist"; /** + * List of blacklists, each blacklist is a blacklist for a specific version of Game Driver. + * @hide + */ + public static final String GAME_DRIVER_BLACKLISTS = "game_driver_blacklists"; + + /** * Apps on the whitelist that are allowed to use Game Driver. * The string is a list of application package names, seperated by comma. * i.e. <apk1>,<apk2>,...,<apkN> @@ -13343,6 +13357,14 @@ public final class Settings { public static final String ISOLATED_STORAGE_REMOTE = "isolated_storage_remote"; /** + * Indicates whether aware is available in the current location. + * @hide + */ + public static final String AWARE_ALLOWED = "aware_allowed"; + + private static final Validator AWARE_ALLOWED_VALIDATOR = BOOLEAN_VALIDATOR; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * @@ -13388,6 +13410,7 @@ public final class Settings { SOFT_AP_TIMEOUT_ENABLED, ZEN_DURATION, CHARGING_VIBRATION_ENABLED, + AWARE_ALLOWED, }; /** @@ -13448,6 +13471,7 @@ public final class Settings { VALIDATORS.put(WIFI_PNO_RECENCY_SORTING_ENABLED, WIFI_PNO_RECENCY_SORTING_ENABLED_VALIDATOR); VALIDATORS.put(WIFI_LINK_PROBING_ENABLED, WIFI_LINK_PROBING_ENABLED_VALIDATOR); + VALIDATORS.put(AWARE_ALLOWED, AWARE_ALLOWED_VALIDATOR); } /** diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java index 56558d04e50d..0eeef70a79a6 100644 --- a/core/java/android/util/MathUtils.java +++ b/core/java/android/util/MathUtils.java @@ -166,6 +166,26 @@ public final class MathUtils { } /** + * Returns the interpolation scalar (s) that satisfies the equation: {@code value = }{@link + * #lerp}{@code (a, b, s)} + * + * <p>If {@code a == b}, then this function will return 0. + */ + public static float lerpInv(float a, float b, float value) { + return a != b ? ((value - a) / (b - a)) : 0.0f; + } + + /** Returns the single argument constrained between [0.0, 1.0]. */ + public static float saturate(float value) { + return constrain(value, 0.0f, 1.0f); + } + + /** Returns the saturated (constrained between [0, 1]) result of {@link #lerpInv}. */ + public static float lerpInvSat(float a, float b, float value) { + return saturate(lerpInv(a, b, value)); + } + + /** * Returns an interpolated angle in degrees between a set of start and end * angles. * <p> @@ -195,6 +215,32 @@ public final class MathUtils { } /** + * Calculates a value in [rangeMin, rangeMax] that maps value in [valueMin, valueMax] to + * returnVal in [rangeMin, rangeMax]. + * <p> + * Always returns a constrained value in the range [rangeMin, rangeMax], even if value is + * outside [valueMin, valueMax]. + * <p> + * Eg: + * constrainedMap(0f, 100f, 0f, 1f, 0.5f) = 50f + * constrainedMap(20f, 200f, 10f, 20f, 20f) = 200f + * constrainedMap(20f, 200f, 10f, 20f, 50f) = 200f + * constrainedMap(10f, 50f, 10f, 20f, 5f) = 10f + * + * @param rangeMin minimum of the range that should be returned. + * @param rangeMax maximum of the range that should be returned. + * @param valueMin minimum of range to map {@code value} to. + * @param valueMax maximum of range to map {@code value} to. + * @param value to map to the range [{@code valueMin}, {@code valueMax}]. Note, can be outside + * this range, resulting in a clamped value. + * @return the mapped value, constrained to [{@code rangeMin}, {@code rangeMax}. + */ + public static float constrainedMap( + float rangeMin, float rangeMax, float valueMin, float valueMax, float value) { + return lerp(rangeMin, rangeMax, lerpInvSat(valueMin, valueMax, value)); + } + + /** * Perform Hermite interpolation between two values. * Eg: * smoothStep(0, 0.5f, 0.5f) = 1f diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 39b6876d86c7..b28ac4714e90 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -33,6 +33,8 @@ import android.os.Handler; import android.os.Message; import android.os.SystemProperties; import android.os.Trace; +import android.provider.DeviceConfig; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; @@ -80,8 +82,6 @@ public abstract class LayoutInflater { private static final String TAG = LayoutInflater.class.getSimpleName(); private static final boolean DEBUG = false; - private static final String USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY - = "view.precompiled_layout_enabled"; private static final String COMPILED_VIEW_DEX_FILE_NAME = "/compiled_view.dex"; /** Empty stack trace used to avoid log spam in re-throw exceptions. */ @@ -412,8 +412,24 @@ public abstract class LayoutInflater { } private void initPrecompiledViews() { - initPrecompiledViews( - SystemProperties.getBoolean(USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY, false)); + // Use the device config if enabled, otherwise default to the system property. + String usePrecompiledLayout = null; + try { + usePrecompiledLayout = DeviceConfig.getProperty( + DeviceConfig.Runtime.NAMESPACE, + DeviceConfig.Runtime.USE_PRECOMPILED_LAYOUT); + } catch (Exception e) { + // May be caused by permission errors reading the property (i.e. instant apps). + } + boolean enabled = false; + if (TextUtils.isEmpty(usePrecompiledLayout)) { + enabled = SystemProperties.getBoolean( + DeviceConfig.Runtime.USE_PRECOMPILED_LAYOUT, + false); + } else { + enabled = Boolean.parseBoolean(usePrecompiledLayout); + } + initPrecompiledViews(enabled); } private void initPrecompiledViews(boolean enablePrecompiledViews) { diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 06207a9290d7..384cdbb3831c 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -174,7 +174,7 @@ public final class AccessibilityManager { final Handler.Callback mCallback; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) boolean mIsEnabled; int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS new file mode 100644 index 000000000000..265674a74b7e --- /dev/null +++ b/core/java/android/view/accessibility/OWNERS @@ -0,0 +1,3 @@ +svetoslavganov@google.com +pweaver@google.com +rhedjao@google.com diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 397fc6610d5a..5e5c8265f782 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -337,6 +337,14 @@ public final class AutofillManager { public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes /** + * Disables Augmented Autofill. + * + * @hide + */ + @TestApi + public static final int FLAG_SMART_SUGGESTION_OFF = 0x0; + + /** * Displays the Augment Autofill window using the same mechanism (such as a popup-window * attached to the focused view) as the standard autofill. * @@ -346,14 +354,13 @@ public final class AutofillManager { public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x1; /** @hide */ - @IntDef(flag = true, prefix = { "FLAG_SMART_SUGGESTION_" }, value = { - FLAG_SMART_SUGGESTION_SYSTEM - }) + @IntDef(flag = false, value = { FLAG_SMART_SUGGESTION_OFF, FLAG_SMART_SUGGESTION_SYSTEM }) @Retention(RetentionPolicy.SOURCE) public @interface SmartSuggestionMode {} /** - * Used to emulate Smart Suggestion for Augmented Autofill during development + * {@code DeviceConfig} property used to set which Smart Suggestion modes for Augmented Autofill + * are available. * * @hide */ @@ -2355,7 +2362,14 @@ public final class AutofillManager { /** @hide */ public static String getSmartSuggestionModeToString(@SmartSuggestionMode int flags) { - return (flags == FLAG_SMART_SUGGESTION_SYSTEM) ? "1-SYSTEM" : flags + "-UNSUPPORTED"; + switch (flags) { + case FLAG_SMART_SUGGESTION_OFF: + return "OFF"; + case FLAG_SMART_SUGGESTION_SYSTEM: + return "SYSTEM"; + default: + return "INVALID:" + flags; + } } @GuardedBy("mLock") diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 8a097883dd5c..e63a406411d8 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1373,7 +1373,7 @@ public final class InputMethodManager { * @hide */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768499) public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) { try { Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be removed " diff --git a/core/java/android/view/textclassifier/TEST_MAPPING b/core/java/android/view/textclassifier/TEST_MAPPING index 0d3c3465f2d6..01a6edecf21e 100644 --- a/core/java/android/view/textclassifier/TEST_MAPPING +++ b/core/java/android/view/textclassifier/TEST_MAPPING @@ -10,14 +10,6 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] - }, - { - "name": "CtsViewTestCases", - "options": [ - { - "include-filter": "android.view.textclassifier.cts" - } - ] } ] } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 9c21ba60a034..ded3be4e4ef5 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -6020,9 +6020,6 @@ public class Editor { } updateSelection(event); - if (mTextView.hasSelection() && mEndHandle != null) { - mEndHandle.updateMagnifier(event); - } break; case MotionEvent.ACTION_UP: @@ -6030,9 +6027,6 @@ public class Editor { break; } updateSelection(event); - if (mEndHandle != null) { - mEndHandle.dismissMagnifier(); - } // No longer dragging to select text, let the parent intercept events. mTextView.getParent().requestDisallowInterceptTouchEvent(false); diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 89bb2738b899..dad2669d3de8 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -17,6 +17,8 @@ package android.widget; import android.annotation.CallSuper; +import android.annotation.ColorInt; +import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.Px; @@ -340,7 +342,7 @@ public class NumberPicker extends LinearLayout { /** * The {@link Paint} for drawing the selector. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private final Paint mSelectorWheelPaint; /** @@ -1718,6 +1720,44 @@ public class NumberPicker extends LinearLayout { } /** + * Sets the text color for all the states (normal, selected, focused) to be the given color. + * + * @param color A color value in the form 0xAARRGGBB. + */ + public void setTextColor(@ColorInt int color) { + mSelectorWheelPaint.setColor(color); + mInputText.setTextColor(color); + invalidate(); + } + + /** + * @return the text color. + */ + @ColorInt + public int getTextColor() { + return mSelectorWheelPaint.getColor(); + } + + /** + * Sets the text size to the given value. This value must be > 0 + * + * @param size The size in pixel units. + */ + public void setTextSize(@FloatRange(from = 0.0, fromInclusive = false) float size) { + mSelectorWheelPaint.setTextSize(size); + mInputText.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); + invalidate(); + } + + /** + * @return the size (in pixels) of the text size in this NumberPicker. + */ + @FloatRange(from = 0.0, fromInclusive = false) + public float getTextSize() { + return mSelectorWheelPaint.getTextSize(); + } + + /** * Makes a measure spec that tries greedily to use the max value. * * @param measureSpec The measure spec. diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 119a015cd5ea..8ebcef5133b6 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -46,6 +46,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; +import android.database.Cursor; import android.database.DataSetObserver; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -69,6 +70,8 @@ import android.os.ResultReceiver; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; +import android.provider.DocumentsContract; +import android.provider.OpenableColumns; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; @@ -87,7 +90,6 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.AbsListView; import android.widget.BaseAdapter; -import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; @@ -373,50 +375,6 @@ public class ChooserActivity extends ResolverActivity { super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); - Button copyButton = findViewById(R.id.copy_button); - copyButton.setOnClickListener(view -> { - Intent targetIntent = getTargetIntent(); - if (targetIntent == null) { - finish(); - } else { - final String action = targetIntent.getAction(); - - ClipData clipData = null; - if (Intent.ACTION_SEND.equals(action)) { - String extraText = targetIntent.getStringExtra(Intent.EXTRA_TEXT); - Uri extraStream = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); - - if (extraText != null) { - clipData = ClipData.newPlainText(null, extraText); - } else if (extraStream != null) { - clipData = ClipData.newUri(getContentResolver(), null, extraStream); - } else { - Log.w(TAG, "No data available to copy to clipboard"); - return; - } - } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) { - final ArrayList<Uri> streams = targetIntent.getParcelableArrayListExtra( - Intent.EXTRA_STREAM); - clipData = ClipData.newUri(getContentResolver(), null, streams.get(0)); - for (int i = 1; i < streams.size(); i++) { - clipData.addItem(getContentResolver(), new ClipData.Item(streams.get(i))); - } - } else { - // expected to only be visible with ACTION_SEND or ACTION_SEND_MULTIPLE - // so warn about unexpected action - Log.w(TAG, "Action (" + action + ") not supported for copying to clipboard"); - return; - } - - ClipboardManager clipboardManager = (ClipboardManager) getSystemService( - Context.CLIPBOARD_SERVICE); - clipboardManager.setPrimaryClip(clipData); - Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show(); - - finish(); - } - }); - mChooserShownTime = System.currentTimeMillis(); final long systemCost = mChooserShownTime - intentReceivedTime; @@ -474,6 +432,10 @@ public class ChooserActivity extends ResolverActivity { return; } + if (mChooserListAdapter == null || mChooserListAdapter.getCount() == 0) { + return; + } + int previewType = findPreferredContentPreview(targetIntent, getContentResolver()); getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW) @@ -481,6 +443,49 @@ public class ChooserActivity extends ResolverActivity { displayContentPreview(previewType, targetIntent); } + private void onCopyButtonClicked(View v) { + Intent targetIntent = getTargetIntent(); + if (targetIntent == null) { + finish(); + } else { + final String action = targetIntent.getAction(); + + ClipData clipData = null; + if (Intent.ACTION_SEND.equals(action)) { + String extraText = targetIntent.getStringExtra(Intent.EXTRA_TEXT); + Uri extraStream = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + + if (extraText != null) { + clipData = ClipData.newPlainText(null, extraText); + } else if (extraStream != null) { + clipData = ClipData.newUri(getContentResolver(), null, extraStream); + } else { + Log.w(TAG, "No data available to copy to clipboard"); + return; + } + } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) { + final ArrayList<Uri> streams = targetIntent.getParcelableArrayListExtra( + Intent.EXTRA_STREAM); + clipData = ClipData.newUri(getContentResolver(), null, streams.get(0)); + for (int i = 1; i < streams.size(); i++) { + clipData.addItem(getContentResolver(), new ClipData.Item(streams.get(i))); + } + } else { + // expected to only be visible with ACTION_SEND or ACTION_SEND_MULTIPLE + // so warn about unexpected action + Log.w(TAG, "Action (" + action + ") not supported for copying to clipboard"); + return; + } + + ClipboardManager clipboardManager = (ClipboardManager) getSystemService( + Context.CLIPBOARD_SERVICE); + clipboardManager.setPrimaryClip(clipData); + Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show(); + + finish(); + } + } + private void displayContentPreview(@ContentPreviewType int previewType, Intent targetIntent) { switch (previewType) { case CONTENT_PREVIEW_TEXT: @@ -501,6 +506,8 @@ public class ChooserActivity extends ResolverActivity { ViewGroup contentPreviewLayout = findViewById(R.id.content_preview_text_area); contentPreviewLayout.setVisibility(View.VISIBLE); + findViewById(R.id.copy_button).setOnClickListener(this::onCopyButtonClicked); + CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT); if (sharingText == null) { findViewById(R.id.content_preview_text_layout).setVisibility(View.GONE); @@ -510,7 +517,7 @@ public class ChooserActivity extends ResolverActivity { } String previewTitle = targetIntent.getStringExtra(Intent.EXTRA_TITLE); - if (previewTitle == null || previewTitle.trim().isEmpty()) { + if (TextUtils.isEmpty(previewTitle)) { findViewById(R.id.content_preview_title_layout).setVisibility(View.GONE); } else { TextView previewTitleView = findViewById(R.id.content_preview_title); @@ -561,6 +568,7 @@ public class ChooserActivity extends ResolverActivity { if (imageUris.size() == 0) { Log.i(TAG, "Attempted to display image preview area with zero" + " available images detected in EXTRA_STREAM list"); + contentPreviewLayout.setVisibility(View.GONE); return; } @@ -580,15 +588,95 @@ public class ChooserActivity extends ResolverActivity { } } + private static class FileInfo { + public final String name; + public final boolean hasThumbnail; + + FileInfo(String name, boolean hasThumbnail) { + this.name = name; + this.hasThumbnail = hasThumbnail; + } + } + + private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) { + String fileName = null; + boolean hasThumbnail = false; + Cursor cursor = resolver.query(uri, null, null, null, null); + if (cursor != null && cursor.getCount() > 0) { + int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS); + + cursor.moveToFirst(); + fileName = cursor.getString(nameIndex); + if (flagsIndex != -1) { + hasThumbnail = (cursor.getInt(flagsIndex) + & DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL) != 0; + } + } + + if (TextUtils.isEmpty(fileName)) { + fileName = uri.getPath(); + int index = fileName.lastIndexOf('/'); + if (index != -1) { + fileName = fileName.substring(index + 1); + } + } + + return new FileInfo(fileName, hasThumbnail); + } + private void displayFileContentPreview(Intent targetIntent) { - // support coming + ViewGroup contentPreviewLayout = findViewById(R.id.content_preview_file_area); + contentPreviewLayout.setVisibility(View.VISIBLE); + + // TODO(b/120417119): Disable file copy until after moving to sysui, + // due to permissions issues + findViewById(R.id.file_copy_button).setVisibility(View.GONE); + + ContentResolver resolver = getContentResolver(); + TextView fileNameView = findViewById(R.id.content_preview_filename); + String action = targetIntent.getAction(); + if (Intent.ACTION_SEND.equals(action)) { + Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + + FileInfo fileInfo = extractFileInfo(uri, resolver); + fileNameView.setText(fileInfo.name); + + if (fileInfo.hasThumbnail) { + loadUriIntoView(R.id.content_preview_file_thumbnail, uri); + } else { + ImageView fileIconView = findViewById(R.id.content_preview_file_icon); + fileIconView.setVisibility(View.VISIBLE); + fileIconView.setImageResource(R.drawable.ic_doc_generic); + } + } else { + List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + if (uris.size() == 0) { + contentPreviewLayout.setVisibility(View.GONE); + Log.i(TAG, + "Appears to be no uris available in EXTRA_STREAM, removing preview area"); + return; + } + + FileInfo fileInfo = extractFileInfo(uris.get(0), resolver); + int remFileCount = uris.size() - 1; + String fileName = getResources().getQuantityString(R.plurals.file_count, + remFileCount, fileInfo.name, remFileCount); + + fileNameView.setText(fileName); + ImageView fileIconView = findViewById(R.id.content_preview_file_icon); + fileIconView.setVisibility(View.VISIBLE); + fileIconView.setImageResource(R.drawable.ic_file_copy); + } } private RoundedRectImageView loadUriIntoView(int imageResourceId, Uri uri) { RoundedRectImageView imageView = findViewById(imageResourceId); - imageView.setVisibility(View.VISIBLE); Bitmap bmp = loadThumbnail(uri, new Size(200, 200)); - imageView.setImageBitmap(bmp); + if (bmp != null) { + imageView.setVisibility(View.VISIBLE); + imageView.setImageBitmap(bmp); + } return imageView; } @@ -1261,9 +1349,8 @@ public class ChooserActivity extends ResolverActivity { } try { - return ImageUtils.decodeSampledBitmapFromStream(getContentResolver(), - uri, size.getWidth(), size.getHeight()); - } catch (IOException | NullPointerException ex) { + return ImageUtils.loadThumbnail(getContentResolver(), uri, size); + } catch (IOException | NullPointerException | SecurityException ex) { Log.w(TAG, "Error loading preview thumbnail for uri: " + uri.toString(), ex); } return null; diff --git a/core/java/com/android/internal/os/ChildZygoteInit.java b/core/java/com/android/internal/os/ChildZygoteInit.java index cc74863632dd..1f816c18f886 100644 --- a/core/java/com/android/internal/os/ChildZygoteInit.java +++ b/core/java/com/android/internal/os/ChildZygoteInit.java @@ -102,7 +102,7 @@ public class ChildZygoteInit { // are just isolated UIDs in the range, because for the webview zygote, there is no // single range that captures all possible isolated UIDs. // TODO(b/123615476) narrow this down - if (uidGidMin < Process.FIRST_ISOLATED_UID) { + if (uidGidMin < Process.FIRST_APP_ZYGOTE_ISOLATED_UID) { throw new RuntimeException("Passed in UID range does not map to isolated processes."); } diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java index 195ae52ce977..274a5136d8e9 100644 --- a/core/java/com/android/internal/util/ImageUtils.java +++ b/core/java/com/android/internal/util/ImageUtils.java @@ -16,20 +16,25 @@ package com.android.internal.util; +import android.content.ContentProviderClient; import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.ImageDecoder; +import android.graphics.ImageDecoder.ImageInfo; +import android.graphics.ImageDecoder.Source; import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; +import android.util.Size; import java.io.IOException; -import java.io.InputStream; /** * Utility class for image analysis and processing. @@ -166,21 +171,18 @@ public class ImageUtils { /** * @see https://developer.android.com/topic/performance/graphics/load-bitmap */ - public static int calculateInSampleSize(BitmapFactory.Options options, - int reqWidth, int reqHeight) { - // Raw height and width of image - final int height = options.outHeight; - final int width = options.outWidth; + public static int calculateSampleSize(Size currentSize, Size requestedSize) { int inSampleSize = 1; - if (height > reqHeight || width > reqWidth) { - final int halfHeight = height / 2; - final int halfWidth = width / 2; + if (currentSize.getHeight() > requestedSize.getHeight() + || currentSize.getWidth() > requestedSize.getWidth()) { + final int halfHeight = currentSize.getHeight() / 2; + final int halfWidth = currentSize.getWidth() / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. - while ((halfHeight / inSampleSize) >= reqHeight - && (halfWidth / inSampleSize) >= reqWidth) { + while ((halfHeight / inSampleSize) >= requestedSize.getHeight() + && (halfWidth / inSampleSize) >= requestedSize.getWidth()) { inSampleSize *= 2; } } @@ -190,27 +192,27 @@ public class ImageUtils { /** * Load a bitmap, and attempt to downscale to the required size, to save - * on memory. + * on memory. Updated to use newer and more compatible ImageDecoder. * * @see https://developer.android.com/topic/performance/graphics/load-bitmap */ - public static Bitmap decodeSampledBitmapFromStream(ContentResolver resolver, - Uri uri, int reqWidth, int reqHeight) throws IOException { - - final BitmapFactory.Options options = new BitmapFactory.Options(); - try (InputStream is = resolver.openInputStream(uri)) { - // First decode with inJustDecodeBounds=true to check dimensions - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(is, null, options); - - options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); - } - - // need to do this twice as the InputStream is consumed in the first call, - // and not all InputStreams support marks - try (InputStream is = resolver.openInputStream(uri)) { - options.inJustDecodeBounds = false; - return BitmapFactory.decodeStream(is, null, options); + public static Bitmap loadThumbnail(ContentResolver resolver, Uri uri, Size size) + throws IOException { + + try (ContentProviderClient client = resolver.acquireContentProviderClient(uri)) { + final Bundle opts = new Bundle(); + opts.putParcelable(ContentResolver.EXTRA_SIZE, Point.convert(size)); + + return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> { + return client.openTypedAssetFile(uri, "image/*", opts, null); + }), (ImageDecoder decoder, ImageInfo info, Source source) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + + final int sample = calculateSampleSize(info.getSize(), size); + if (sample > 1) { + decoder.setTargetSampleSize(sample); + } + }); } } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 345058bd4f6f..af0b7c307ef6 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -287,8 +287,8 @@ cc_library_shared { "libsoundtrigger", "libminikin", "libprocessgroup", - "libnativebridge", - "libnativeloader", + "libnativebridge_lazy", + "libnativeloader_lazy", "libmemunreachable", "libhidlbase", "libhidltransport", diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp index a9d75fd6aff7..15d1944205b6 100644 --- a/core/jni/android_opengl_EGL14.cpp +++ b/core/jni/android_opengl_EGL14.cpp @@ -107,6 +107,7 @@ fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { if (obj == NULL){ jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null."); + return nullptr; } jlong handle = _env->CallLongMethod(obj, mid); @@ -238,6 +239,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return false; } return (jboolean)_returnValue; } @@ -335,6 +337,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return false; } return (jboolean)_returnValue; } @@ -454,6 +457,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return false; } return (jboolean)_returnValue; } @@ -509,6 +513,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return false; } return (jboolean)_returnValue; } @@ -582,6 +587,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue); } @@ -664,6 +670,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue); } @@ -721,6 +728,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue); } @@ -731,7 +739,7 @@ android_eglCreatePixmapSurface (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jint pixmap, jintArray attrib_list_ref, jint offset) { jniThrowException(_env, "java/lang/UnsupportedOperationException", "eglCreatePixmapSurface"); - return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0); + return nullptr; } /* EGLBoolean eglDestroySurface ( EGLDisplay dpy, EGLSurface surface ) */ @@ -800,6 +808,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return false; } return (jboolean)_returnValue; } @@ -898,6 +907,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue); } @@ -1034,6 +1044,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglcontextClass, eglcontextConstructor, _returnValue); } @@ -1152,6 +1163,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return false; } return (jboolean)_returnValue; } diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index e2e66ceb6fbe..95f99b760382 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -55,6 +55,11 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appNa devOptInChars.c_str(), rulesFd_native, rulesOffset, rulesLength); } +bool shouldUseAngle_native(JNIEnv* env, jobject clazz, jstring appName) { + ScopedUtfChars appNameChars(env, appName); + return android::GraphicsEnv::getInstance().shouldUseAngle(appNameChars.c_str()); +} + void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) { android::NativeLoaderNamespace* appNamespace = android::FindNativeLoaderNamespaceByClassLoader( env, classLoader); @@ -81,6 +86,7 @@ const JNINativeMethod g_methods[] = { { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) }, { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) }, { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) }, + { "getShouldUseAngle", "(Ljava/lang/String;)Z", reinterpret_cast<void*>(shouldUseAngle_native) }, { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) }, { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) }, { "setDebugLayersGLES", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayersGLES_native) }, diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index d04db92294d7..5cecf66a593c 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -742,7 +742,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, return; } - if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, false))) { + if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, true))) { if (mount_mode == MOUNT_EXTERNAL_FULL || mount_mode == MOUNT_EXTERNAL_LEGACY) { storageSource = (mount_mode == MOUNT_EXTERNAL_FULL) ? "/mnt/runtime/full" : "/mnt/runtime/write"; diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 30b9b7847fb6..5497b8665cf0 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -610,6 +610,11 @@ enum Action { // CATEGORY: SETTINGS // OS: Q ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = 1646; + + // ACTION: An interaction with a Slice or other component in the Panel. + // CATEGORY: SETTINGS + // OS: Q + ACTION_PANEL_INTERACTION = 1658; } /** @@ -2214,4 +2219,16 @@ enum PageId { // CATEGORY: SETTINGS // OS: Q SET_NEW_PASSWORD_ACTIVITY = 1644; + + // Panel for Internet Connectivity + PANEL_INTERNET_CONNECTIVITY = 1654; + + // Panel for Volume + PANEL_VOLUME = 1655; + + // Panel for NFC + PANEL_NFC = 1656; + + // Panel for Media Output + PANEL_MEDIA_OUTPUT = 1657; } diff --git a/media/java/android/media/session/MediaSessionProviderService.java b/core/proto/android/hardware/sensor/assist/enums.proto index 9a346ff4a12e..8c5841a32d54 100644 --- a/media/java/android/media/session/MediaSessionProviderService.java +++ b/core/proto/android/hardware/sensor/assist/enums.proto @@ -14,22 +14,21 @@ * limitations under the License. */ -package android.media.session; +syntax = "proto2"; +package android.hardware.sensor.assist; -import android.app.Service; -import android.content.Intent; -import android.os.IBinder; +option java_outer_classname = "AssistGestureProtoEnums"; +option java_multiple_files = true; -/** - * Abstract class for mainline module services. - * - * @hide // TODO: Make it as a @SystemApi - */ -public abstract class MediaSessionProviderService extends Service { - - @Override - public IBinder onBind(Intent intent) { - // TODO: Return IMediaSessionProviderService.Stub() - return null; - } +enum AssistGestureStageEnum { + ASSIST_GESTURE_STAGE_UNKNOWN = 0; + ASSIST_GESTURE_STAGE_PROGRESS = 1; + ASSIST_GESTURE_STAGE_PRIMED = 2; + ASSIST_GESTURE_STAGE_DETECTED = 3; } + +enum AssistGestureFeedbackEnum { + ASSIST_GESTURE_FEEDBACK_UNKNOWN = 0; + ASSIST_GESTURE_FEEDBACK_NOT_USED = 1; + ASSIST_GESTURE_FEEDBACK_USED = 2; +}
\ No newline at end of file diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index d5776534bb90..c9957f369473 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -448,6 +448,11 @@ message GlobalSettingsProto { optional SettingProto game_driver_whitelist = 12; // ANGLE - List of Apps that can check ANGLE rules optional SettingProto angle_whitelist = 13; + // Game Driver - List of blacklists, each blacklist is a blacklist for + // a specific Game Driver version + optional SettingProto game_driver_blacklists = 14; + // ANGLE - Show a dialog box when ANGLE is selected for the currently running PKG + optional SettingProto show_angle_in_use_dialog = 15; } optional Gpu gpu = 59; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 34ec92e68511..5b74d90608f7 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2151,13 +2151,13 @@ android:label="@string/permlab_disableKeyguard" android:protectionLevel="normal" /> - <!-- Allows an application to get the screen lock complexity and prompt users to update the + <!-- Allows an application to request the screen lock complexity and prompt users to update the screen lock to a certain complexity level. <p>Protection level: normal --> - <permission android:name="android.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY" - android:label="@string/permlab_getAndRequestScreenLockComplexity" - android:description="@string/permdesc_getAndRequestScreenLockComplexity" + <permission android:name="android.permission.REQUEST_SCREEN_LOCK_COMPLEXITY" + android:label="@string/permlab_requestScreenLockComplexity" + android:description="@string/permdesc_requestScreenLockComplexity" android:protectionLevel="normal" /> <!-- ================================== --> diff --git a/core/res/res/drawable/bottomsheet_background.xml b/core/res/res/drawable/bottomsheet_background.xml index bc32ba6e3896..3a8ad71187a0 100644 --- a/core/res/res/drawable/bottomsheet_background.xml +++ b/core/res/res/drawable/bottomsheet_background.xml @@ -16,7 +16,7 @@ <shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android"> <corners - android:topLeftRadius="?attr/dialogCornerRadius" - android:topRightRadius="?attr/dialogCornerRadius" /> + android:topLeftRadius="@dimen/config_bottomDialogCornerRadius" + android:topRightRadius="@dimen/config_bottomDialogCornerRadius"/> <solid android:color="?attr/colorBackgroundFloating" /> </shape> diff --git a/core/res/res/drawable/ic_file_copy.xml b/core/res/res/drawable/ic_file_copy.xml new file mode 100644 index 000000000000..b6d5e7328c40 --- /dev/null +++ b/core/res/res/drawable/ic_file_copy.xml @@ -0,0 +1,24 @@ +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM15,5l6,6v10c0,1.1 -0.9,2 -2,2L7.99,23C6.89,23 6,22.1 6,21l0.01,-14c0,-1.1 0.89,-2 1.99,-2h7zM14,12h5.5L14,6.5L14,12z" + android:fillColor="#FF737373"/> +</vector> diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 14a5310a4ff2..3683bfd02e87 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -213,6 +213,65 @@ </LinearLayout> </LinearLayout> + <!-- Layout Option 3: File preview, icon, filename, copy--> + <LinearLayout + android:id="@+id/content_preview_file_area" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingBottom="@dimen/chooser_view_spacing" + android:visibility="gone" + android:background="?attr/colorBackgroundFloating"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingLeft="@dimen/chooser_edge_margin_normal" + android:paddingRight="@dimen/chooser_edge_margin_normal" + android:layout_marginBottom="@dimen/chooser_view_spacing" + android:id="@+id/content_preview_file_layout"> + + <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView" + android:id="@+id/content_preview_file_thumbnail" + android:layout_width="75dp" + android:layout_height="75dp" + android:layout_marginRight="16dp" + android:adjustViewBounds="true" + android:layout_gravity="center_vertical" + android:gravity="center" + android:scaleType="centerCrop" + android:visibility="gone"/> + <ImageView + android:id="@+id/content_preview_file_icon" + android:layout_width="36dp" + android:layout_height="36dp" + android:layout_marginRight="16dp" + android:adjustViewBounds="true" + android:layout_gravity="center_vertical" + android:gravity="center" + android:scaleType="fitCenter" + android:visibility="gone"/> + <TextView + android:id="@+id/content_preview_filename" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:ellipsize="middle" + android:gravity="start|top" + android:paddingRight="24dp" + android:singleLine="true"/> + <Button + android:id="@+id/file_copy_button" + android:layout_width="24dp" + android:layout_height="24dp" + android:gravity="center" + android:layout_gravity="center_vertical" + android:background="@drawable/ic_content_copy_gm2"/> + </LinearLayout> + </LinearLayout> + <ListView android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 130f6291b516..d14164f69850 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3425,8 +3425,14 @@ --> <string-array translatable="false" name="config_convert_to_emergency_number_map" /> - <!-- An array of packages for which notifications cannot be blocked. --> - <string-array translatable="false" name="config_nonBlockableNotificationPackages" /> + <!-- An array of packages for which notifications cannot be blocked. + Should only be used for core device functionality that must not be + rendered inoperative for safety reasons, like the phone dialer and + SMS handler. --> + <string-array translatable="false" name="config_nonBlockableNotificationPackages"> + <item>com.android.dialer</item> + <item>com.android.messaging</item> + </string-array> <!-- An array of packages which can listen for notifications on low ram devices. --> <string-array translatable="false" name="config_allowedManagedServicesOnLowRamDevices" /> @@ -3897,4 +3903,22 @@ The ambient color temperature (in cct) to which we fall back when the ambient brightness drops beneath a certain threshold. --> <item name="config_displayWhiteBalanceLowLightAmbientColorTemperature" format="float" type="dimen">6500.0</item> + + <!-- See DisplayWhiteBalanceController. + A float array containing a list of ambient color temperatures, in Kelvin. This array, + together with config_displayWhiteBalanceDisplayTemperatureValues, is used to generate a + lookup table used in DisplayWhiteBalanceController. This lookup table is used to map + ambient color temperature readings to a target color temperature for the display. + This table is optional. If used, this array must, + 1) Contain at least two entries + 2) Be the same length as config_displayWhiteBalanceDisplayTemperatureValues. --> + <array name="config_displayWhiteBalanceAmbientTemperatureValues"> + </array> + + <!-- See DisplayWhiteBalanceController. + An array containing a list of display color temperatures, in Kelvin. See + config_displayWhiteBalanceAmbientTemperatureValues for additional details. + The same restrictions apply to this array. --> + <array name="config_displayWhiteBalanceDisplayTemperatureValues"> + </array> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6d5bd4ba1645..0cdf38849d8b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1406,9 +1406,9 @@ re-enables the keylock when the call is finished.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] --> - <string name="permlab_getAndRequestScreenLockComplexity">get and request screen lock complexity</string> + <string name="permlab_requestScreenLockComplexity">request screen lock complexity</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] --> - <string name="permdesc_getAndRequestScreenLockComplexity">Allows the app to learn the screen + <string name="permdesc_requestScreenLockComplexity">Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the @@ -5284,4 +5284,9 @@ <!-- Strings for car --> <!-- String displayed when loading a user in the car [CHAR LIMIT=30] --> <string name="car_loading_profile">Loading</string> + + <plurals name="file_count"> + <item quantity="one"><xliff:g id="file_name">%s</xliff:g> + <xliff:g id="count">%d</xliff:g> file</item> + <item quantity="other"><xliff:g id="file_name">%s</xliff:g> + <xliff:g id="count">%d</xliff:g> files</item> + </plurals> </resources> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 0dc54e091f9d..93068ea975bd 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -41,9 +41,7 @@ easier. <item name="textAppearance">?attr/textAppearanceButton</item> <item name="textColor">@color/btn_colored_text_material</item> </style> - <style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView"> - <item name="fontFamily">@string/config_bodyFontFamily</item> - </style> + <style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView" /> <style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/> <style name="Widget.DeviceDefault.AutoCompleteTextView" parent="Widget.Material.AutoCompleteTextView"/> <style name="Widget.DeviceDefault.CompoundButton.CheckBox" parent="Widget.Material.CompoundButton.CheckBox"/> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8e251fd4ea6f..a1bafbf8de69 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -50,6 +50,11 @@ <java-symbol type="id" name="characterPicker" /> <java-symbol type="id" name="clearDefaultHint" /> <java-symbol type="id" name="contentPanel" /> + <java-symbol type="id" name="content_preview_file_area" /> + <java-symbol type="id" name="content_preview_file_icon" /> + <java-symbol type="id" name="content_preview_file_layout" /> + <java-symbol type="id" name="content_preview_file_thumbnail" /> + <java-symbol type="id" name="content_preview_filename" /> <java-symbol type="id" name="content_preview_image_area" /> <java-symbol type="id" name="content_preview_image_1_large" /> <java-symbol type="id" name="content_preview_image_2_large" /> @@ -62,6 +67,7 @@ <java-symbol type="id" name="content_preview_title" /> <java-symbol type="id" name="content_preview_title_layout" /> <java-symbol type="id" name="copy_button" /> + <java-symbol type="id" name="file_copy_button" /> <java-symbol type="id" name="current_scene" /> <java-symbol type="id" name="scene_layoutid_cache" /> <java-symbol type="id" name="customPanel" /> @@ -1266,6 +1272,7 @@ <java-symbol type="string" name="tooltip_popup_title" /> <java-symbol type="plurals" name="bugreport_countdown" /> + <java-symbol type="plurals" name="file_count" /> <java-symbol type="plurals" name="last_num_days" /> <java-symbol type="plurals" name="matches_found" /> <java-symbol type="plurals" name="restr_pin_countdown" /> @@ -1307,6 +1314,7 @@ <java-symbol type="drawable" name="default_wallpaper" /> <java-symbol type="drawable" name="default_lock_wallpaper" /> <java-symbol type="drawable" name="indicator_input_error" /> + <java-symbol type="drawable" name="ic_file_copy" /> <java-symbol type="drawable" name="popup_bottom_dark" /> <java-symbol type="drawable" name="popup_bottom_bright" /> <java-symbol type="drawable" name="popup_bottom_medium" /> @@ -3643,6 +3651,7 @@ <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" /> <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" /> <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" /> - + <java-symbol type="array" name="config_displayWhiteBalanceAmbientTemperatureValues" /> + <java-symbol type="array" name="config_displayWhiteBalanceDisplayTemperatureValues" /> <java-symbol type="drawable" name="ic_action_open" /> </resources> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 206682a1955b..4d2f005998c3 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -491,8 +491,10 @@ public class SettingsBackupTest { Settings.Global.GAME_DRIVER_ALL_APPS, Settings.Global.GAME_DRIVER_OPT_IN_APPS, Settings.Global.GAME_DRIVER_OPT_OUT_APPS, + Settings.Global.GAME_DRIVER_BLACKLISTS, Settings.Global.GAME_DRIVER_BLACKLIST, Settings.Global.GAME_DRIVER_WHITELIST, + Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, Settings.Global.GPU_DEBUG_LAYER_APP, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT, diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index 3d59835a6719..7f104b1b0a14 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -409,8 +409,7 @@ public class ChooserActivityTest { @Test public void copyTextToClipboard() throws Exception { Intent sendIntent = createSendTextIntent(); - List<ResolvedComponentInfo> resolvedComponentInfos = - createResolvedComponentsForTestWithOtherProfile(1); + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent( Mockito.anyBoolean(), @@ -421,6 +420,7 @@ public class ChooserActivityTest { .launchActivity(Intent.createChooser(sendIntent, null)); waitForIdle(); + onView(withId(R.id.copy_button)).check(matches(isDisplayed())); onView(withId(R.id.copy_button)).perform(click()); ClipboardManager clipboard = (ClipboardManager) activity.getSystemService( Context.CLIPBOARD_SERVICE); @@ -439,8 +439,9 @@ public class ChooserActivityTest { ArrayList<Uri> uris = new ArrayList<>(); uris.add(uri); - Intent sendIntent = createSendImageIntentWithPreview(uris); + Intent sendIntent = createSendUriIntentWithPreview(uris); sOverrides.previewThumbnail = createBitmap(); + sOverrides.isImageType = true; List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); @@ -464,8 +465,9 @@ public class ChooserActivityTest { uris.add(uri); uris.add(uri); - Intent sendIntent = createSendImageIntentWithPreview(uris); + Intent sendIntent = createSendUriIntentWithPreview(uris); sOverrides.previewThumbnail = createBitmap(); + sOverrides.isImageType = true; List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); @@ -492,8 +494,9 @@ public class ChooserActivityTest { uris.add(uri); uris.add(uri); - Intent sendIntent = createSendImageIntentWithPreview(uris); + Intent sendIntent = createSendUriIntentWithPreview(uris); sOverrides.previewThumbnail = createBitmap(); + sOverrides.isImageType = true; List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); @@ -538,12 +541,11 @@ public class ChooserActivityTest { ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); mActivityRule.launchActivity(Intent.createChooser(sendIntent, "empty preview logger test")); waitForIdle(); - verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture()); + + verify(mockLogger, Mockito.times(1)).write(logMakerCaptor.capture()); // First invocation is from onCreate - assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); - assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(), - is(CONTENT_PREVIEW_TEXT)); + assertThat(logMakerCaptor.getAllValues().get(0).getCategory(), + is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); } @Test @@ -552,9 +554,17 @@ public class ChooserActivityTest { MetricsLogger mockLogger = sOverrides.metricsLogger; ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent( + Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); waitForIdle(); - verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture()); + verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture()); // First invocation is from onCreate assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); @@ -570,8 +580,9 @@ public class ChooserActivityTest { ArrayList<Uri> uris = new ArrayList<>(); uris.add(uri); - Intent sendIntent = createSendImageIntentWithPreview(uris); + Intent sendIntent = createSendUriIntentWithPreview(uris); sOverrides.previewThumbnail = createBitmap(); + sOverrides.isImageType = true; List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); @@ -595,6 +606,51 @@ public class ChooserActivityTest { is(CONTENT_PREVIEW_IMAGE)); } + @Test + public void oneVisibleFilePreview() throws InterruptedException { + Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf"); + + ArrayList<Uri> uris = new ArrayList<>(); + uris.add(uri); + + Intent sendIntent = createSendUriIntentWithPreview(uris); + + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); + onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf"))); + onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); + } + + + @Test + public void moreThanOneVisibleFilePreview() throws InterruptedException { + Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf"); + + ArrayList<Uri> uris = new ArrayList<>(); + uris.add(uri); + uris.add(uri); + uris.add(uri); + + Intent sendIntent = createSendUriIntentWithPreview(uris); + + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); + onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf + 2 files"))); + onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); + } + private Intent createSendTextIntent() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); @@ -615,7 +671,7 @@ public class ChooserActivityTest { return sendIntent; } - private Intent createSendImageIntentWithPreview(ArrayList<Uri> uris) { + private Intent createSendUriIntentWithPreview(ArrayList<Uri> uris) { Intent sendIntent = new Intent(); if (uris.size() > 1) { diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index f60467bd3df2..096b78b95fbd 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -89,11 +89,7 @@ public class ChooserWrapperActivity extends ChooserActivity { @Override protected boolean isImageType(String mimeType) { - if (sOverrides.previewThumbnail != null) { - return true; - } - - return super.isImageType(mimeType); + return sOverrides.isImageType; } @Override @@ -112,6 +108,7 @@ public class ChooserWrapperActivity extends ChooserActivity { public Function<TargetInfo, Boolean> onSafelyStartCallback; public ResolverListController resolverListController; public Boolean isVoiceInteraction; + public boolean isImageType; public Bitmap previewThumbnail; public MetricsLogger metricsLogger; @@ -120,6 +117,7 @@ public class ChooserWrapperActivity extends ChooserActivity { isVoiceInteraction = null; createPackageManager = null; previewThumbnail = null; + isImageType = false; resolverListController = mock(ResolverListController.class); metricsLogger = mock(MetricsLogger.class); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 6770ae19ce3d..738221340aba 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -209,21 +209,14 @@ applications that come with the platform <privapp-permissions package="com.android.mainline.networkstack"> <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/> - <permission name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"/> <permission name="android.permission.CONNECTIVITY_INTERNAL"/> <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> <permission name="android.permission.CONTROL_VPN"/> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.LOCAL_MAC_ADDRESS"/> - <permission name="android.permission.MANAGE_IPSEC_TUNNELS"/> - <permission name="android.permission.MANAGE_NETWORK_POLICY"/> <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/> <permission name="android.permission.MANAGE_USB"/> - <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/> - <permission name="android.permission.NETWORK_SETTINGS"/> - <permission name="android.permission.NETWORK_STACK" /> - <permission name="android.permission.NET_TUNNELING"/> <permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/> - <permission name="android.permission.PEERS_MAC_ADDRESS"/> <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/> <permission name="android.permission.READ_PRECISE_PHONE_STATE"/> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> @@ -261,6 +254,7 @@ applications that come with the platform <permission name="android.permission.CHANGE_LOWPAN_STATE"/> <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/> <permission name="android.permission.CLEAR_APP_CACHE"/> + <permission name="android.permission.ACCESS_INSTANT_APPS" /> <permission name="android.permission.CONNECTIVITY_INTERNAL"/> <permission name="android.permission.DELETE_CACHE_FILES"/> <permission name="android.permission.DELETE_PACKAGES"/> diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 56be05b51d1a..ef9255f66695 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -127,7 +127,10 @@ public class Typeface { static Typeface sDefaultTypeface; // Following two fields are not used but left for hiddenapi private list - @UnsupportedAppUsage + /** + * Use {@link SystemFonts#getAvailableFonts()} instead. + */ + @UnsupportedAppUsage(trackingBug = 123769347) static final Map<String, Typeface> sSystemFontMap; // We cannot support sSystemFallbackMap since we will migrate to public FontFamily API. diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 991847ad27fa..6ecb62140532 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -44,6 +44,7 @@ import android.graphics.RectF; import android.graphics.Shader; import android.graphics.SweepGradient; import android.graphics.Xfermode; +import android.os.Build; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; @@ -156,12 +157,12 @@ public class GradientDrawable extends Drawable { private static final float DEFAULT_INNER_RADIUS_RATIO = 3.0f; private static final float DEFAULT_THICKNESS_RATIO = 9.0f; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) private GradientState mGradientState; @UnsupportedAppUsage private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124051827) private Rect mPadding; @UnsupportedAppUsage private Paint mStrokePaint; // optional, set by the caller @@ -670,7 +671,28 @@ public class GradientDrawable extends Drawable { * @see #setColor(int) */ public void setColors(@ColorInt int[] colors) { + setColors(colors, null); + } + + /** + * Sets the colors and offsets used to draw the gradient. + * <p> + * Each color is specified as an ARGB integer and the array must contain at + * least 2 colors. + * <p> + * <strong>Note</strong>: changing colors will affect all instances of a + * drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing the colors. + * + * @param colors an array containing 2 or more ARGB colors + * @param offsets optional array of floating point parameters representing the positions + * of the colors. Null evenly disperses the colors + * @see #mutate() + * @see #setColors(int[]) + */ + public void setColors(@ColorInt int[] colors, @Nullable float[] offsets) { mGradientState.setGradientColors(colors); + mGradientState.mPositions = offsets; mGradientIsDirty = true; invalidateSelf(); } @@ -849,6 +871,115 @@ public class GradientDrawable extends Drawable { } } + /** + * Inner radius of the ring expressed as a ratio of the ring's width. + * + * @see #getInnerRadiusRatio() + * @attr ref android.R.styleable#GradientDrawable_innerRadiusRatio + */ + public void setInnerRadiusRatio(float innerRadiusRatio) { + mGradientState.mInnerRadiusRatio = innerRadiusRatio; + mPathIsDirty = true; + invalidateSelf(); + } + + /** + * Return the inner radius of the ring expressed as a ratio of the ring's width. + * + * @see #setInnerRadiusRatio(float) + * @attr ref android.R.styleable#GradientDrawable_innerRadiusRatio + */ + public float getInnerRadiusRatio() { + return mGradientState.mInnerRadiusRatio; + } + + /** + * Configure the inner radius of the ring. + * + * @see #getInnerRadius() + * @attr ref android.R.styleable#GradientDrawable_innerRadius + */ + public void setInnerRadius(int innerRadius) { + mGradientState.mInnerRadius = innerRadius; + mPathIsDirty = true; + invalidateSelf(); + } + + /** + * Retrn the inner radius of the ring + * + * @see #setInnerRadius(int) + * @attr ref android.R.styleable#GradientDrawable_innerRadius + */ + public int getInnerRadius() { + return mGradientState.mInnerRadius; + } + + /** + * Configure the thickness of the ring expressed as a ratio of the ring's width. + * + * @see #getThicknessRatio() + * @attr ref android.R.styleable#GradientDrawable_thicknessRatio + */ + public void setThicknessRatio(float thicknessRatio) { + mGradientState.mThicknessRatio = thicknessRatio; + mPathIsDirty = true; + invalidateSelf(); + } + + /** + * Return the thickness ratio of the ring expressed as a ratio of the ring's width. + * + * @see #setThicknessRatio(float) + * @attr ref android.R.styleable#GradientDrawable_thicknessRatio + */ + public float getThicknessRatio() { + return mGradientState.mThicknessRatio; + } + + /** + * Configure the thickness of the ring. + * + * @attr ref android.R.styleable#GradientDrawable_thickness + */ + public void setThickness(int thickness) { + mGradientState.mThickness = thickness; + mPathIsDirty = true; + invalidateSelf(); + } + + /** + * Return the thickness of the ring + * + * @see #setThickness(int) + * @attr ref android.R.styleable#GradientDrawable_thickness + */ + public int getThickness() { + return mGradientState.mThickness; + } + + /** + * Configure the padding of the gradient shape + * @param left Left padding of the gradient shape + * @param top Top padding of the gradient shape + * @param right Right padding of the gradient shape + * @param bottom Bottom padding of the gradient shape + * + * @attr ref android.R.styleable#GradientDrawablePadding_left + * @attr ref android.R.styleable#GradientDrawablePadding_top + * @attr ref android.R.styleable#GradientDrawablePadding_right + * @attr ref android.R.styleable#GradientDrawablePadding_bottom + */ + public void setPadding(int left, int top, int right, int bottom) { + if (mGradientState.mPadding == null) { + mGradientState.mPadding = new Rect(); + } + + mGradientState.mPadding.set(left, top, right, bottom); + mPadding = mGradientState.mPadding; + invalidateSelf(); + } + private Path buildRing(GradientState st) { if (mRingPath != null && (!st.mUseLevelForShape || !mPathIsDirty)) return mRingPath; mPathIsDirty = false; @@ -1814,46 +1945,46 @@ public class GradientDrawable extends Drawable { final static class GradientState extends ConstantState { public @Config int mChangingConfigurations; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public @Shape int mShape = RECTANGLE; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public @GradientType int mGradient = LINEAR_GRADIENT; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public int mAngle = 0; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public Orientation mOrientation; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public ColorStateList mSolidColors; public ColorStateList mStrokeColors; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public @ColorInt int[] mGradientColors; public @ColorInt int[] mTempColors; // no need to copy public float[] mTempPositions; // no need to copy @UnsupportedAppUsage public float[] mPositions; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public int mStrokeWidth = -1; // if >= 0 use stroking. - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public float mStrokeDashWidth = 0.0f; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public float mStrokeDashGap = 0.0f; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public float mRadius = 0.0f; // use this if mRadiusArray is null - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public float[] mRadiusArray = null; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public Rect mPadding = null; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public int mWidth = -1; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public int mHeight = -1; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public float mInnerRadiusRatio = DEFAULT_INNER_RADIUS_RATIO; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050218) public float mThicknessRatio = DEFAULT_THICKNESS_RATIO; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public int mInnerRadius = -1; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050218) public int mThickness = -1; public boolean mDither = false; public Insets mOpticalInsets = Insets.NONE; diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h index 237fc622dd2e..b93f07853242 100644 --- a/libs/hwui/FrameMetricsObserver.h +++ b/libs/hwui/FrameMetricsObserver.h @@ -23,7 +23,7 @@ namespace uirenderer { class FrameMetricsObserver : public VirtualLightRefBase { public: - virtual void notify(const int64_t* buffer); + virtual void notify(const int64_t* buffer) = 0; }; } // namespace uirenderer diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index 59eff6401deb..a545f2edd1bf 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -96,7 +96,7 @@ public final class GnssMeasurement implements Parcelable { STATE_TOW_DECODED, STATE_MSEC_AMBIGUOUS, STATE_SYMBOL_SYNC, STATE_GLO_STRING_SYNC, STATE_GLO_TOD_DECODED, STATE_BDS_D2_BIT_SYNC, STATE_BDS_D2_SUBFRAME_SYNC, STATE_GAL_E1BC_CODE_LOCK, STATE_GAL_E1C_2ND_CODE_LOCK, STATE_GAL_E1B_PAGE_SYNC, - STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN + STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN, STATE_2ND_CODE_LOCK }) @Retention(RetentionPolicy.SOURCE) public @interface State {} @@ -144,6 +144,9 @@ public final class GnssMeasurement implements Parcelable { */ public static final int STATE_GLO_TOD_KNOWN = (1<<15); + /** This GNSS measurement's tracking state has secondary code lock. */ + public static final int STATE_2ND_CODE_LOCK = (1 << 16); + /** * All the GNSS receiver state flags, for bit masking purposes (not a sensible state for any * individual measurement.) @@ -517,6 +520,9 @@ public final class GnssMeasurement implements Parcelable { if ((mState & STATE_SBAS_SYNC) != 0) { builder.append("SbasSync|"); } + if ((mState & STATE_2ND_CODE_LOCK) != 0) { + builder.append("2ndCodeLock|"); + } int remainingStates = mState & ~STATE_ALL; if (remainingStates > 0) { @@ -531,96 +537,315 @@ public final class GnssMeasurement implements Parcelable { /** * Gets the received GNSS satellite time, at the measurement time, in nanoseconds. * - * <p>For GPS & QZSS, this is: - * <ul> - * <li>Received GPS Time-of-Week at the measurement time, in nanoseconds.</li> - * <li>The value is relative to the beginning of the current GPS week.</li> - * </ul> - * - * <p>Given the highest sync state that can be achieved, per each satellite, valid range - * for this field can be: - * <pre> - * Searching : [ 0 ] : STATE_UNKNOWN - * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set - * Bit sync : [ 0 20ms ] : STATE_BIT_SYNC is set - * Subframe sync : [ 0 6s ] : STATE_SUBFRAME_SYNC is set - * TOW decoded : [ 0 1week ] : STATE_TOW_DECODED is set - * TOW Known : [ 0 1week ] : STATE_TOW_KNOWN set</pre> - * - * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has + * <p>The received satellite time is relative to the beginning of the system week for all + * constellations except for Glonass where it is relative to the beginning of the Glonass + * system day. + * + * <p>The table below indicates the valid range of the received GNSS satellite time. These + * ranges depend on the constellation and code being tracked and the state of the tracking + * algorithms given by the {@link #getState} method. The minimum value of this field is zero. + * The maximum value of this field is determined by looking across all of the state flags + * that are set, for the given constellation and code type, and finding the the maximum value + * in this table. + * + * <p>For example, for GPS L1 C/A, if STATE_TOW_KNOWN is set, this field can be any value from 0 + * to 1 week (in nanoseconds), and for GAL E1B code, if only STATE_GAL_E1BC_CODE_LOCK is set, + * then this field can be any value from 0 to 4 milliseconds (in nanoseconds.) + * + * <table border="1"> + * <thead> + * <tr> + * <td /> + * <td colspan="3"><strong>GPS/QZSS</strong></td> + * <td><strong>GLNS</strong></td> + * <td colspan="2"><strong>BDS</strong></td> + * <td colspan="3"><strong>GAL</strong></td> + * <td><strong>SBAS</strong></td> + * </tr> + * <tr> + * <td><strong>State Flag</strong></td> + * <td><strong>L1 C/A</strong></td> + * <td><strong>L5I</strong></td> + * <td><strong>L5Q</strong></td> + * <td><strong>L1OF</strong></td> + * <td><strong>B1I (D1)</strong></td> + * <td><strong>B1I (D2)</strong></td> + * <td><strong>E1B</strong></td> + * <td><strong>E1C</strong></td> + * <td><strong>E5AQ</strong></td> + * <td><strong>L1 C/A</strong></td> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td> + * <strong>STATE_UNKNOWN</strong> + * </td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_CODE_LOCK</strong> + * </td> + * <td>1 ms</td> + * <td>1 ms</td> + * <td>1 ms</td> + * <td>1 ms</td> + * <td>1 ms</td> + * <td>1 ms</td> + * <td>-</td> + * <td>-</td> + * <td>1 ms</td> + * <td>1 ms</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_SYMBOL_SYNC</strong> + * </td> + * <td>20 ms (optional)</td> + * <td>10 ms</td> + * <td>1 ms (optional)</td> + * <td>10 ms</td> + * <td>20 ms (optional)</td> + * <td>2 ms</td> + * <td>4 ms (optional)</td> + * <td>4 ms (optional)</td> + * <td>1 ms (optional)</td> + * <td>2 ms</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_BIT_SYNC</strong> + * </td> + * <td>20 ms</td> + * <td>20 ms</td> + * <td>1 ms (optional)</td> + * <td>20 ms</td> + * <td>20 ms</td> + * <td>-</td> + * <td>8 ms</td> + * <td>-</td> + * <td>1 ms (optional)</td> + * <td>4 ms</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_SUBFRAME_SYNC</strong> + * </td> + * <td>6s</td> + * <td>6s</td> + * <td>-</td> + * <td>2 s</td> + * <td>6 s</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>100 ms</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_TOW_DECODED</strong> + * </td> + * <td colspan="2">1 week</td> + * <td>-</td> + * <td>1 day</td> + * <td colspan="2">1 week</td> + * <td colspan="2">1 week</td> + * <td>-</td> + * <td>1 week</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_TOW_KNOWN</strong> + * </td> + * <td colspan="3">1 week</td> + * <td>1 day</td> + * <td colspan="2">1 week</td> + * <td colspan="3">1 week</td> + * <td>1 week</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GLO_STRING_SYNC</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>2 s</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GLO_TOD_DECODED</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>1 day</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GLO_TOD_KNOWN</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>1 day</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_BDS_D2_BIT_SYNC</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>2 ms</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_BDS_D2_SUBFRAME_SYNC</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>600 ms</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GAL_E1BC_CODE_LOCK</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>4 ms</td> + * <td>4 ms</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GAL_E1C_2ND_CODE_LOCK</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>100 ms</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_2ND_CODE_LOCK</strong> + * </td> + * <td>-</td> + * <td>10 ms (optional)</td> + * <td>20 ms</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>100 ms (optional)</td> + * <td>100 ms</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GAL_E1B_PAGE_SYNC</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>2 s</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_SBAS_SYNC</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>1 s</td> + * </tr> + * </tbody> + * </table> + * + * <p>Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has * been determined from other sources. If TOW decoded is set then TOW Known must also be set. * - * <p>Note well: if there is any ambiguity in integer millisecond, {@code STATE_MSEC_AMBIGUOUS} - * must be set accordingly, in the 'state' field. - * - * <p>This value must be populated if 'state' != {@code STATE_UNKNOWN}. + * <p>Note well: if there is any ambiguity in integer millisecond, STATE_MSEC_AMBIGUOUS must be + * set accordingly, in the 'state' field. This value must be populated, unless the 'state' == + * STATE_UNKNOWN. * - * <p>For Glonass, this is: + * <p>Note on optional flags: * <ul> - * <li>Received Glonass time of day, at the measurement time in nanoseconds.</li> + * <li> For L1 C/A and B1I, STATE_SYMBOL_SYNC is optional since the symbol length is the + * same as the bit length. + * <li> For L5Q and E5aQ, STATE_BIT_SYNC and STATE_SYMBOL_SYNC are optional since they are + * implied by STATE_CODE_LOCK. + * <li> STATE_2ND_CODE_LOCK for L5I is optional since it is implied by STATE_SYMBOL_SYNC. + * <li> STATE_2ND_CODE_LOCK for E1C is optional since it is implied by + * STATE_GAL_E1C_2ND_CODE_LOCK. + * <li> For E1B and E1C, STATE_SYMBOL_SYNC is optional, because it is implied by + * STATE_GAL_E1BC_CODE_LOCK. * </ul> - * - * <p>Given the highest sync state that can be achieved, per each satellite, valid range for - * this field can be: - * <pre> - * Searching : [ 0 ] : STATE_UNKNOWN - * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set - * Symbol sync : [ 0 10ms ] : STATE_SYMBOL_SYNC is set - * Bit sync : [ 0 20ms ] : STATE_BIT_SYNC is set - * String sync : [ 0 2s ] : STATE_GLO_STRING_SYNC is set - * Time of day decoded : [ 0 1day ] : STATE_GLO_TOD_DECODED is set - * Time of day known : [ 0 1day ] : STATE_GLO_TOD_KNOWN set</pre> - * - * Note: Time of day known refers to the case where it is possibly not decoded over the air but - * has been determined from other sources. If Time of day decoded is set then Time of day known - * must also be set. - * - * <p>For Beidou, this is: - * <ul> - * <li>Received Beidou time of week, at the measurement time in nanoseconds.</li> - * </ul> - * - * <p>Given the highest sync state that can be achieved, per each satellite, valid range for - * this field can be: - * <pre> - * Searching : [ 0 ] : STATE_UNKNOWN - * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set - * Bit sync (D2) : [ 0 2ms ] : STATE_BDS_D2_BIT_SYNC is set - * Bit sync (D1) : [ 0 20ms ] : STATE_BIT_SYNC is set - * Subframe (D2) : [ 0 0.6s ] : STATE_BDS_D2_SUBFRAME_SYNC is set - * Subframe (D1) : [ 0 6s ] : STATE_SUBFRAME_SYNC is set - * Time of week decoded : [ 0 1week ] : STATE_TOW_DECODED is set - * Time of week known : [ 0 1week ] : STATE_TOW_KNOWN set</pre> - * - * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has - * been determined from other sources. If TOW decoded is set then TOW Known must also be set. - * - * <p>For Galileo, this is: - * <ul> - * <li>Received Galileo time of week, at the measurement time in nanoseconds.</li> - * </ul> - * <pre> - * E1BC code lock : [ 0 4ms ] : STATE_GAL_E1BC_CODE_LOCK is set - * E1C 2nd code lock : [ 0 100ms ] : STATE_GAL_E1C_2ND_CODE_LOCK is set - * E1B page : [ 0 2s ] : STATE_GAL_E1B_PAGE_SYNC is set - * Time of week decoded : [ 0 1week ] : STATE_TOW_DECODED is set - * Time of week known : [ 0 1week ] : STATE_TOW_KNOWN set</pre> - * - * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has - * been determined from other sources. If TOW decoded is set then TOW Known must also be set. - * - * <p>For SBAS, this is: - * <ul> - * <li>Received SBAS time, at the measurement time in nanoseconds.</li> - * </ul> - * - * <p>Given the highest sync state that can be achieved, per each satellite, valid range for - * this field can be: - * <pre> - * Searching : [ 0 ] : STATE_UNKNOWN - * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set - * Symbol sync : [ 0 2ms ] : STATE_SYMBOL_SYNC is set - * Message : [ 0 1s ] : STATE_SBAS_SYNC is set</pre> */ public long getReceivedSvTimeNanos() { return mReceivedSvTimeNanos; diff --git a/media/Android.bp b/media/Android.bp index 141d415cbaf3..86dc509501a4 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -3,7 +3,6 @@ java_library { srcs: [ ":updatable-media-srcs", - ":framework-media-annotation-srcs", ], aidl: { @@ -28,7 +27,12 @@ java_library { installable: true, // Make sure that the implementaion only relies on SDK or system APIs. - sdk_version: "system_current", + no_framework_libs: true, + libs: [ + // The order matters. android_system_* library should come later. + "framework_media_annotation", + "android_system_stubs_current", + ], } filegroup { @@ -125,3 +129,8 @@ java_library { sdk_version: "28", } +java_library { + name: "framework_media_annotation", + srcs: [":framework-media-annotation-srcs"], + installable: false, +} diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java index c38a83131a6c..89a954061958 100644 --- a/media/apex/java/android/media/MediaPlayer2.java +++ b/media/apex/java/android/media/MediaPlayer2.java @@ -45,8 +45,8 @@ import android.util.Size; import android.view.Surface; import android.view.SurfaceHolder; -import com.android.framework.protobuf.InvalidProtocolBufferException; import com.android.internal.annotations.GuardedBy; +import com.android.media.protobuf.InvalidProtocolBufferException; import java.io.ByteArrayOutputStream; import java.io.File; diff --git a/media/packages/MediaCore/Android.bp.bak b/media/packages/MediaCore/Android.bp.bak deleted file mode 100644 index c7fd58bf933a..000000000000 --- a/media/packages/MediaCore/Android.bp.bak +++ /dev/null @@ -1,21 +0,0 @@ -android_app { - name: "MediaCore", - - srcs: [ - "src/**/*.java", - ], - - static_libs: [ - // TODO: Temporarily statically linked. Should go into "libs" - "media1", - ], - - // System app - platform_apis: true, - - // Privileged app - privileged: true, - - // Make sure that the implementation only relies on SDK or system APIs. - sdk_version: "system_current", -} diff --git a/media/packages/MediaCore/AndroidManifest.xml b/media/packages/MediaCore/AndroidManifest.xml deleted file mode 100644 index 4e2b274511e8..000000000000 --- a/media/packages/MediaCore/AndroidManifest.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/AndroidManifest.xml -** -** Copyright 2019, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.media" coreApp="true" android:sharedUserId="android.uid.system" - android:sharedUserLabel="@string/android_system_label"> - <application android:process="system" - android:persistent="true" - android:directBootAware="true"> - <service android:name="AmlMediaSessionProviderService" android:singleUser="true"> - <intent-filter> - <action android:name="android.media.session.MediaSessionProviderService"/> - </intent-filter> - </service> - </application> -</manifest> diff --git a/media/packages/MediaCore/res/values/strings.xml b/media/packages/MediaCore/res/values/strings.xml deleted file mode 100644 index 59fd635b905f..000000000000 --- a/media/packages/MediaCore/res/values/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<resources> - <!-- Label for the Android system components when they are shown to the user. --> - <string name="android_system_label" translatable="false">Android System</string> -</resources> - diff --git a/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java b/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java deleted file mode 100644 index 43b95ab7ebdb..000000000000 --- a/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import android.content.Context; -import android.media.session.MediaSessionProviderService; -import android.os.PowerManager; -import android.util.Log; - -/** - * System implementation of MediaSessionProviderService - */ -public class AmlMediaSessionProviderService extends MediaSessionProviderService { - private static final String TAG = "AmlMediaSessionProviderS"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - private Context mContext; - - public AmlMediaSessionProviderService(Context context) { - mContext = context; - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - } -} diff --git a/media/proto/jarjar-rules.txt b/media/proto/jarjar-rules.txt index 7be6e732d3fd..bfb0b2782486 100644 --- a/media/proto/jarjar-rules.txt +++ b/media/proto/jarjar-rules.txt @@ -1,2 +1,2 @@ -rule com.google.protobuf.** com.android.framework.protobuf.@1 +rule com.google.protobuf.** com.android.media.protobuf.@1 diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 730c409a91fb..a3db2d6a5055 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -146,6 +146,7 @@ LIBANDROID { AHardwareBuffer_getNativeHandle; # introduced=26 AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; # introduced=26 + AHardwareBuffer_lockPlanes; # introduced=29 AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26 AHardwareBuffer_release; # introduced=26 AHardwareBuffer_sendHandleToUnixSocket; # introduced=26 diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 2dba1d5bc257..c5a951ca5249 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -221,30 +221,6 @@ public class CarStatusBar extends StatusBar implements } } - @Override - public void destroy() { - mCarBatteryController.stopListening(); - mConnectedDeviceSignalController.stopListening(); - mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackListener); - mDrivingStateHelper.disconnectFromCarService(); - - if (mNavigationBarWindow != null) { - mWindowManager.removeViewImmediate(mNavigationBarWindow); - mNavigationBarView = null; - } - - if (mLeftNavigationBarWindow != null) { - mWindowManager.removeViewImmediate(mLeftNavigationBarWindow); - mLeftNavigationBarView = null; - } - - if (mRightNavigationBarWindow != null) { - mWindowManager.removeViewImmediate(mRightNavigationBarWindow); - mRightNavigationBarView = null; - } - super.destroy(); - } - @Override protected void makeStatusBarView() { diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java index f372fe55dfb0..24fa87a7f39b 100644 --- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java +++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java @@ -15,11 +15,10 @@ */ package android.ext.services.notification; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.Notification; import android.app.Person; import android.app.RemoteAction; +import android.app.RemoteInput; import android.content.Context; import android.graphics.drawable.Icon; import android.os.Bundle; @@ -27,7 +26,9 @@ import android.os.Parcelable; import android.os.Process; import android.service.notification.NotificationAssistantService; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.LruCache; +import android.util.Pair; import android.view.textclassifier.ConversationAction; import android.view.textclassifier.ConversationActions; import android.view.textclassifier.TextClassificationContext; @@ -35,6 +36,8 @@ import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextClassifierEvent; +import com.android.internal.util.ArrayUtils; + import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -43,11 +46,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; public class SmartActionsHelper { private static final String KEY_ACTION_TYPE = "action_type"; + private static final String KEY_ACTION_SCORE = "action_score"; // If a notification has any of these flags set, it's inelgibile for actions being added. private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS = Notification.FLAG_ONGOING_EVENT @@ -58,75 +63,136 @@ public class SmartActionsHelper { private static final List<String> HINTS = Collections.singletonList(ConversationActions.Request.HINT_FOR_NOTIFICATION); + private static final ConversationActions EMPTY_CONVERSATION_ACTIONS = + new ConversationActions(Collections.emptyList(), null); private Context mContext; - @Nullable private TextClassifier mTextClassifier; - @NonNull private AssistantSettings mSettings; - private LruCache<String, String> mNotificationKeyToResultIdCache = - new LruCache<>(MAX_RESULT_ID_TO_CACHE); + private LruCache<String, Session> mSessionCache = new LruCache<>(MAX_RESULT_ID_TO_CACHE); SmartActionsHelper(Context context, AssistantSettings settings) { mContext = context; TextClassificationManager textClassificationManager = mContext.getSystemService(TextClassificationManager.class); - if (textClassificationManager != null) { - mTextClassifier = textClassificationManager.getTextClassifier(); - } + mTextClassifier = textClassificationManager.getTextClassifier(); mSettings = settings; } - SmartSuggestions suggest(@NonNull NotificationEntry entry) { + SmartSuggestions suggest(NotificationEntry entry) { // Whenever suggest() is called on a notification, its previous session is ended. - mNotificationKeyToResultIdCache.remove(entry.getSbn().getKey()); + mSessionCache.remove(entry.getSbn().getKey()); boolean eligibleForReplyAdjustment = mSettings.mGenerateReplies && isEligibleForReplyAdjustment(entry); boolean eligibleForActionAdjustment = mSettings.mGenerateActions && isEligibleForActionAdjustment(entry); - List<ConversationAction> conversationActions = + ConversationActions conversationActionsResult = suggestConversationActions( entry, eligibleForReplyAdjustment, eligibleForActionAdjustment); - ArrayList<CharSequence> replies = conversationActions.stream() - .map(ConversationAction::getTextReply) - .filter(textReply -> !TextUtils.isEmpty(textReply)) - .collect(Collectors.toCollection(ArrayList::new)); + String resultId = conversationActionsResult.getId(); + List<ConversationAction> conversationActions = + conversationActionsResult.getConversationActions(); + + ArrayList<CharSequence> replies = new ArrayList<>(); + Map<CharSequence, Float> repliesScore = new ArrayMap<>(); + for (ConversationAction conversationAction : conversationActions) { + CharSequence textReply = conversationAction.getTextReply(); + if (TextUtils.isEmpty(textReply)) { + continue; + } + replies.add(textReply); + repliesScore.put(textReply, conversationAction.getConfidenceScore()); + } ArrayList<Notification.Action> actions = conversationActions.stream() .filter(conversationAction -> conversationAction.getAction() != null) - .map(action -> createNotificationAction(action.getAction(), action.getType())) + .map(action -> createNotificationAction( + action.getAction(), action.getType(), action.getConfidenceScore())) .collect(Collectors.toCollection(ArrayList::new)); + + // Start a new session for logging if necessary. + if (!TextUtils.isEmpty(resultId) + && !conversationActions.isEmpty() + && suggestionsMightBeUsedInNotification( + entry, !actions.isEmpty(), !replies.isEmpty())) { + mSessionCache.put(entry.getSbn().getKey(), new Session(resultId, repliesScore)); + } + return new SmartSuggestions(replies, actions); } /** + * Returns whether the suggestion might be used in the notifications in SysUI. + * <p> + * Currently, NAS has no idea if suggestions will actually be used in the notification, and thus + * this function tries to make a heuristic. This function tries to optimize the precision, + * that means when it is unsure, it will return false. The objective is to avoid false positive, + * which could pollute the log and CTR as we are logging click rate of suggestions that could + * be never visible to users. On the other hand, it is fine to have false negative because + * it would be just like sampling. + */ + private boolean suggestionsMightBeUsedInNotification( + NotificationEntry notificationEntry, boolean hasSmartAction, boolean hasSmartReply) { + Notification notification = notificationEntry.getNotification(); + boolean hasAppGeneratedContextualActions = !notification.getContextualActions().isEmpty(); + + Pair<RemoteInput, Notification.Action> freeformRemoteInputAndAction = + notification.findRemoteInputActionPair(/* requiresFreeform */ true); + boolean hasAppGeneratedReplies = false; + boolean allowGeneratedReplies = false; + if (freeformRemoteInputAndAction != null) { + RemoteInput freeformRemoteInput = freeformRemoteInputAndAction.first; + Notification.Action actionWithFreeformRemoteInput = freeformRemoteInputAndAction.second; + hasAppGeneratedReplies = !ArrayUtils.isEmpty(freeformRemoteInput.getChoices()); + allowGeneratedReplies = actionWithFreeformRemoteInput.getAllowGeneratedReplies(); + } + + if (hasAppGeneratedReplies || hasAppGeneratedContextualActions) { + return false; + } + return hasSmartAction && notification.getAllowSystemGeneratedContextualActions() + || hasSmartReply && allowGeneratedReplies; + } + + private void reportActionsGenerated( + String resultId, List<ConversationAction> conversationActions) { + if (TextUtils.isEmpty(resultId)) { + return; + } + TextClassifierEvent textClassifierEvent = + createTextClassifierEventBuilder( + TextClassifierEvent.TYPE_ACTIONS_GENERATED, resultId) + .setEntityTypes(conversationActions.stream() + .map(ConversationAction::getType) + .toArray(String[]::new)) + .build(); + mTextClassifier.onTextClassifierEvent(textClassifierEvent); + } + + /** * Adds action adjustments based on the notification contents. */ - @NonNull - private List<ConversationAction> suggestConversationActions( - @NonNull NotificationEntry entry, + private ConversationActions suggestConversationActions( + NotificationEntry entry, boolean includeReplies, boolean includeActions) { if (!includeReplies && !includeActions) { - return Collections.emptyList(); - } - if (mTextClassifier == null) { - return Collections.emptyList(); + return EMPTY_CONVERSATION_ACTIONS; } List<ConversationActions.Message> messages = extractMessages(entry.getNotification()); if (messages.isEmpty()) { - return Collections.emptyList(); + return EMPTY_CONVERSATION_ACTIONS; } // Do not generate smart actions if the last message is from the local user. ConversationActions.Message lastMessage = messages.get(messages.size() - 1); if (arePersonsEqual( ConversationActions.Message.PERSON_USER_SELF, lastMessage.getAuthor())) { - return Collections.emptyList(); + return EMPTY_CONVERSATION_ACTIONS; } TextClassifier.EntityConfig.Builder typeConfigBuilder = @@ -146,25 +212,20 @@ public class SmartActionsHelper { .setHints(HINTS) .setTypeConfig(typeConfigBuilder.build()) .build(); - - ConversationActions conversationActionsResult = + ConversationActions conversationActions = mTextClassifier.suggestConversationActions(request); - - String resultId = conversationActionsResult.getId(); - if (!TextUtils.isEmpty(resultId) - && !conversationActionsResult.getConversationActions().isEmpty()) { - mNotificationKeyToResultIdCache.put(entry.getSbn().getKey(), resultId); - } - return conversationActionsResult.getConversationActions(); + reportActionsGenerated( + conversationActions.getId(), conversationActions.getConversationActions()); + return conversationActions; } - void onNotificationExpansionChanged(@NonNull NotificationEntry entry, boolean isUserAction, + void onNotificationExpansionChanged(NotificationEntry entry, boolean isUserAction, boolean isExpanded) { if (!isExpanded) { return; } - String resultId = mNotificationKeyToResultIdCache.get(entry.getSbn().getKey()); - if (resultId == null) { + Session session = mSessionCache.get(entry.getSbn().getKey()); + if (session == null) { return; } // Only report if this is the first time the user sees these suggestions. @@ -173,56 +234,50 @@ public class SmartActionsHelper { } entry.setShowActionEventLogged(); TextClassifierEvent textClassifierEvent = - createTextClassifierEventBuilder(TextClassifierEvent.TYPE_ACTIONS_SHOWN, - resultId) + createTextClassifierEventBuilder( + TextClassifierEvent.TYPE_ACTIONS_SHOWN, session.resultId) .build(); // TODO: If possible, report which replies / actions are actually seen by user. mTextClassifier.onTextClassifierEvent(textClassifierEvent); } - void onNotificationDirectReplied(@NonNull String key) { - if (mTextClassifier == null) { - return; - } - String resultId = mNotificationKeyToResultIdCache.get(key); - if (resultId == null) { + void onNotificationDirectReplied(String key) { + Session session = mSessionCache.get(key); + if (session == null) { return; } TextClassifierEvent textClassifierEvent = - createTextClassifierEventBuilder(TextClassifierEvent.TYPE_MANUAL_REPLY, resultId) + createTextClassifierEventBuilder( + TextClassifierEvent.TYPE_MANUAL_REPLY, session.resultId) .build(); mTextClassifier.onTextClassifierEvent(textClassifierEvent); } - void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply, + void onSuggestedReplySent(String key, CharSequence reply, @NotificationAssistantService.Source int source) { - if (mTextClassifier == null) { - return; - } if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) { return; } - String resultId = mNotificationKeyToResultIdCache.get(key); - if (resultId == null) { + Session session = mSessionCache.get(key); + if (session == null) { return; } TextClassifierEvent textClassifierEvent = - createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId) + createTextClassifierEventBuilder( + TextClassifierEvent.TYPE_SMART_ACTION, session.resultId) .setEntityTypes(ConversationAction.TYPE_TEXT_REPLY) + .setScore(session.repliesScores.getOrDefault(reply, 0f)) .build(); mTextClassifier.onTextClassifierEvent(textClassifierEvent); } - void onActionClicked(@NonNull String key, @NonNull Notification.Action action, + void onActionClicked(String key, Notification.Action action, @NotificationAssistantService.Source int source) { - if (mTextClassifier == null) { - return; - } if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) { return; } - String resultId = mNotificationKeyToResultIdCache.get(key); - if (resultId == null) { + Session session = mSessionCache.get(key); + if (session == null) { return; } String actionType = action.getExtras().getString(KEY_ACTION_TYPE); @@ -230,28 +285,32 @@ public class SmartActionsHelper { return; } TextClassifierEvent textClassifierEvent = - createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId) + createTextClassifierEventBuilder( + TextClassifierEvent.TYPE_SMART_ACTION, session.resultId) .setEntityTypes(actionType) .build(); mTextClassifier.onTextClassifierEvent(textClassifierEvent); } private Notification.Action createNotificationAction( - RemoteAction remoteAction, String actionType) { + RemoteAction remoteAction, String actionType, float score) { Icon icon = remoteAction.shouldShowIcon() ? remoteAction.getIcon() : Icon.createWithResource(mContext, com.android.internal.R.drawable.ic_action_open); + Bundle extras = new Bundle(); + extras.putString(KEY_ACTION_TYPE, actionType); + extras.putFloat(KEY_ACTION_SCORE, score); return new Notification.Action.Builder( icon, remoteAction.getTitle(), remoteAction.getActionIntent()) .setContextual(true) - .addExtras(Bundle.forPair(KEY_ACTION_TYPE, actionType)) + .addExtras(extras) .build(); } private TextClassifierEvent.Builder createTextClassifierEventBuilder( - int eventType, @NonNull String resultId) { + int eventType, String resultId) { return new TextClassifierEvent.Builder( TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType) .setEventTime(System.currentTimeMillis()) @@ -269,7 +328,7 @@ public class SmartActionsHelper { * to fundamental phone functionality where any error would result in a very negative user * experience. */ - private boolean isEligibleForActionAdjustment(@NonNull NotificationEntry entry) { + private boolean isEligibleForActionAdjustment(NotificationEntry entry) { Notification notification = entry.getNotification(); String pkg = entry.getSbn().getPackageName(); if (!Process.myUserHandle().equals(entry.getSbn().getUser())) { @@ -285,7 +344,7 @@ public class SmartActionsHelper { return entry.isMessaging(); } - private boolean isEligibleForReplyAdjustment(@NonNull NotificationEntry entry) { + private boolean isEligibleForReplyAdjustment(NotificationEntry entry) { if (!Process.myUserHandle().equals(entry.getSbn().getUser())) { return false; } @@ -306,8 +365,7 @@ public class SmartActionsHelper { } /** Returns the text most salient for action extraction in a notification. */ - @Nullable - private List<ConversationActions.Message> extractMessages(@NonNull Notification notification) { + private List<ConversationActions.Message> extractMessages(Notification notification) { Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES); if (messages == null || messages.length == 0) { return Collections.singletonList(new ConversationActions.Message.Builder( @@ -343,7 +401,7 @@ public class SmartActionsHelper { return new ArrayList<>(extractMessages); } - private static boolean arePersonsEqual(@NonNull Person left, @NonNull Person right) { + private static boolean arePersonsEqual(Person left, Person right) { return Objects.equals(left.getKey(), right.getKey()) && Objects.equals(left.getName(), right.getName()) && Objects.equals(left.getUri(), right.getUri()); @@ -359,4 +417,14 @@ public class SmartActionsHelper { this.actions = actions; } } + + private static class Session { + public final String resultId; + public final Map<CharSequence, Float> repliesScores; + + Session(String resultId, Map<CharSequence, Float> repliesScores) { + this.resultId = resultId; + this.repliesScores = repliesScores; + } + } } diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java index 74c20fc09df2..d0b6d0061166 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java @@ -19,7 +19,9 @@ import static com.google.common.truth.Truth.assertAbout; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -53,8 +55,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.time.Instant; @@ -71,9 +73,12 @@ import javax.annotation.Nullable; public class SmartActionsHelperTest { private static final String NOTIFICATION_KEY = "key"; private static final String RESULT_ID = "id"; + private static final float SCORE = 0.7f; + private static final CharSequence SMART_REPLY = "Home"; private static final ConversationAction REPLY_ACTION = new ConversationAction.Builder(ConversationAction.TYPE_TEXT_REPLY) - .setTextReply("Home") + .setTextReply(SMART_REPLY) + .setConfidenceScore(SCORE) .build(); private static final String MESSAGE = "Where are you?"; @@ -197,8 +202,16 @@ public class SmartActionsHelperTest { List<ConversationActions.Message> messages = runSuggestAndCaptureRequest().getConversation(); + assertThat(messages).hasSize(1); MessageSubject.assertThat(messages.get(0)).hasText(MESSAGE); + ArgumentCaptor<TextClassifierEvent> argumentCaptor = + ArgumentCaptor.forClass(TextClassifierEvent.class); + verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); + TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); + assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertThat(textClassifierEvent.getEntityTypes()).asList() + .containsExactly(ConversationAction.TYPE_TEXT_REPLY); } @Test @@ -249,6 +262,14 @@ public class SmartActionsHelperTest { MessageSubject.assertThat(fourthMessage).hasPerson(userB); MessageSubject.assertThat(fourthMessage) .hasReferenceTime(createZonedDateTimeFromMsUtc(4000)); + + ArgumentCaptor<TextClassifierEvent> argumentCaptor = + ArgumentCaptor.forClass(TextClassifierEvent.class); + verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); + TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); + assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertThat(textClassifierEvent.getEntityTypes()).asList() + .containsExactly(ConversationAction.TYPE_TEXT_REPLY); } @Test @@ -299,13 +320,15 @@ public class SmartActionsHelperTest { mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onSuggestedReplySent( - NOTIFICATION_KEY, MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT); ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); - verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); - TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); - assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_SMART_ACTION); + verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture()); + List<TextClassifierEvent> events = argumentCaptor.getAllValues(); + assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_SMART_ACTION); + assertThat(events.get(1).getScore()).isEqualTo(SCORE); } @Test @@ -317,24 +340,22 @@ public class SmartActionsHelperTest { mSmartActionsHelper.onSuggestedReplySent( "something_else", MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT); - verify(mTextClassifier, never()) - .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class)); + verify(mTextClassifier, never()).onTextClassifierEvent( + argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_SMART_ACTION))); } @Test public void testOnSuggestedReplySent_missingResultId() { when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class))) - .thenReturn(new ConversationActions(Collections.emptyList(), null)); - + .thenReturn(new ConversationActions(Collections.singletonList(REPLY_ACTION), null)); Notification notification = createMessageNotification(); when(mStatusBarNotification.getNotification()).thenReturn(notification); mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onSuggestedReplySent( - "something_else", MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT); - verify(mTextClassifier, never()) - .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class)); + verify(mTextClassifier, never()).onTextClassifierEvent(any(TextClassifierEvent.class)); } @Test @@ -347,9 +368,10 @@ public class SmartActionsHelperTest { ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); - verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); - TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); - assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_MANUAL_REPLY); + verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture()); + List<TextClassifierEvent> events = argumentCaptor.getAllValues(); + assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_MANUAL_REPLY); } @Test @@ -362,9 +384,10 @@ public class SmartActionsHelperTest { ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); - verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); - TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); - assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN); + verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture()); + List<TextClassifierEvent> events = argumentCaptor.getAllValues(); + assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN); } @Test @@ -376,7 +399,7 @@ public class SmartActionsHelperTest { mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false, false); verify(mTextClassifier, never()).onTextClassifierEvent( - Mockito.any(TextClassifierEvent.class)); + argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_ACTIONS_SHOWN))); } @Test @@ -389,9 +412,10 @@ public class SmartActionsHelperTest { ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); - verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); - TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); - assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN); + verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture()); + List<TextClassifierEvent> events = argumentCaptor.getAllValues(); + assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN); } private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) { @@ -490,4 +514,21 @@ public class SmartActionsHelperTest { return assertAbout(FACTORY).that(message); } } + + private final class TextClassifierEventMatcher implements ArgumentMatcher<TextClassifierEvent> { + + private int mType; + + private TextClassifierEventMatcher(int type) { + mType = type; + } + + @Override + public boolean matches(TextClassifierEvent textClassifierEvent) { + if (textClassifierEvent == null) { + return false; + } + return mType == textClassifierEvent.getEventType(); + } + } } diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml index ac55bfa1aed7..e4d35915c77c 100644 --- a/packages/NetworkStack/AndroidManifest.xml +++ b/packages/NetworkStack/AndroidManifest.xml @@ -27,8 +27,8 @@ <uses-permission android:name="android.permission.NETWORK_SETTINGS" /> <!-- Signature permission defined in NetworkStackStub --> <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" /> - <!-- Launch captive portal app as specific user --> - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <!-- Send latency broadcast as current user --> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.NETWORK_STACK" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <application diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 0d6d080b6dc2..2e72d8296a37 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -500,7 +500,7 @@ public class NetworkMonitor extends StateMachine { private void showProvisioningNotification(String action) { try { - mCallback.showProvisioningNotification(action); + mCallback.showProvisioningNotification(action, mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error showing provisioning notification", e); } diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java index d11bb64213c3..b98b0f798fe3 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -482,7 +482,7 @@ public class NetworkMonitorTest { nm.notifyNetworkConnected(); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .showProvisioningNotification(any()); + .showProvisioningNotification(any(), any()); // Check that startCaptivePortalApp sends the expected intent. nm.launchCaptivePortalApp(); diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk index 8c309ff97370..8d24eaba5cc4 100644 --- a/packages/SettingsLib/common.mk +++ b/packages/SettingsLib/common.mk @@ -31,3 +31,4 @@ LOCAL_STATIC_ANDROID_LIBRARIES += \ androidx.legacy_legacy-preference-v14 \ SettingsLib +LOCAL_RESOURCE_DIR += $(call my-dir)/res diff --git a/packages/SettingsLib/res/drawable/ic_system_update.xml b/packages/SettingsLib/res/drawable/ic_system_update.xml new file mode 100644 index 000000000000..3325fdd5c01a --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_system_update.xml @@ -0,0 +1,25 @@ +<!-- + 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M17,1.01L7,1C5.9,1 5,1.9 5,3v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3C19,1.9 18.1,1.01 17,1.01zM17,21H7l0,-1h10V21zM17,18H7V6h10V18zM7,4V3h10v1H7zM16,12.5l-4,4l-4,-4l1.41,-1.41L11,12.67V8.5V8h2v0.5v4.17l1.59,-1.59L16,12.5z"/> +</vector> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index d6448336e402..f5d58d8a2004 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -337,6 +337,9 @@ <string name="data_usage_uninstalled_apps">Removed apps</string> <!-- Title of data usage item that represents all uninstalled applications or removed users. [CHAR LIMIT=48] --> <string name="data_usage_uninstalled_apps_users">Removed apps and users</string> + <!-- Title of data usage item that represents system updates (OTAs). [CHAR LIMIT=48] --> + <string name="data_usage_ota">System updates</string> + <!-- Tethering controls, item title to go into the tethering settings --> <!-- Tethering controls, item title to go into the tethering settings when only USB tethering is available [CHAR LIMIT=25]--> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index e28c894ff8f3..ab95910a77b5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -167,7 +167,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // This is to ensure all the profiles are disconnected as some CK/Hs do not // disconnect PBAP connection when HF connection is brought down PbapServerProfile PbapProfile = mProfileManager.getPbapProfile(); - if (PbapProfile.getConnectionStatus(mDevice) == BluetoothProfile.STATE_CONNECTED) + if (PbapProfile != null && isConnectedProfile(PbapProfile)) { PbapProfile.disconnect(mDevice); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index a1bf93654774..c1933fd87633 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -73,9 +73,8 @@ public class HeadsetProfile implements LocalBluetoothProfile { BluetoothProfile.STATE_CONNECTED); device.refresh(); } - - mProfileManager.callServiceConnectedListeners(); mIsProfileReady=true; + mProfileManager.callServiceConnectedListeners(); } public void onServiceDisconnected(int profile) { diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java index 305a1ff97e71..dd6d563b3197 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java @@ -40,6 +40,7 @@ import androidx.loader.content.AsyncTaskLoader; import com.android.settingslib.NetworkPolicyEditor; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Iterator; /** @@ -52,6 +53,7 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { protected final int mNetworkType; private final NetworkPolicy mPolicy; private final NetworkTemplate mNetworkTemplate; + private final ArrayList<Long> mCycles; @VisibleForTesting final INetworkStatsService mNetworkStatsService; @@ -60,6 +62,7 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { mSubId = builder.mSubId; mNetworkType = builder.mNetworkType; mNetworkTemplate = builder.mNetworkTemplate; + mCycles = builder.mCycles; mNetworkStatsManager = (NetworkStatsManager) builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE); mNetworkStatsService = INetworkStatsService.Stub.asInterface( @@ -77,7 +80,9 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { } public D loadInBackground() { - if (mPolicy == null) { + if (mCycles != null && mCycles.size() > 1) { + loadDataForSpecificCycles(); + } else if (mPolicy == null) { loadFourWeeksData(); } else { loadPolicyData(); @@ -132,6 +137,17 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { } @VisibleForTesting + void loadDataForSpecificCycles() { + long cycleEnd = mCycles.get(0); + final int lastCycleIndex = mCycles.size() - 1; + for (int i = 1; i <= lastCycleIndex; i++) { + final long cycleStart = mCycles.get(i); + recordUsage(cycleStart, cycleEnd); + cycleEnd = cycleStart; + } + } + + @VisibleForTesting abstract void recordUsage(long start, long end); abstract D getCycleUsage(); @@ -157,11 +173,17 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { return bytes; } + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + public ArrayList<Long> getCycles() { + return mCycles; + } + public static abstract class Builder<T extends NetworkCycleDataLoader> { private final Context mContext; private String mSubId; private int mNetworkType; private NetworkTemplate mNetworkTemplate; + private ArrayList<Long> mCycles; public Builder (Context context) { mContext = context; @@ -178,6 +200,16 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { return this; } + /** + * Sets the network cycles to be used to query the usage data. + * @param cycles the time slots for the network cycle to be used to query the network usage. + * @return the builder + */ + public Builder<T> setCycles(ArrayList<Long> cycles) { + mCycles = cycles; + return this; + } + public abstract T build(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java index c14f5588cddf..e3516158daac 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java @@ -28,6 +28,7 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.TrafficStats; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -111,7 +112,7 @@ public class UidDetailProvider { // handle special case labels switch (uid) { - case android.os.Process.SYSTEM_UID: + case Process.SYSTEM_UID: detail.label = res.getString(R.string.process_kernel_label); detail.icon = pm.getDefaultActivityIcon(); return detail; @@ -127,6 +128,10 @@ public class UidDetailProvider { detail.label = res.getString(Utils.getTetheringLabel(cm)); detail.icon = pm.getDefaultActivityIcon(); return detail; + case Process.OTA_UPDATE_UID: + detail.label = res.getString(R.string.data_usage_ota); + detail.icon = mContext.getDrawable(R.drawable.ic_system_update); + return detail; } final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java index 88adcdb16edc..5c9a06f91e6a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java @@ -20,6 +20,7 @@ import android.os.Looper; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; public class ThreadUtils { @@ -59,12 +60,14 @@ public class ThreadUtils { /** * Posts runnable in background using shared background thread pool. + * + * @Return A future of the task that can be monitored for updates or cancelled. */ - public static void postOnBackgroundThread(Runnable runnable) { + public static Future postOnBackgroundThread(Runnable runnable) { if (sSingleThreadExecutor == null) { sSingleThreadExecutor = Executors.newSingleThreadExecutor(); } - sSingleThreadExecutor.execute(runnable); + return sSingleThreadExecutor.submit(runnable); } /** diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index b9a5f2347f16..8ea0f4b6ba61 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -53,6 +53,7 @@ import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import com.android.internal.util.CollectionUtils; import com.android.settingslib.R; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -566,9 +567,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro AccessPoint accessPoint = getCachedOrCreate(entry.getValue(), cachedAccessPoints); - if (mLastInfo != null && mLastNetworkInfo != null) { - accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); - } // Update the matching config if there is one, to populate saved network info accessPoint.update(configsByKey.get(entry.getKey())); @@ -578,68 +576,20 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values()); - // Add a unique Passpoint R1 AccessPoint for each Passpoint profile's FQDN. - List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans = - mWifiManager.getAllMatchingWifiConfigs(cachedScanResults); - Set<String> seenFQDNs = new ArraySet<>(); - for (Pair<WifiConfiguration, - Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) { - WifiConfiguration config = pairing.first; - - List<ScanResult> scanResults = new ArrayList<>(); + // Add a unique Passpoint AccessPoint for each Passpoint profile's FQDN. + accessPoints.addAll(updatePasspointAccessPoints( + mWifiManager.getAllMatchingWifiConfigs(cachedScanResults), cachedAccessPoints)); - List<ScanResult> homeScans = - pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK); - List<ScanResult> roamingScans = - pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK); + // Add OSU Provider AccessPoints + accessPoints.addAll(updateOsuAccessPoints( + mWifiManager.getMatchingOsuProviders(cachedScanResults), cachedAccessPoints)); - if (homeScans == null) { - homeScans = new ArrayList<>(); - } - if (roamingScans == null) { - roamingScans = new ArrayList<>(); - } - - // TODO(b/118705403): Differentiate home network vs roaming network for summary info - if (!homeScans.isEmpty()) { - scanResults.addAll(homeScans); - } else { - scanResults.addAll(roamingScans); - } - - if (seenFQDNs.add(config.FQDN)) { - int bestRssi = Integer.MIN_VALUE; - for (ScanResult result : scanResults) { - if (result.level >= bestRssi) { - bestRssi = result.level; - config.SSID = AccessPoint.convertToQuotedString(result.SSID); - } - } - - AccessPoint accessPoint = - getCachedOrCreatePasspoint(scanResults, cachedAccessPoints, config); - accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); - accessPoints.add(accessPoint); - } - } - - // Add Passpoint OSU Provider AccessPoints - Map<OsuProvider, List<ScanResult>> providersAndScans = - mWifiManager.getMatchingOsuProviders(cachedScanResults); - Set<OsuProvider> alreadyProvisioned = mWifiManager - .getMatchingPasspointConfigsForOsuProviders( - providersAndScans.keySet()).keySet(); - for (OsuProvider provider : providersAndScans.keySet()) { - if (!alreadyProvisioned.contains(provider)) { - AccessPoint accessPointOsu = - getCachedOrCreateOsu(providersAndScans.get(provider), - cachedAccessPoints, provider); - accessPointOsu.update(connectionConfig, mLastInfo, mLastNetworkInfo); - accessPoints.add(accessPointOsu); + if (mLastInfo != null && mLastNetworkInfo != null) { + for (AccessPoint ap : accessPoints) { + ap.update(connectionConfig, mLastInfo, mLastNetworkInfo); } } - // If there were no scan results, create an AP for the currently connected network (if // it exists). if (accessPoints.isEmpty() && connectionConfig != null) { @@ -686,7 +636,67 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } @VisibleForTesting - AccessPoint getCachedOrCreate( + List<AccessPoint> updatePasspointAccessPoints( + List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans, + List<AccessPoint> accessPointCache) { + List<AccessPoint> accessPoints = new ArrayList<>(); + + Set<String> seenFQDNs = new ArraySet<>(); + for (Pair<WifiConfiguration, + Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) { + WifiConfiguration config = pairing.first; + if (seenFQDNs.add(config.FQDN)) { + List<ScanResult> apScanResults = new ArrayList<>(); + + List<ScanResult> homeScans = + pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK); + List<ScanResult> roamingScans = + pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK); + + // TODO(b/118705403): Differentiate home network vs roaming network for summary info + if (!CollectionUtils.isEmpty(homeScans)) { + apScanResults.addAll(homeScans); + } else if (!CollectionUtils.isEmpty(roamingScans)) { + apScanResults.addAll(roamingScans); + } + + int bestRssi = Integer.MIN_VALUE; + for (ScanResult result : apScanResults) { + if (result.level >= bestRssi) { + bestRssi = result.level; + config.SSID = AccessPoint.convertToQuotedString(result.SSID); + } + } + + AccessPoint accessPoint = + getCachedOrCreatePasspoint(apScanResults, accessPointCache, config); + accessPoints.add(accessPoint); + } + } + return accessPoints; + } + + @VisibleForTesting + List<AccessPoint> updateOsuAccessPoints( + Map<OsuProvider, List<ScanResult>> providersAndScans, + List<AccessPoint> accessPointCache) { + List<AccessPoint> accessPoints = new ArrayList<>(); + + Set<OsuProvider> alreadyProvisioned = mWifiManager + .getMatchingPasspointConfigsForOsuProviders( + providersAndScans.keySet()).keySet(); + for (OsuProvider provider : providersAndScans.keySet()) { + if (!alreadyProvisioned.contains(provider)) { + AccessPoint accessPointOsu = + getCachedOrCreateOsu(providersAndScans.get(provider), + accessPointCache, provider); + accessPoints.add(accessPointOsu); + } + } + return accessPoints; + } + + private AccessPoint getCachedOrCreate( List<ScanResult> scanResults, List<AccessPoint> cache) { AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(scanResults.get(0))); diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index 42eb0b940938..7d227883b1fe 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,11 +49,15 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.WifiSsid; +import android.net.wifi.hotspot2.OsuProvider; +import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; import android.provider.Settings; +import android.util.ArraySet; +import android.util.Pair; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -74,7 +79,10 @@ import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -94,6 +102,8 @@ public class WifiTrackerTest { private static final int RSSI_1 = -30; private static final byte SCORE_1 = 10; private static final int BADGE_1 = AccessPoint.Speed.MODERATE; + private static final String FQDN_1 = "fqdn1"; + private static final String PROVIDER_FRIENDLY_NAME_1 = "providerFriendlyName1"; private static final String SSID_2 = "ssid2"; private static final String BSSID_2 = "AA:AA:AA:AA:AA:AA"; @@ -102,6 +112,8 @@ public class WifiTrackerTest { private static final int RSSI_2 = -30; private static final byte SCORE_2 = 15; private static final int BADGE_2 = AccessPoint.Speed.FAST; + private static final String FQDN_2 = "fqdn2"; + private static final String PROVIDER_FRIENDLY_NAME_2 = "providerFriendlyName2"; private static final String SSID_3 = "ssid3"; private static final String BSSID_3 = "CC:00:00:00:00:00"; @@ -271,6 +283,61 @@ public class WifiTrackerTest { 0 /* microsecond timestamp */); } + private static WifiConfiguration buildPasspointConfiguration(String fqdn, String friendlyName) { + WifiConfiguration config = spy(new WifiConfiguration()); + config.FQDN = fqdn; + config.providerFriendlyName = friendlyName; + when(config.isPasspoint()).thenReturn(true); + return config; + } + + private List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> + createPasspointMatchingWifiConfigsWithDuplicates() { + List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> matchingList = + new ArrayList<>(); + Map<Integer, List<ScanResult>> mapping = new HashMap<>(); + + mapping.put(WifiManager.PASSPOINT_HOME_NETWORK, Arrays.asList(buildScanResult1())); + + WifiConfiguration passpointConfig1 = + buildPasspointConfiguration(FQDN_1, PROVIDER_FRIENDLY_NAME_1); + WifiConfiguration passpointConfig2 = + buildPasspointConfiguration(FQDN_2, PROVIDER_FRIENDLY_NAME_2); + + matchingList.add(new Pair(passpointConfig1, mapping)); + matchingList.add(new Pair(passpointConfig1, mapping)); + matchingList.add(new Pair(passpointConfig2, mapping)); + matchingList.add(new Pair(passpointConfig2, mapping)); + + return matchingList; + } + + private List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> + createPasspointMatchingWifiConfigWithScanResults( + List<ScanResult> homeList, List<ScanResult> roamingList) { + List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> matchingList = + new ArrayList<>(); + Map<Integer, List<ScanResult>> mapping = new HashMap<>(); + + if (homeList != null) { + mapping.put(WifiManager.PASSPOINT_HOME_NETWORK, homeList); + } + if (roamingList != null) { + mapping.put(WifiManager.PASSPOINT_ROAMING_NETWORK, roamingList); + } + + matchingList.add(new Pair(buildPasspointConfiguration(FQDN_1, PROVIDER_FRIENDLY_NAME_1), + mapping)); + + return matchingList; + } + + private static OsuProvider buildOsuProvider(String friendlyName) { + Map<String, String> friendlyNames = new HashMap<>(); + friendlyNames.put("en", friendlyName); + return new OsuProvider(null, friendlyNames, null, null, null, null, null); + } + private WifiTracker createTrackerWithImmediateBroadcastsAndInjectInitialScanResults( Intent ... intents) throws InterruptedException { @@ -926,4 +993,172 @@ public class WifiTrackerTest { assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1); assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2); } + + /** + * Verifies that updatePasspointAccessPoints will only return AccessPoints whose + * isPasspoint() evaluates as true. + */ + @Test + public void updatePasspointAccessPoints_returnedAccessPointsArePasspoint() { + WifiTracker tracker = createMockedWifiTracker(); + + List<AccessPoint> passpointAccessPoints = tracker.updatePasspointAccessPoints( + createPasspointMatchingWifiConfigsWithDuplicates(), new ArrayList<>()); + + assertTrue(passpointAccessPoints.size() != 0); + for (AccessPoint ap : passpointAccessPoints) { + assertTrue(ap.isPasspoint()); + } + } + + /** + * Verifies that updatePasspointAccessPoints will return the same amount of AccessPoints as + * unique WifiConfigurations, even if duplicate FQDNs exist. + */ + @Test + public void updatePasspointAccessPoints_ignoresDuplicateFQDNs() { + WifiTracker tracker = createMockedWifiTracker(); + + // Process matching list of four configs with two duplicate FQDNs. + List<AccessPoint> passpointAccessPoints = tracker.updatePasspointAccessPoints( + createPasspointMatchingWifiConfigsWithDuplicates(), new ArrayList<>()); + + // Should have 2 APs with unique FQDNs, ignoring the 2 duplicate FQDNs. + assertThat(passpointAccessPoints).hasSize(2); + + Set<String> fqdns = new ArraySet<>(Arrays.asList(FQDN_1, FQDN_2)); + + assertTrue(fqdns.remove(passpointAccessPoints.get(0).getConfig().FQDN)); + assertTrue(fqdns.remove(passpointAccessPoints.get(1).getConfig().FQDN)); + } + + /** + * Verifies that updatePasspointAccessPoints will return matching cached APs and update their + * scan results instead of creating new APs. + */ + @Test + public void updatePasspointAccessPoints_usesCachedAccessPoints() { + WifiTracker tracker = createMockedWifiTracker(); + + ScanResult result = buildScanResult1(); + + List<AccessPoint> passpointAccessPointsFirstUpdate = tracker.updatePasspointAccessPoints( + createPasspointMatchingWifiConfigWithScanResults(Arrays.asList(result), + null), new ArrayList<>()); + List<AccessPoint> cachedAccessPoints = new ArrayList<>(passpointAccessPointsFirstUpdate); + + int prevRssi = result.level; + int newRssi = prevRssi + 10; + result.level = newRssi; + + List<AccessPoint> passpointAccessPointsSecondUpdate = tracker.updatePasspointAccessPoints( + createPasspointMatchingWifiConfigWithScanResults(Arrays.asList(result), + null), cachedAccessPoints); + + // Verify second update AP is the same object as the first update AP + assertThat(passpointAccessPointsFirstUpdate.get(0)) + .isSameAs(passpointAccessPointsSecondUpdate.get(0)); + // Verify second update AP has the average of the first and second update RSSIs + assertThat(passpointAccessPointsSecondUpdate.get(0).getRssi()) + .isEqualTo((prevRssi + newRssi) / 2); + } + + /** + * Verifies that updateOsuAccessPoints will only return AccessPoints whose + * isOsuProvider() evaluates as true. + */ + @Test + public void updateOsuAccessPoints_returnedAccessPointsAreOsuProviders() { + WifiTracker tracker = createMockedWifiTracker(); + + Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>(); + providersAndScans.put( + buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(buildScanResult1())); + providersAndScans.put( + buildOsuProvider(PROVIDER_FRIENDLY_NAME_2), Arrays.asList(buildScanResult2())); + + List<AccessPoint> osuAccessPoints = tracker.updateOsuAccessPoints( + providersAndScans, new ArrayList<>()); + + assertThat(osuAccessPoints).hasSize(2); + for (AccessPoint ap: osuAccessPoints) { + assertThat(ap.isOsuProvider()).isTrue(); + } + } + + /** + * Verifies that updateOsuAccessPoints will not return Osu AccessPoints for already provisioned + * networks + */ + @Test + public void updateOsuAccessPoints_doesNotReturnAlreadyProvisionedOsuAccessPoints() { + WifiTracker tracker = createMockedWifiTracker(); + + // Start with two Osu Providers + Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>(); + providersAndScans.put( + buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(buildScanResult1())); + providersAndScans.put( + buildOsuProvider(PROVIDER_FRIENDLY_NAME_2), Arrays.asList(buildScanResult2())); + + // First update + List<AccessPoint> osuAccessPoints = tracker.updateOsuAccessPoints( + providersAndScans, new ArrayList<>()); + + // Make sure both Osu Providers' APs are returned + assertThat(osuAccessPoints).hasSize(2); + List<String> friendlyNames = Arrays.asList( + osuAccessPoints.get(0).getTitle(), osuAccessPoints.get(1).getTitle()); + assertThat(friendlyNames) + .containsExactly(PROVIDER_FRIENDLY_NAME_1, PROVIDER_FRIENDLY_NAME_2); + + // Simulate Osu Provider 1 being provisioned + Map<OsuProvider, PasspointConfiguration> matchingPasspointConfigForOsuProvider = + new HashMap<>(); + matchingPasspointConfigForOsuProvider.put(buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), null); + when(mockWifiManager.getMatchingPasspointConfigsForOsuProviders(any())).thenReturn( + matchingPasspointConfigForOsuProvider); + + // Second update + osuAccessPoints = tracker.updateOsuAccessPoints( + providersAndScans, new ArrayList<>()); + + // Returned AP should only be for Osu Provider 2 + assertThat(osuAccessPoints).hasSize(1); + assertThat(osuAccessPoints.get(0).getTitle()).isEqualTo(PROVIDER_FRIENDLY_NAME_2); + } + + /** + * Verifies that updateOsuAccessPoints will return matching cached APs and update their + * scan results instead of creating new APs. + */ + @Test + public void updateOsuAccessPoints_usesCachedAccessPoints() { + WifiTracker tracker = createMockedWifiTracker(); + + ScanResult result = buildScanResult1(); + + Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>(); + providersAndScans.put( + buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(result)); + + List<AccessPoint> osuAccessPointsFirstUpdate = tracker.updateOsuAccessPoints( + providersAndScans, new ArrayList<>()); + List<AccessPoint> cachedAccessPoints = new ArrayList<>(osuAccessPointsFirstUpdate); + + // New RSSI for second update + int prevRssi = result.level; + int newRssi = prevRssi + 10; + result.level = newRssi; + + List<AccessPoint> osuAccessPointsSecondUpdate = tracker.updateOsuAccessPoints( + providersAndScans, cachedAccessPoints); + + // Verify second update AP is the same object as the first update AP + assertTrue(osuAccessPointsFirstUpdate.get(0) + == osuAccessPointsSecondUpdate.get(0)); + // Verify second update AP has the average of the first and second update RSSIs + assertThat(osuAccessPointsSecondUpdate.get(0).getRssi()) + .isEqualTo((prevRssi + newRssi) / 2); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java index b8a143a376fd..c5f54bb0f0d9 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java @@ -46,6 +46,7 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.util.ReflectionHelpers; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -99,6 +100,20 @@ public class NetworkCycleDataLoaderTest { } @Test + public void loadInBackground_hasCyclePeriod_shouldLoadDataForSpecificCycles() { + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); + doNothing().when(mLoader).loadDataForSpecificCycles(); + final ArrayList<Long> cycles = new ArrayList<>(); + cycles.add(67890L); + cycles.add(12345L); + ReflectionHelpers.setField(mLoader, "mCycles", cycles); + + mLoader.loadInBackground(); + + verify(mLoader).loadDataForSpecificCycles(); + } + + @Test public void loadPolicyData_shouldRecordUsageFromPolicyCycle() { final int networkType = ConnectivityManager.TYPE_MOBILE; final String subId = "TestSubscriber"; @@ -139,6 +154,27 @@ public class NetworkCycleDataLoaderTest { verify(mLoader).recordUsage(fourWeeksAgo, now); } + @Test + public void loadDataForSpecificCycles_shouldRecordUsageForSpecifiedTime() { + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); + final long now = System.currentTimeMillis(); + final long tenDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 10); + final long twentyDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 20); + final long thirtyDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 30); + final ArrayList<Long> cycles = new ArrayList<>(); + cycles.add(now); + cycles.add(tenDaysAgo); + cycles.add(twentyDaysAgo); + cycles.add(thirtyDaysAgo); + ReflectionHelpers.setField(mLoader, "mCycles", cycles); + + mLoader.loadDataForSpecificCycles(); + + verify(mLoader).recordUsage(tenDaysAgo, now); + verify(mLoader).recordUsage(twentyDaysAgo, tenDaysAgo); + verify(mLoader).recordUsage(thirtyDaysAgo, twentyDaysAgo); + } + public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> { private NetworkCycleDataTestLoader(Context context) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 6152b8cbd562..db4b131a0f6f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -698,6 +698,9 @@ class SettingsProtoDumpUtil { Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, GlobalSettingsProto.Gpu.ANGLE_WHITELIST); dumpSetting(s, p, + Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, + GlobalSettingsProto.Gpu.SHOW_ANGLE_IN_USE_DIALOG); + dumpSetting(s, p, Settings.Global.GPU_DEBUG_LAYER_APP, GlobalSettingsProto.Gpu.DEBUG_LAYER_APP); dumpSetting(s, p, @@ -718,6 +721,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.GAME_DRIVER_WHITELIST, GlobalSettingsProto.Gpu.GAME_DRIVER_WHITELIST); + dumpSetting(s, p, + Settings.Global.GAME_DRIVER_BLACKLISTS, + GlobalSettingsProto.Gpu.GAME_DRIVER_BLACKLISTS); p.end(gpuToken); final long hdmiToken = p.start(GlobalSettingsProto.HDMI); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index e8c728d358ea..0da3b10778e9 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -82,6 +82,7 @@ <uses-permission android:name="android.permission.MOVE_PACKAGE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> + <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" /> <uses-permission android:name="android.permission.DELETE_CACHE_FILES" /> <uses-permission android:name="android.permission.DELETE_PACKAGES" /> <uses-permission android:name="android.permission.MANAGE_ROLLBACKS" /> diff --git a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml index 6f7f39810608..c8dc8e43893c 100644 --- a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml @@ -19,9 +19,21 @@ android:layout_width="match_parent" android:layout_height="match_parent" > - <include + <TextClock android:id="@+id/digital_clock" - layout="@layout/text_clock" + android:layout_marginLeft="20dp" + android:layout_marginTop="72dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|left" + android:textSize="44dp" + android:letterSpacing="0.05" + android:textColor="?attr/wallpaperTextColor" + android:singleLine="true" + style="@style/widget_big" + android:format12Hour="@string/keyguard_widget_12_hours_format" + android:format24Hour="@string/keyguard_widget_24_hours_format" + android:elegantTextHeight="false" /> <com.android.keyguard.clock.ImageClock android:id="@+id/analog_clock" diff --git a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml index 64b676f55fd6..116a044a7075 100644 --- a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml @@ -19,9 +19,21 @@ android:layout_width="match_parent" android:layout_height="match_parent" > - <include + <TextClock android:id="@+id/digital_clock" - layout="@layout/text_clock" + android:layout_marginLeft="20dp" + android:layout_marginTop="72dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|left" + android:textSize="44dp" + android:letterSpacing="0.05" + android:textColor="?attr/wallpaperTextColor" + android:singleLine="true" + style="@style/widget_big" + android:format12Hour="@string/keyguard_widget_12_hours_format" + android:format24Hour="@string/keyguard_widget_24_hours_format" + android:elegantTextHeight="false" /> <com.android.keyguard.clock.StretchAnalogClock android:id="@+id/analog_clock" diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 94481e7fe456..5714556ae8a3 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -64,6 +64,9 @@ charged, say that it is charged. --> <string name="keyguard_charged">Fully charged</string> + <!-- When the lock screen is showing and the phone plugged in, and the battery is not fully charged, say that it's wirelessly charging. [CHAR LIMIT=50] --> + <string name="keyguard_plugged_in_wireless"><xliff:g id="percentage" example="20%">%s</xliff:g> • Wirelessly Charging</string> + <!-- When the lock screen is showing and the phone plugged in, and the battery is not fully charged, say that it's charging. --> <string name="keyguard_plugged_in"><xliff:g id="percentage">%s</xliff:g> • Charging</string> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index db92ed258064..7d009b5a9f18 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -955,6 +955,9 @@ <!-- Interruption level: Alarms only. Optimized for narrow two-line display. [CHAR LIMIT=40] --> <string name="interruption_level_alarms_twoline">Alarms\nonly</string> + <!-- Indication on the keyguard that is shown when the device is wirelessly charging. [CHAR LIMIT=80]--> + <string name="keyguard_indication_charging_time_wireless"><xliff:g id="percentage" example="20%">%2$s</xliff:g> • Wirelessly Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%1$s</xliff:g> until full)</string> + <!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=50]--> <string name="keyguard_indication_charging_time"><xliff:g id="percentage">%2$s</xliff:g> • Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%1$s</xliff:g> until full)</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index fc1843ba982a..822920e63460 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -187,7 +187,9 @@ public class KeyguardClockSwitch extends RelativeLayout { View bigClockView = mClockPlugin.getBigClockView(); if (bigClockView != null) { container.addView(bigClockView); - container.setVisibility(View.VISIBLE); + if (container.getVisibility() == View.GONE) { + container.setVisibility(View.VISIBLE); + } } } mBigClockContainer = container; diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java index 3591dc82c8ec..4d8cf963ff50 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java @@ -22,6 +22,7 @@ import android.content.res.Resources; import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; +import android.widget.FrameLayout.LayoutParams; import com.android.keyguard.R; @@ -80,8 +81,9 @@ public class ClockLayout extends FrameLayout { // Put digital clock in two left corner of the screen. if (mDigitalClock != null) { - mDigitalClock.setX(0.1f * getWidth() + offsetX); - mDigitalClock.setY(0.1f * getHeight() + offsetY); + LayoutParams params = (LayoutParams) mDigitalClock.getLayoutParams(); + mDigitalClock.setX(offsetX + params.leftMargin); + mDigitalClock.setY(offsetY + params.topMargin); } // Put the analog clock in the middle of the screen. diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 4d708908975e..038f4912e40f 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -290,10 +290,6 @@ public class AssistManager implements ConfigurationChangedReceiver { return mAssistUtils.isSessionRunning(); } - public void destroy() { - mWindowManager.removeViewImmediate(mView); - } - private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) { replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME, isService); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 82aa4737af99..9c65994425ed 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -79,6 +79,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call private static final int MSG_RESIZE_IMMEDIATE = 1; private static final int MSG_RESIZE_ANIMATE = 2; + private static final int MSG_OFFSET_ANIMATE = 3; private Context mContext; private IActivityManager mActivityManager; @@ -360,9 +361,20 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call /** * Animates the PiP to offset it from the IME or shelf. */ - void animateToOffset(Rect toBounds) { + void animateToOffset(Rect originalBounds, int offset) { cancelAnimations(); - resizeAndAnimatePipUnchecked(toBounds, SHIFT_DURATION); + adjustAndAnimatePipOffset(originalBounds, offset, SHIFT_DURATION); + } + + private void adjustAndAnimatePipOffset(Rect originalBounds, int offset, int duration) { + if (offset == 0) { + return; + } + SomeArgs args = SomeArgs.obtain(); + args.arg1 = originalBounds; + args.argi1 = offset; + args.argi2 = duration; + mHandler.sendMessage(mHandler.obtainMessage(MSG_OFFSET_ANIMATE, args)); } /** @@ -549,6 +561,31 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return true; } + case MSG_OFFSET_ANIMATE: { + SomeArgs args = (SomeArgs) msg.obj; + Rect originalBounds = (Rect) args.arg1; + final int offset = args.argi1; + final int duration = args.argi2; + try { + StackInfo stackInfo = mActivityTaskManager.getStackInfo( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); + if (stackInfo == null) { + // In the case where we've already re-expanded or dismissed the PiP, then + // just skip the resize + return true; + } + + mActivityTaskManager.offsetPinnedStackBounds(stackInfo.stackId, originalBounds, + 0/* xOffset */, offset, duration); + Rect toBounds = new Rect(originalBounds); + toBounds.offset(0, offset); + mBounds.set(toBounds); + } catch (RemoteException e) { + Log.e(TAG, "Could not animate offset pinned stack with offset: " + offset, e); + } + return true; + } + default: return false; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 6a9f24c619fe..cef1b6b1d93b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -346,12 +346,11 @@ public class PipTouchHandler { } private void animateToOffset(Rect animatingBounds, Rect toAdjustedBounds) { - final Rect bounds = new Rect(animatingBounds); - bounds.offset(0, toAdjustedBounds.bottom - bounds.top); + int offset = toAdjustedBounds.bottom - animatingBounds.top; // In landscape mode, PIP window can go offset while launching IME. We want to align the // the top of the PIP window with the top of the movement bounds in that case. - bounds.offset(0, Math.max(0, mMovementBounds.top - bounds.top)); - mMotionHelper.animateToOffset(bounds); + offset += Math.max(0, mMovementBounds.top - animatingBounds.top); + mMotionHelper.animateToOffset(animatingBounds, offset); } private void onRegistrationChanged(boolean isRegistered) { diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index f0543454073d..c43f5728aaa2 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -54,6 +54,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.time.Duration; import java.util.Arrays; +import java.util.concurrent.Future; public class PowerUI extends SystemUI { static final String TAG = "PowerUI"; @@ -80,6 +81,7 @@ public class PowerUI extends SystemUI { private Estimate mLastEstimate; private boolean mLowWarningShownThisChargeCycle; private boolean mSevereWarningShownThisChargeCycle; + private Future mLastShowWarningTask; private int mLowBatteryAlertCloseLevel; private final int[] mLowBatteryReminderLevels = new int[2]; @@ -247,7 +249,10 @@ public class PowerUI extends SystemUI { } // Show the correct version of low battery warning if needed - ThreadUtils.postOnBackgroundThread(() -> { + if (mLastShowWarningTask != null) { + mLastShowWarningTask.cancel(true); + } + mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> { maybeShowBatteryWarning( oldBatteryLevel, plugged, oldPlugged, oldBucket, bucket); }); @@ -276,7 +281,7 @@ public class PowerUI extends SystemUI { estimate = mEnhancedEstimates.getEstimate(); mLastEstimate = estimate; } - // Turbo is not always booted once SysUI is running so we have ot make sure we actually + // Turbo is not always booted once SysUI is running so we have to make sure we actually // get data back if (estimate != null) { mTimeRemaining = estimate.estimateMillis; @@ -355,13 +360,26 @@ public class PowerUI extends SystemUI { // Only show the low warning once per charge cycle & no battery saver final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !isPowerSaver && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold() - || mBatteryLevel <= warnLevel); + || mBatteryLevel <= warnLevel); // Only show the severe warning once per charge cycle final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle && (timeRemaining < mEnhancedEstimates.getSevereWarningThreshold() - || mBatteryLevel <= critLevel); - + || mBatteryLevel <= critLevel); + + final boolean canShow = canShowWarning || canShowSevereWarning; + if (DEBUG) { + Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith values: " + + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle + + " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle + + " mEnhancedEstimates.timeremaining: " + timeRemaining + + " mBatteryLevel: " + mBatteryLevel + + " canShowWarning: " + canShowWarning + + " canShowSevereWarning: " + canShowSevereWarning + + " plugged: " + plugged + + " batteryStatus: " + batteryStatus + + " isPowerSaver: " + isPowerSaver); + } return canShowWarning || canShowSevereWarning; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index be749aef7f25..164f5826061e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -424,22 +424,28 @@ public class KeyguardIndicationController implements StateListener { final boolean hasChargingTime = chargingTimeRemaining > 0; int chargingId; - switch (mChargingSpeed) { - case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST: - chargingId = hasChargingTime - ? R.string.keyguard_indication_charging_time_fast - : R.string.keyguard_plugged_in_charging_fast; - break; - case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY: - chargingId = hasChargingTime - ? R.string.keyguard_indication_charging_time_slowly - : R.string.keyguard_plugged_in_charging_slowly; - break; - default: - chargingId = hasChargingTime - ? R.string.keyguard_indication_charging_time - : R.string.keyguard_plugged_in; - break; + if (mPowerPluggedInWired) { + switch (mChargingSpeed) { + case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST: + chargingId = hasChargingTime + ? R.string.keyguard_indication_charging_time_fast + : R.string.keyguard_plugged_in_charging_fast; + break; + case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY: + chargingId = hasChargingTime + ? R.string.keyguard_indication_charging_time_slowly + : R.string.keyguard_plugged_in_charging_slowly; + break; + default: + chargingId = hasChargingTime + ? R.string.keyguard_indication_charging_time + : R.string.keyguard_plugged_in; + break; + } + } else { + chargingId = hasChargingTime + ? R.string.keyguard_indication_charging_time_wireless + : R.string.keyguard_plugged_in_wireless; } String percentage = NumberFormat.getPercentInstance() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index e11ec2d24e29..2edea789d820 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -158,18 +158,6 @@ public class NavigationBarController implements DisplayListener, Callbacks { }); } - /** Removes navigation bars. */ - public void destroy() { - mDisplayManager.unregisterDisplayListener(this); - if (mNavigationBars.size() > 0) { - for (int i = 0; i < mNavigationBars.size(); i++) { - int displayId = mNavigationBars.keyAt(i); - removeNavigationBar(displayId); - } - mNavigationBars.clear(); - } - } - private void removeNavigationBar(int displayId) { NavigationBarFragment navBar = mNavigationBars.get(displayId); if (navBar != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index c25b7cfd804c..1cc6dc391c1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -42,8 +42,6 @@ public interface NotificationLockscreenUserManager { /** Adds a listener to be notified when the current user changes. */ void addUserChangedListener(UserChangedListener listener); - void destroy(); - SparseArray<UserInfo> getCurrentProfiles(); void setLockscreenPublicMode(boolean isProfilePublic, int userId); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 6a49b804f5ba..9cb6f11fb558 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -550,12 +550,6 @@ public class NotificationLockscreenUserManagerImpl implements return mKeyguardMonitor.isSecure() || mLockPatternUtils.isSecure(userId); } - public void destroy() { - mContext.unregisterReceiver(mBaseBroadcastReceiver); - mContext.unregisterReceiver(mAllUsersReceiver); - mListeners.clear(); - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NotificationLockscreenUserManager state:"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java index 88f4ca239af4..769cbb7b984c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java @@ -61,11 +61,6 @@ public class NotificationListController { mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); } - /** Should be called when the list controller is being destroyed. */ - public void destroy() { - mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener); - } - @SuppressWarnings("FieldCanBeLocal") private final NotificationEntryListener mEntryListener = new NotificationEntryListener() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 7105876907bf..fbf1e310abf2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -578,6 +578,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onDensityOrFontScaleChanged() { + reinflateViews(); + } + + private void reinflateViews() { inflateFooterView(); inflateEmptyShadeView(); updateFooter(); @@ -608,6 +612,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mCornerRadius = newRadius; invalidate(); } + reinflateViews(); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 4c1c0a4b4e58..de0e194ef90c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -141,10 +141,6 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, mAnimationStateHandler = handler; } - public void destroy() { - Dependency.get(StatusBarStateController.class).removeCallback(this); - } - private void initResources() { Resources resources = mContext.getResources(); mStatusBarHeight = resources.getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 18711c0d1ae3..2799191a886f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -284,25 +284,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, }); } - public void destroy() { - mRotationLockController.removeCallback(this); - mBluetooth.removeCallback(this); - mProvisionedController.removeCallback(this); - mZenController.removeCallback(this); - mCast.removeCallback(mCastCallback); - mHotspot.removeCallback(mHotspotCallback); - mNextAlarmController.removeCallback(mNextAlarmCallback); - mDataSaver.removeCallback(this); - mKeyguardMonitor.removeCallback(this); - mPrivacyItemController.removeCallback(this); - SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this); - mContext.unregisterReceiver(mIntentReceiver); - - NotificationManager noMan = mContext.getSystemService(NotificationManager.class); - mCurrentNotifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS, - new UserHandle(v.second))); - } - @Override public void onZenChanged(int zen) { updateVolumeZen(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 9f3bec60cb6d..849570937ea4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2843,38 +2843,6 @@ public class StatusBar extends SystemUI implements DemoMode, startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */); } - public void destroy() { - // Begin old BaseStatusBar.destroy(). - mContext.unregisterReceiver(mBannerActionBroadcastReceiver); - mLockscreenUserManager.destroy(); - try { - mNotificationListener.unregisterAsSystemService(); - } catch (RemoteException e) { - // Ignore. - } - mNotificationListController.destroy(); - // End old BaseStatusBar.destroy(). - if (mStatusBarWindow != null) { - mWindowManager.removeViewImmediate(mStatusBarWindow); - mStatusBarWindow = null; - } - mNavigationBarController.destroy(); - mContext.unregisterReceiver(mBroadcastReceiver); - mContext.unregisterReceiver(mDemoReceiver); - mAssistManager.destroy(); - mHeadsUpManager.destroy(); - mStatusBarStateController.removeCallback(this); - - if (mQSPanel != null && mQSPanel.getHost() != null) { - mQSPanel.getHost().destroy(); - } - Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(null); - mDeviceProvisionedController.removeCallback(mUserSetupObserver); - Dependency.get(ConfigurationController.class).removeCallback(this); - mZenController.removeCallback(this); - mAppOpsController.removeCallback(APP_OPS, this); - } - private boolean mDemoModeAllowed; private boolean mDemoMode; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index e9705ff35a4d..3ce66c5de372 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -258,6 +258,11 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } } + @Override + public void onOverlayChanged() { + onDensityOrFontScaleChanged(); + } + private void updateNotificationOnUiModeChanged() { ArrayList<NotificationEntry> userNotifications = mEntryManager.getNotificationData().getNotificationsForCurrentUser(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 5d03f19f4655..b0d1106ecb24 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -35,7 +35,6 @@ import android.testing.TestableLooper.RunWithLooper; import android.text.TextPaint; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TextClock; @@ -60,6 +59,8 @@ import org.mockito.MockitoAnnotations; @RunWithLooper(setAsMainLooper = true) public class KeyguardClockSwitchTest extends SysuiTestCase { private FrameLayout mClockContainer; + private FrameLayout mBigClockContainer; + private TextClock mBigClock; private StatusBarStateController.StateListener mStateListener; @Mock @@ -73,6 +74,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { mKeyguardClockSwitch = (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null); mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view); + mBigClockContainer = new FrameLayout(getContext()); + mBigClock = new TextClock(getContext()); MockitoAnnotations.initMocks(this); when(mClockView.getPaint()).thenReturn(mock(TextPaint.class)); mStateListener = mKeyguardClockSwitch.getStateListener(); @@ -93,19 +96,17 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { @Test public void onPluginConnected_showPluginBigClock() { // GIVEN that the container for the big clock has visibility GONE - FrameLayout bigClockContainer = new FrameLayout(getContext()); - bigClockContainer.setVisibility(GONE); - mKeyguardClockSwitch.setBigClockContainer(bigClockContainer); + mBigClockContainer.setVisibility(GONE); + mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); // AND the plugin returns a view for the big clock ClockPlugin plugin = mock(ClockPlugin.class); - TextClock pluginView = new TextClock(getContext()); - when(plugin.getBigClockView()).thenReturn(pluginView); + when(plugin.getBigClockView()).thenReturn(mBigClock); // WHEN the plugin is connected mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); // THEN the big clock container is visible and it is the parent of the // big clock view. - assertThat(bigClockContainer.getVisibility()).isEqualTo(VISIBLE); - assertThat(pluginView.getParent()).isEqualTo(bigClockContainer); + assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mBigClock.getParent()).isEqualTo(mBigClockContainer); } @Test @@ -246,24 +247,64 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { @Test public void onStateChanged_InvisibleInShade() { // GIVEN that the big clock container is visible - ViewGroup container = mock(ViewGroup.class); - when(container.getVisibility()).thenReturn(View.VISIBLE); - mKeyguardClockSwitch.setBigClockContainer(container); + mBigClockContainer.setVisibility(View.VISIBLE); + mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); // WHEN transitioned to SHADE state mStateListener.onStateChanged(StatusBarState.SHADE); // THEN the container is invisible. - verify(container).setVisibility(View.INVISIBLE); + assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.INVISIBLE); } @Test public void onStateChanged_VisibleInKeyguard() { // GIVEN that the big clock container is invisible - ViewGroup container = mock(ViewGroup.class); - when(container.getVisibility()).thenReturn(View.INVISIBLE); - mKeyguardClockSwitch.setBigClockContainer(container); + mBigClockContainer.setVisibility(View.INVISIBLE); + mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); // WHEN transitioned to KEYGUARD state mStateListener.onStateChanged(StatusBarState.KEYGUARD); // THEN the container is visible. - verify(container).setVisibility(View.VISIBLE); + assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void setBigClockContainer_visible() { + // GIVEN that the big clock container is visible + mBigClockContainer.setVisibility(View.VISIBLE); + // AND GIVEN that a plugin is active. + ClockPlugin plugin = mock(ClockPlugin.class); + when(plugin.getBigClockView()).thenReturn(mBigClock); + mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); + // WHEN the container is associated with the clock switch + mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); + // THEN the container remains visible. + assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void setBigClockContainer_invisible() { + // GIVEN that the big clock container is invisible + mBigClockContainer.setVisibility(View.INVISIBLE); + // AND GIVEN that a plugin is active. + ClockPlugin plugin = mock(ClockPlugin.class); + when(plugin.getBigClockView()).thenReturn(mBigClock); + mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); + // WHEN the container is associated with the clock switch + mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); + // THEN the container remains invisible. + assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.INVISIBLE); + } + + @Test + public void setBigClockContainer_gone() { + // GIVEN that the big clock container is gone + mBigClockContainer.setVisibility(View.GONE); + // AND GIVEN that a plugin is active. + ClockPlugin plugin = mock(ClockPlugin.class); + when(plugin.getBigClockView()).thenReturn(mBigClock); + mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); + // WHEN the container is associated with the clock switch + mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); + // THEN the container is made visible. + assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.VISIBLE); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index cad1a96b8075..79bc0a39d59c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -521,6 +521,16 @@ public class NotificationEntryManagerTest extends SysuiTestCase { verify(extender2).setShouldManageLifetime(mEntry, false); } + /** + * Ensure that calling NotificationEntryManager.performRemoveNotification() doesn't crash when + * given a notification that has already been removed from NotificationData. + */ + @Test + public void testPerformRemoveNotification_removedEntry() { + mEntryManager.getNotificationData().remove(mSbn.getKey(), null /* ranking */); + mEntryManager.performRemoveNotification(mSbn); + } + private Notification.Action createAction() { return new Notification.Action.Builder( Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon), diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 3c910696ba60..d9adec85d40e 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6975,6 +6975,23 @@ message MetricsEvent { // formerly: histogram system_cost_for_smart_sharing FIELD_TIME_TO_APP_TARGETS = 1653; + // Open: Settings > Panel for Internet Connectivity + PANEL_INTERNET_CONNECTIVITY = 1654; + + // Open: Settings > Panel for Volume + PANEL_VOLUME = 1655; + + // Open: Settings > Panel for NFC + PANEL_NFC = 1656; + + // Open: Settings > Panel for Media Output + PANEL_MEDIA_OUTPUT = 1657; + + // ACTION: An interaction with a Slice or other component in the Panel. + // CATEGORY: SETTINGS + // OS: Q + ACTION_PANEL_INTERACTION = 1658; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index 82359c547a88..ad0ed8beb7c5 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -518,6 +518,9 @@ message WifiLog { // WifiConfigStore read/write metrics. optional WifiConfigStoreIO wifi_config_store_io = 137; + + // Total number of saved networks with mac randomization enabled. + optional int32 num_saved_networks_with_mac_randomization = 138; } // Information that gets logged for every WiFi connection. diff --git a/sax/tests/saxtests/Android.bp b/sax/tests/saxtests/Android.bp new file mode 100644 index 000000000000..5889f769a645 --- /dev/null +++ b/sax/tests/saxtests/Android.bp @@ -0,0 +1,11 @@ +android_test { + name: "FrameworksSaxTests", + // Include all test java files. + srcs: ["src/**/*.java"], + libs: [ + "android.test.runner", + "android.test.base", + ], + static_libs: ["junit"], + platform_apis: true, +} diff --git a/sax/tests/saxtests/Android.mk b/sax/tests/saxtests/Android.mk deleted file mode 100644 index c4517a9a954a..000000000000 --- a/sax/tests/saxtests/Android.mk +++ /dev/null @@ -1,16 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -# Include all test java files. -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base -LOCAL_STATIC_JAVA_LIBRARIES := junit -LOCAL_PACKAGE_NAME := FrameworksSaxTests -LOCAL_PRIVATE_PLATFORM_APIS := true - -include $(BUILD_PACKAGE) - diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS new file mode 100644 index 000000000000..265674a74b7e --- /dev/null +++ b/services/accessibility/OWNERS @@ -0,0 +1,3 @@ +svetoslavganov@google.com +pweaver@google.com +rhedjao@google.com diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListing.java b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListingMap.java index 2d2e88afccf1..a44890118717 100644 --- a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListing.java +++ b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListingMap.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,13 +11,14 @@ * 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 + * limitations under the License. */ package com.android.server.backup.encryption.chunk; import android.annotation.Nullable; import android.util.proto.ProtoInputStream; + import java.io.IOException; import java.util.Collections; import java.util.HashMap; @@ -30,16 +31,16 @@ import java.util.Map; * It can then tell the server to use that chunk, through telling it the position and length of the * chunk in the previous backup's blob. */ -public class ChunkListing { +public class ChunkListingMap { /** - * Reads a ChunkListing from a {@link ProtoInputStream}. Expects the message to be of format + * Reads a ChunkListingMap from a {@link ProtoInputStream}. Expects the message to be of format * {@link ChunksMetadataProto.ChunkListing}. * * @param inputStream Currently at a {@link ChunksMetadataProto.ChunkListing} message. * @throws IOException when the message is not structured as expected or a field can not be * read. */ - public static ChunkListing readFromProto(ProtoInputStream inputStream) throws IOException { + public static ChunkListingMap readFromProto(ProtoInputStream inputStream) throws IOException { Map<ChunkHash, Entry> entries = new HashMap(); long start = 0; @@ -54,12 +55,12 @@ public class ChunkListing { } } - return new ChunkListing(entries); + return new ChunkListingMap(entries); } private final Map<ChunkHash, Entry> mChunksByHash; - private ChunkListing(Map<ChunkHash, Entry> chunksByHash) { + private ChunkListingMap(Map<ChunkHash, Entry> chunksByHash) { mChunksByHash = Collections.unmodifiableMap(new HashMap<>(chunksByHash)); } diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java index 275661084aa3..5b0de5e2aae0 100644 --- a/services/core/java/com/android/server/AlarmManagerInternal.java +++ b/services/core/java/com/android/server/AlarmManagerInternal.java @@ -26,6 +26,8 @@ public interface AlarmManagerInternal { void broadcastAlarmComplete(int recipientUid); } + /** Returns true if AlarmManager is delaying alarms due to device idle. */ + boolean isIdling(); public void removeAlarmsForUid(int uid); public void registerInFlightListener(InFlightListener callback); } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 10b532700d1b..a400cc384a58 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1991,6 +1991,11 @@ class AlarmManagerService extends SystemService { */ private final class LocalService implements AlarmManagerInternal { @Override + public boolean isIdling() { + return isIdlingImpl(); + } + + @Override public void removeAlarmsForUid(int uid) { synchronized (mLock) { removeLocked(uid); @@ -2823,6 +2828,12 @@ class AlarmManagerService extends SystemService { } } + private boolean isIdlingImpl() { + synchronized (mLock) { + return mPendingIdleUntil != null; + } + } + AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) { synchronized (mLock) { return mNextAlarmClockForUser.get(userId); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index fe4411c1dbb5..d1cd072ee215 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -38,7 +38,6 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.uidRulesToString; -import static android.net.NetworkStack.NETWORKSTACK_PACKAGE_NAME; import static android.net.shared.NetworkMonitorUtils.isValidationRequired; import static android.net.shared.NetworkParcelableUtil.toStableParcelable; import static android.os.Process.INVALID_UID; @@ -2667,9 +2666,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public void showProvisioningNotification(String action) { + public void showProvisioningNotification(String action, String packageName) { final Intent intent = new Intent(action); - intent.setPackage(NETWORKSTACK_PACKAGE_NAME); + intent.setPackage(packageName); final PendingIntent pendingIntent; // Only the system server can register notifications with package "android" diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 39030aaf3eb4..6b663941f8fb 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -271,6 +271,7 @@ public class DeviceIdleController extends SystemService private static final int EVENT_BUFFER_SIZE = 100; private AlarmManager mAlarmManager; + private AlarmManagerInternal mLocalAlarmManager; private IBatteryStats mBatteryStats; private ActivityManagerInternal mLocalActivityManager; private ActivityTaskManagerInternal mLocalActivityTaskManager; @@ -616,7 +617,8 @@ public class DeviceIdleController extends SystemService } }; - private final AlarmManager.OnAlarmListener mDeepAlarmListener + @VisibleForTesting + final AlarmManager.OnAlarmListener mDeepAlarmListener = new AlarmManager.OnAlarmListener() { @Override public void onAlarm() { @@ -1874,6 +1876,7 @@ public class DeviceIdleController extends SystemService if (phase == PHASE_SYSTEM_SERVICES_READY) { synchronized (this) { mAlarmManager = mInjector.getAlarmManager(); + mLocalAlarmManager = getLocalService(AlarmManagerInternal.class); mBatteryStats = BatteryStatsService.getService(); mLocalActivityManager = getLocalService(ActivityManagerInternal.class); mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class); @@ -2605,6 +2608,16 @@ public class DeviceIdleController extends SystemService // next natural time to come out of it. } + + /** Returns true if the screen is locked. */ + @VisibleForTesting + boolean isKeyguardShowing() { + synchronized (this) { + return mScreenLocked; + } + } + + @VisibleForTesting void keyguardShowingLocked(boolean showing) { if (DEBUG) Slog.i(TAG, "keyguardShowing=" + showing); if (mScreenLocked != showing) { @@ -2616,25 +2629,38 @@ public class DeviceIdleController extends SystemService } } + @VisibleForTesting void scheduleReportActiveLocked(String activeReason, int activeUid) { Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason); mHandler.sendMessage(msg); } void becomeActiveLocked(String activeReason, int activeUid) { - if (DEBUG) Slog.i(TAG, "becomeActiveLocked, reason = " + activeReason); + becomeActiveLocked(activeReason, activeUid, mConstants.INACTIVE_TIMEOUT, true); + } + + private void becomeActiveLocked(String activeReason, int activeUid, + long newInactiveTimeout, boolean changeLightIdle) { + if (DEBUG) { + Slog.i(TAG, "becomeActiveLocked, reason=" + activeReason + + ", changeLightIdle=" + changeLightIdle); + } if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) { EventLogTags.writeDeviceIdle(STATE_ACTIVE, activeReason); - EventLogTags.writeDeviceIdleLight(LIGHT_STATE_ACTIVE, activeReason); - scheduleReportActiveLocked(activeReason, activeUid); mState = STATE_ACTIVE; - mLightState = LIGHT_STATE_ACTIVE; - mInactiveTimeout = mConstants.INACTIVE_TIMEOUT; + mInactiveTimeout = newInactiveTimeout; mCurIdleBudget = 0; mMaintenanceStartTime = 0; resetIdleManagementLocked(); - resetLightIdleManagementLocked(); - addEvent(EVENT_NORMAL, activeReason); + + if (changeLightIdle) { + EventLogTags.writeDeviceIdleLight(LIGHT_STATE_ACTIVE, activeReason); + mLightState = LIGHT_STATE_ACTIVE; + resetLightIdleManagementLocked(); + // Only report active if light is also ACTIVE. + scheduleReportActiveLocked(activeReason, activeUid); + addEvent(EVENT_NORMAL, activeReason); + } } } @@ -2654,50 +2680,82 @@ public class DeviceIdleController extends SystemService } } + /** Sanity check to make sure DeviceIdleController and AlarmManager are on the same page. */ + private void verifyAlarmStateLocked() { + if (mState == STATE_ACTIVE && mNextAlarmTime != 0) { + Slog.wtf(TAG, "mState=ACTIVE but mNextAlarmTime=" + mNextAlarmTime); + } + if (mState != STATE_IDLE && mLocalAlarmManager.isIdling()) { + Slog.wtf(TAG, "mState=" + stateToString(mState) + " but AlarmManager is idling"); + } + if (mState == STATE_IDLE && !mLocalAlarmManager.isIdling()) { + Slog.wtf(TAG, "mState=IDLE but AlarmManager is not idling"); + } + if (mLightState == LIGHT_STATE_ACTIVE && mNextLightAlarmTime != 0) { + Slog.wtf(TAG, "mLightState=ACTIVE but mNextLightAlarmTime is " + + TimeUtils.formatDuration(mNextLightAlarmTime - SystemClock.elapsedRealtime()) + + " from now"); + } + } + void becomeInactiveIfAppropriateLocked() { - if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()"); - if ((!mScreenOn && !mCharging) || mForceIdle) { - // Become inactive and determine if we will ultimately go idle. - if (mDeepEnabled) { - if (mQuickDozeActivated) { - if (mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE - || mState == STATE_IDLE_MAINTENANCE) { - // Already "idling". Don't want to restart the process. - // mLightState can't be LIGHT_STATE_ACTIVE if mState is any of these 3 - // values, so returning here is safe. - return; - } - if (DEBUG) { - Slog.d(TAG, "Moved from " - + stateToString(mState) + " to STATE_QUICK_DOZE_DELAY"); - } - mState = STATE_QUICK_DOZE_DELAY; - // Make sure any motion sensing or locating is stopped. - resetIdleManagementLocked(); - // Wait a small amount of time in case something (eg: background service from - // recently closed app) needs to finish running. - scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false); - EventLogTags.writeDeviceIdle(mState, "no activity"); - } else if (mState == STATE_ACTIVE) { - mState = STATE_INACTIVE; - if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE"); - resetIdleManagementLocked(); - long delay = mInactiveTimeout; - if (shouldUseIdleTimeoutFactorLocked()) { - delay = (long) (mPreIdleFactor * delay); - } - scheduleAlarmLocked(delay, false); - EventLogTags.writeDeviceIdle(mState, "no activity"); + verifyAlarmStateLocked(); + + final boolean isScreenBlockingInactive = + mScreenOn && (!mConstants.WAIT_FOR_UNLOCK || !mScreenLocked); + if (DEBUG) { + Slog.d(TAG, "becomeInactiveIfAppropriateLocked():" + + " isScreenBlockingInactive=" + isScreenBlockingInactive + + " (mScreenOn=" + mScreenOn + + ", WAIT_FOR_UNLOCK=" + mConstants.WAIT_FOR_UNLOCK + + ", mScreenLocked=" + mScreenLocked + ")" + + " mCharging=" + mCharging + + " mForceIdle=" + mForceIdle + ); + } + if (!mForceIdle && (mCharging || isScreenBlockingInactive)) { + return; + } + // Become inactive and determine if we will ultimately go idle. + if (mDeepEnabled) { + if (mQuickDozeActivated) { + if (mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE + || mState == STATE_IDLE_MAINTENANCE) { + // Already "idling". Don't want to restart the process. + // mLightState can't be LIGHT_STATE_ACTIVE if mState is any of these 3 + // values, so returning here is safe. + return; } - } - if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) { - mLightState = LIGHT_STATE_INACTIVE; - if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE"); - resetLightIdleManagementLocked(); - scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT); - EventLogTags.writeDeviceIdleLight(mLightState, "no activity"); + if (DEBUG) { + Slog.d(TAG, "Moved from " + + stateToString(mState) + " to STATE_QUICK_DOZE_DELAY"); + } + mState = STATE_QUICK_DOZE_DELAY; + // Make sure any motion sensing or locating is stopped. + resetIdleManagementLocked(); + // Wait a small amount of time in case something (eg: background service from + // recently closed app) needs to finish running. + scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false); + EventLogTags.writeDeviceIdle(mState, "no activity"); + } else if (mState == STATE_ACTIVE) { + mState = STATE_INACTIVE; + if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE"); + resetIdleManagementLocked(); + long delay = mInactiveTimeout; + if (shouldUseIdleTimeoutFactorLocked()) { + delay = (long) (mPreIdleFactor * delay); + } + scheduleAlarmLocked(delay, false); + EventLogTags.writeDeviceIdle(mState, "no activity"); } } + if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) { + mLightState = LIGHT_STATE_INACTIVE; + if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE"); + resetLightIdleManagementLocked(); + scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT); + EventLogTags.writeDeviceIdleLight(mLightState, "no activity"); + } } private void resetIdleManagementLocked() { @@ -3216,33 +3274,10 @@ public class DeviceIdleController extends SystemService // The device is not yet active, so we want to go back to the pending idle // state to wait again for no motion. Note that we only monitor for motion // after moving out of the inactive state, so no need to worry about that. - boolean becomeInactive = false; - if (mState != STATE_ACTIVE) { - // Motion shouldn't affect light state, if it's already in doze-light or maintenance - boolean lightIdle = mLightState == LIGHT_STATE_IDLE - || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK - || mLightState == LIGHT_STATE_IDLE_MAINTENANCE; - if (!lightIdle) { - // Only switch to active state if we're not in either idle state - scheduleReportActiveLocked(type, Process.myUid()); - addEvent(EVENT_NORMAL, type); - } - mActiveReason = ACTIVE_REASON_MOTION; - mState = STATE_ACTIVE; - mInactiveTimeout = timeout; - mCurIdleBudget = 0; - mMaintenanceStartTime = 0; - EventLogTags.writeDeviceIdle(mState, type); - becomeInactive = true; - updateActiveConstraintsLocked(); - } - if (mLightState == LIGHT_STATE_OVERRIDE) { - // We went out of light idle mode because we had started deep idle mode... let's - // now go back and reset things so we resume light idling if appropriate. - mLightState = LIGHT_STATE_ACTIVE; - EventLogTags.writeDeviceIdleLight(mLightState, type); - becomeInactive = true; - } + final boolean becomeInactive = mState != STATE_ACTIVE + || mLightState == LIGHT_STATE_OVERRIDE; + // We only want to change the IDLE state if it's OVERRIDE. + becomeActiveLocked(type, Process.myUid(), timeout, mLightState == LIGHT_STATE_OVERRIDE); if (becomeInactive) { becomeInactiveIfAppropriateLocked(); } diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java index b3084f50071f..ebd4c5584fe1 100644 --- a/services/core/java/com/android/server/ExtconUEventObserver.java +++ b/services/core/java/com/android/server/ExtconUEventObserver.java @@ -22,8 +22,11 @@ import android.util.Slog; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.regex.Pattern; /** * A specialized UEventObserver that receives UEvents from the kernel for devices in the {@code @@ -40,13 +43,14 @@ import java.util.Map; * time in that process. Once started the UEvent thread will not stop (although it can stop * notifying UEventObserver's via stopObserving()). * - * <p> - * * @hide */ public abstract class ExtconUEventObserver extends UEventObserver { private static final String TAG = "ExtconUEventObserver"; private static final boolean LOG = false; + private static final String SELINUX_POLICIES_NEED_TO_BE_CHANGED = + "This probably mean the selinux policies need to be changed."; + private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>(); @Override @@ -70,15 +74,47 @@ public abstract class ExtconUEventObserver extends UEventObserver { /** Starts observing {@link ExtconInfo#getDevicePath()}. */ public void startObserving(ExtconInfo extconInfo) { - mExtconInfos.put(extconInfo.getDevicePath(), extconInfo); - if (LOG) Slog.v(TAG, "Observing " + extconInfo.getDevicePath()); - startObserving("DEVPATH=" + extconInfo.getDevicePath()); + String devicePath = extconInfo.getDevicePath(); + if (devicePath == null) { + Slog.wtf(TAG, "Unable to start observing " + extconInfo.getName() + + " because the device path is null. " + SELINUX_POLICIES_NEED_TO_BE_CHANGED); + } else { + mExtconInfos.put(devicePath, extconInfo); + if (LOG) Slog.v(TAG, "Observing " + devicePath); + startObserving("DEVPATH=" + devicePath); + } } /** An External Connection to watch. */ public static final class ExtconInfo { private static final String TAG = "ExtconInfo"; + /** Returns a new list of all external connections whose name matches {@code regex}. */ + public static List<ExtconInfo> getExtconInfos(@Nullable String regex) { + Pattern p = regex == null ? null : Pattern.compile(regex); + File file = new File("/sys/class/extcon"); + File[] files = file.listFiles(); + if (files == null) { + Slog.wtf(TAG, file + " exists " + file.exists() + " isDir " + file.isDirectory() + + " but listFiles returns null. " + + SELINUX_POLICIES_NEED_TO_BE_CHANGED); + return new ArrayList<>(0); // Always return a new list. + } else { + ArrayList list = new ArrayList(files.length); + for (File f : files) { + String name = f.getName(); + if (p == null || p.matcher(name).matches()) { + ExtconInfo uei = new ExtconInfo(name); + list.add(uei); + if (LOG) Slog.d(TAG, name + " matches " + regex); + } else { + if (LOG) Slog.d(TAG, name + " does not match " + regex); + } + } + return list; + } + } + private final String mName; public ExtconInfo(String name) { diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 4834ce0da9b3..3479e18b97c5 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -67,6 +67,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.IInterface; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; @@ -109,6 +110,7 @@ import com.android.server.location.LocationRequestStatistics.PackageProviderKey; import com.android.server.location.LocationRequestStatistics.PackageStatistics; import com.android.server.location.MockProvider; import com.android.server.location.PassiveProvider; +import com.android.server.location.RemoteListenerHelper; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -121,6 +123,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; +import java.util.function.Consumer; +import java.util.function.Function; /** * The service class that manages LocationProviders and issues location @@ -225,11 +229,14 @@ public class LocationManagerService extends ILocationManager.Stub { private final ArraySet<String> mIgnoreSettingsPackageWhitelist = new ArraySet<>(); @GuardedBy("mLock") - private final ArrayMap<IBinder, CallerIdentity> mGnssMeasurementsListeners = new ArrayMap<>(); - + private final ArrayMap<IBinder, LinkedListener<IGnssMeasurementsListener>> + mGnssMeasurementsListeners = new ArrayMap<>(); @GuardedBy("mLock") - private final ArrayMap<IBinder, CallerIdentity> + private final ArrayMap<IBinder, LinkedListener<IGnssNavigationMessageListener>> mGnssNavigationMessageListeners = new ArrayMap<>(); + @GuardedBy("mLock") + private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>> + mGnssStatusListeners = new ArrayMap<>(); // current active user on the device - other users are denied location data private int mCurrentUserId = UserHandle.USER_SYSTEM; @@ -243,7 +250,7 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") private IBatchedLocationCallback mGnssBatchingCallback; @GuardedBy("mLock") - private LinkedCallback mGnssBatchingDeathCallback; + private LinkedListener<IBatchedLocationCallback> mGnssBatchingDeathCallback; @GuardedBy("mLock") private boolean mGnssBatchingInProgress = false; @@ -485,7 +492,7 @@ public class LocationManagerService extends ILocationManager.Stub { && record.mIsForegroundUid != foreground) { if (D) { Log.d(TAG, "request from uid " + uid + " is now " - + (foreground ? "foreground" : "background)")); + + foregroundAsString(foreground)); } record.updateForeground(foreground); @@ -499,44 +506,48 @@ public class LocationManagerService extends ILocationManager.Stub { applyRequirementsLocked(provider); } - for (Entry<IBinder, CallerIdentity> entry : mGnssMeasurementsListeners.entrySet()) { - CallerIdentity callerIdentity = entry.getValue(); - if (callerIdentity.mUid == uid) { - if (D) { - Log.d(TAG, "gnss measurements listener from uid " + uid - + " is now " + (foreground ? "foreground" : "background)")); - } - if (foreground || isThrottlingExemptLocked(entry.getValue())) { - mGnssMeasurementsProvider.addListener( - IGnssMeasurementsListener.Stub.asInterface(entry.getKey()), - callerIdentity); - } else { - mGnssMeasurementsProvider.removeListener( - IGnssMeasurementsListener.Stub.asInterface(entry.getKey())); - } + updateGnssDataProviderOnUidImportanceChangedLocked(mGnssMeasurementsListeners, + mGnssMeasurementsProvider, IGnssMeasurementsListener.Stub::asInterface, + uid, foreground); + + updateGnssDataProviderOnUidImportanceChangedLocked(mGnssNavigationMessageListeners, + mGnssNavigationMessageProvider, IGnssNavigationMessageListener.Stub::asInterface, + uid, foreground); + + updateGnssDataProviderOnUidImportanceChangedLocked(mGnssStatusListeners, + mGnssStatusProvider, IGnssStatusListener.Stub::asInterface, uid, foreground); + } + + @GuardedBy("mLock") + private <TListener extends IInterface> void updateGnssDataProviderOnUidImportanceChangedLocked( + ArrayMap<IBinder, ? extends LinkedListenerBase> gnssDataListeners, + RemoteListenerHelper<TListener> gnssDataProvider, + Function<IBinder, TListener> mapBinderToListener, int uid, boolean foreground) { + for (Entry<IBinder, ? extends LinkedListenerBase> entry : gnssDataListeners.entrySet()) { + LinkedListenerBase linkedListener = entry.getValue(); + CallerIdentity callerIdentity = linkedListener.mCallerIdentity; + if (callerIdentity.mUid != uid) { + continue; } - } - for (Entry<IBinder, CallerIdentity> entry : mGnssNavigationMessageListeners.entrySet()) { - CallerIdentity callerIdentity = entry.getValue(); - if (callerIdentity.mUid == uid) { - if (D) { - Log.d(TAG, "gnss navigation message listener from uid " - + uid + " is now " - + (foreground ? "foreground" : "background)")); - } - if (foreground || isThrottlingExemptLocked(entry.getValue())) { - mGnssNavigationMessageProvider.addListener( - IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()), - callerIdentity); - } else { - mGnssNavigationMessageProvider.removeListener( - IGnssNavigationMessageListener.Stub.asInterface(entry.getKey())); - } + if (D) { + Log.d(TAG, linkedListener.mListenerName + " from uid " + + uid + " is now " + foregroundAsString(foreground)); + } + + TListener listener = mapBinderToListener.apply(entry.getKey()); + if (foreground || isThrottlingExemptLocked(callerIdentity)) { + gnssDataProvider.addListener(listener, callerIdentity); + } else { + gnssDataProvider.removeListener(listener); } } } + private static String foregroundAsString(boolean foreground) { + return foreground ? "foreground" : "background"; + } + private static boolean isImportanceForeground(int importance) { return importance <= FOREGROUND_IMPORTANCE_CUTOFF; } @@ -1218,9 +1229,8 @@ public class LocationManagerService extends ILocationManager.Stub { * A wrapper class holding either an ILocationListener or a PendingIntent to receive * location updates. */ - private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished { + private final class Receiver extends LinkedListenerBase implements PendingIntent.OnFinished { private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000; - final CallerIdentity mCallerIdentity; private final int mAllowedResolutionLevel; // resolution level allowed to receiver private final ILocationListener mListener; @@ -1240,6 +1250,7 @@ public class LocationManagerService extends ILocationManager.Stub { private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) { + super(new CallerIdentity(uid, pid, packageName), "LocationListener"); mListener = listener; mPendingIntent = intent; if (listener != null) { @@ -1248,7 +1259,6 @@ public class LocationManagerService extends ILocationManager.Stub { mKey = intent; } mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid); - mCallerIdentity = new CallerIdentity(uid, pid, packageName); if (workSource != null && workSource.isEmpty()) { workSource = null; } @@ -1486,7 +1496,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void binderDied() { - if (D) Log.d(TAG, "Location listener died"); + if (D) Log.d(TAG, "Remote " + mListenerName + " died."); synchronized (mLock) { removeUpdatesLocked(this); @@ -1617,53 +1627,59 @@ public class LocationManagerService extends ILocationManager.Stub { return false; } + CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), + Binder.getCallingPid(), packageName); synchronized (mLock) { mGnssBatchingCallback = callback; - mGnssBatchingDeathCallback = new LinkedCallback(callback); - try { - callback.asBinder().linkToDeath(mGnssBatchingDeathCallback, 0 /* flags */); - } catch (RemoteException e) { - // if the remote process registering the listener is already dead, just swallow the - // exception and return - Log.e(TAG, "Remote listener already died.", e); + mGnssBatchingDeathCallback = new LinkedListener<>(callback, + "BatchedLocationCallback", callerIdentity, + (IBatchedLocationCallback listener) -> { + stopGnssBatch(); + removeGnssBatchingCallback(); + }); + if (!linkToListenerDeathNotificationLocked(callback.asBinder(), + mGnssBatchingDeathCallback)) { return false; } - return true; } } - private class LinkedCallback implements IBinder.DeathRecipient { - private final IBatchedLocationCallback mCallback; + private abstract static class LinkedListenerBase implements IBinder.DeathRecipient { + protected final CallerIdentity mCallerIdentity; + protected final String mListenerName; - private LinkedCallback(@NonNull IBatchedLocationCallback callback) { - mCallback = callback; + private LinkedListenerBase(@NonNull CallerIdentity callerIdentity, + @NonNull String listenerName) { + mCallerIdentity = callerIdentity; + mListenerName = listenerName; } + } + + private static class LinkedListener<TListener> extends LinkedListenerBase { + private final TListener mListener; + private final Consumer<TListener> mBinderDeathCallback; - @NonNull - public IBatchedLocationCallback getUnderlyingListener() { - return mCallback; + private LinkedListener(@NonNull TListener listener, String listenerName, + @NonNull CallerIdentity callerIdentity, + @NonNull Consumer<TListener> binderDeathCallback) { + super(callerIdentity, listenerName); + mListener = listener; + mBinderDeathCallback = binderDeathCallback; } @Override public void binderDied() { - Log.d(TAG, "Remote Batching Callback died: " + mCallback); - stopGnssBatch(); - removeGnssBatchingCallback(); + if (D) Log.d(TAG, "Remote " + mListenerName + " died."); + mBinderDeathCallback.accept(mListener); } } @Override public void removeGnssBatchingCallback() { synchronized (mLock) { - try { - mGnssBatchingCallback.asBinder().unlinkToDeath(mGnssBatchingDeathCallback, - 0 /* flags */); - } catch (NoSuchElementException e) { - // if the death callback isn't connected (it should be...), log error, swallow the - // exception and return - Log.e(TAG, "Couldn't unlink death callback.", e); - } + unlinkFromListenerDeathNotificationLocked(mGnssBatchingCallback.asBinder(), + mGnssBatchingDeathCallback); mGnssBatchingCallback = null; mGnssBatchingDeathCallback = null; } @@ -2264,10 +2280,8 @@ public class LocationManagerService extends ILocationManager.Stub { if (receiver == null) { receiver = new Receiver(listener, null, pid, uid, packageName, workSource, hideFromAppOps); - try { - receiver.getListener().asBinder().linkToDeath(receiver, 0); - } catch (RemoteException e) { - Slog.e(TAG, "linkToDeath failed:", e); + if (!linkToListenerDeathNotificationLocked(receiver.getListener().asBinder(), + receiver)) { return null; } mReceivers.put(binder, receiver); @@ -2482,7 +2496,8 @@ public class LocationManagerService extends ILocationManager.Stub { if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver))); if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) { - receiver.getListener().asBinder().unlinkToDeath(receiver, 0); + unlinkFromListenerDeathNotificationLocked(receiver.getListener().asBinder(), + receiver); receiver.clearPendingBroadcastsLocked(); } @@ -2694,18 +2709,52 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName) { + public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) { if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) { return false; } - return mGnssStatusProvider.addListener(callback, new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName)); + CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), + Binder.getCallingPid(), packageName); + LinkedListener<IGnssStatusListener> linkedListener = new LinkedListener<>(listener, + "GnssStatusListener", callerIdentity, this::unregisterGnssStatusCallback); + IBinder binder = listener.asBinder(); + synchronized (mLock) { + if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { + return false; + } + + mGnssStatusListeners.put(binder, linkedListener); + long identity = Binder.clearCallingIdentity(); + try { + if (isThrottlingExemptLocked(callerIdentity) + || isImportanceForeground( + mActivityManager.getPackageImportance(packageName))) { + mGnssStatusProvider.addListener(listener, callerIdentity); + } + return true; + } finally { + Binder.restoreCallingIdentity(identity); + } + } } @Override - public void unregisterGnssStatusCallback(IGnssStatusListener callback) { - mGnssStatusProvider.removeListener(callback); + public void unregisterGnssStatusCallback(IGnssStatusListener listener) { + if (mGnssStatusProvider == null) { + return; + } + + IBinder binder = listener.asBinder(); + synchronized (mLock) { + LinkedListener<IGnssStatusListener> linkedListener = + mGnssStatusListeners.remove(binder); + if (linkedListener == null) { + return; + } + unlinkFromListenerDeathNotificationLocked(binder, linkedListener); + mGnssStatusProvider.removeListener(listener); + } } @Override @@ -2715,22 +2764,75 @@ public class LocationManagerService extends ILocationManager.Stub { return false; } + CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), + Binder.getCallingPid(), packageName); + LinkedListener<IGnssMeasurementsListener> linkedListener = new LinkedListener<>(listener, + "GnssMeasurementsListener", callerIdentity, this::removeGnssMeasurementsListener); + IBinder binder = listener.asBinder(); synchronized (mLock) { - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName); - mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity); + if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { + return false; + } + + mGnssMeasurementsListeners.put(binder, linkedListener); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) || isImportanceForeground( mActivityManager.getPackageImportance(packageName))) { - return mGnssMeasurementsProvider.addListener(listener, callerIdentity); + mGnssMeasurementsProvider.addListener(listener, callerIdentity); } + return true; } finally { Binder.restoreCallingIdentity(identity); } + } + } + + @Override + public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { + if (mGnssMeasurementsProvider == null) { + return; + } + + IBinder binder = listener.asBinder(); + synchronized (mLock) { + LinkedListener<IGnssMeasurementsListener> linkedListener = + mGnssMeasurementsListeners.remove(binder); + if (linkedListener == null) { + return; + } + unlinkFromListenerDeathNotificationLocked(binder, linkedListener); + mGnssMeasurementsProvider.removeListener(listener); + } + } + + private boolean linkToListenerDeathNotificationLocked(IBinder binder, + LinkedListenerBase linkedListener) { + try { + binder.linkToDeath(linkedListener, 0 /* flags */); + return true; + } catch (RemoteException e) { + // if the remote process registering the listener is already dead, just swallow the + // exception and return + Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", + e); + return false; + } + } + + private boolean unlinkFromListenerDeathNotificationLocked(IBinder binder, + LinkedListenerBase linkedListener) { + try { + binder.unlinkToDeath(linkedListener, 0 /* flags */); return true; + } catch (NoSuchElementException e) { + // if the death callback isn't connected (it should be...), log error, + // swallow the exception and return + Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", + e); + return false; } } @@ -2759,52 +2861,53 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { - if (mGnssMeasurementsProvider == null) { - return; - } - - synchronized (mLock) { - mGnssMeasurementsListeners.remove(listener.asBinder()); - mGnssMeasurementsProvider.removeListener(listener); - } - } - - @Override public boolean addGnssNavigationMessageListener( - IGnssNavigationMessageListener listener, - String packageName) { + IGnssNavigationMessageListener listener, String packageName) { if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) { return false; } + CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), + Binder.getCallingPid(), packageName); + LinkedListener<IGnssNavigationMessageListener> linkedListener = + new LinkedListener<>(listener, "GnssNavigationMessageListener", callerIdentity, + this::removeGnssNavigationMessageListener); + IBinder binder = listener.asBinder(); synchronized (mLock) { - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName); + if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { + return false; + } - mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity); + mGnssNavigationMessageListeners.put(binder, linkedListener); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) || isImportanceForeground( mActivityManager.getPackageImportance(packageName))) { - return mGnssNavigationMessageProvider.addListener(listener, callerIdentity); + mGnssNavigationMessageProvider.addListener(listener, callerIdentity); } + return true; } finally { Binder.restoreCallingIdentity(identity); } - - return true; } } @Override public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { - if (mGnssNavigationMessageProvider != null) { - synchronized (mLock) { - mGnssNavigationMessageListeners.remove(listener.asBinder()); - mGnssNavigationMessageProvider.removeListener(listener); + if (mGnssNavigationMessageProvider == null) { + return; + } + + IBinder binder = listener.asBinder(); + synchronized (mLock) { + LinkedListener<IGnssNavigationMessageListener> linkedListener = + mGnssNavigationMessageListeners.remove(binder); + if (linkedListener == null) { + return; } + unlinkFromListenerDeathNotificationLocked(binder, linkedListener); + mGnssNavigationMessageProvider.removeListener(listener); } } @@ -3368,18 +3471,14 @@ public class LocationManagerService extends ILocationManager.Stub { pw.println(" " + record); } } + pw.println(" Active GnssMeasurement Listeners:"); - for (CallerIdentity callerIdentity : mGnssMeasurementsListeners.values()) { - pw.println(" " + callerIdentity.mPid + " " + callerIdentity.mUid + " " - + callerIdentity.mPackageName + ": " - + isThrottlingExemptLocked(callerIdentity)); - } + dumpGnssDataListenersLocked(pw, mGnssMeasurementsListeners); pw.println(" Active GnssNavigationMessage Listeners:"); - for (CallerIdentity callerIdentity : mGnssNavigationMessageListeners.values()) { - pw.println(" " + callerIdentity.mPid + " " + callerIdentity.mUid + " " - + callerIdentity.mPackageName + ": " - + isThrottlingExemptLocked(callerIdentity)); - } + dumpGnssDataListenersLocked(pw, mGnssNavigationMessageListeners); + pw.println(" Active GnssStatus Listeners:"); + dumpGnssDataListenersLocked(pw, mGnssStatusListeners); + pw.println(" Historical Records by Provider:"); for (Map.Entry<PackageProviderKey, PackageStatistics> entry : mRequestStatistics.statistics.entrySet()) { @@ -3432,4 +3531,15 @@ public class LocationManagerService extends ILocationManager.Stub { } } } + + @GuardedBy("mLock") + private void dumpGnssDataListenersLocked(PrintWriter pw, + ArrayMap<IBinder, ? extends LinkedListenerBase> gnssDataListeners) { + for (LinkedListenerBase listener : gnssDataListeners.values()) { + CallerIdentity callerIdentity = listener.mCallerIdentity; + pw.println(" " + callerIdentity.mPid + " " + callerIdentity.mUid + " " + + callerIdentity.mPackageName + ": " + + isThrottlingExemptLocked(callerIdentity)); + } + } } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index e7d7434b5dc8..5da281a5ebc3 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -860,7 +860,7 @@ class StorageManagerService extends IStorageManager.Stub } else if (remote == 1) { res = true; } else { - res = false; + res = true; } Slog.d(TAG, "Isolated storage local flag " + local + " and remote flag " @@ -1533,7 +1533,7 @@ class StorageManagerService extends IStorageManager.Stub // Snapshot feature flag used for this boot SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString( - SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false))); + SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true))); mContext = context; mResolver = mContext.getContentResolver(); diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java index 3939bee52aa2..9bbc3158757c 100644 --- a/services/core/java/com/android/server/WiredAccessoryManager.java +++ b/services/core/java/com/android/server/WiredAccessoryManager.java @@ -23,6 +23,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.UEventObserver; +import android.util.Pair; import android.util.Slog; import android.media.AudioManager; import android.util.Log; @@ -31,6 +32,7 @@ import android.view.InputDevice; import com.android.internal.R; import com.android.server.input.InputManagerService; import com.android.server.input.InputManagerService.WiredAccessoryCallbacks; + import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT; import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT; import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT; @@ -41,6 +43,7 @@ import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT_BIT import java.io.File; import java.io.FileReader; import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -52,7 +55,7 @@ import java.util.Locale; */ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private static final String TAG = WiredAccessoryManager.class.getSimpleName(); - private static final boolean LOG = true; + private static final boolean LOG = false; private static final int BIT_HEADSET = (1 << 0); private static final int BIT_HEADSET_NO_MIC = (1 << 1); @@ -60,9 +63,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private static final int BIT_USB_HEADSET_DGTL = (1 << 3); private static final int BIT_HDMI_AUDIO = (1 << 4); private static final int BIT_LINEOUT = (1 << 5); - private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC| - BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL| - BIT_HDMI_AUDIO|BIT_LINEOUT); + private static final int SUPPORTED_HEADSETS = (BIT_HEADSET | BIT_HEADSET_NO_MIC | + BIT_USB_HEADSET_ANLG | BIT_USB_HEADSET_DGTL | + BIT_HDMI_AUDIO | BIT_LINEOUT); private static final String NAME_H2W = "h2w"; private static final String NAME_USB_AUDIO = "usb_audio"; @@ -82,30 +85,34 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private int mSwitchValues; private final WiredAccessoryObserver mObserver; + private final WiredAccessoryExtconObserver mExtconObserver; private final InputManagerService mInputManager; private final boolean mUseDevInputEventForAudioJack; public WiredAccessoryManager(Context context, InputManagerService inputManager) { - PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryManager"); mWakeLock.setReferenceCounted(false); - mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mInputManager = inputManager; mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); + mExtconObserver = new WiredAccessoryExtconObserver(); mObserver = new WiredAccessoryObserver(); } private void onSystemReady() { if (mUseDevInputEventForAudioJack) { int switchValues = 0; - if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) == 1) { + if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) + == 1) { switchValues |= SW_HEADPHONE_INSERT_BIT; } - if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) == 1) { + if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) + == 1) { switchValues |= SW_MICROPHONE_INSERT_BIT; } if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_LINEOUT_INSERT) == 1) { @@ -115,20 +122,31 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT); } - mObserver.init(); + + if (ExtconUEventObserver.extconExists()) { + if (mUseDevInputEventForAudioJack) { + Log.w(TAG, "Both input event and extcon are used for audio jack," + + " please just choose one."); + } + mExtconObserver.init(); + } else { + mObserver.init(); + } } @Override public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) { - if (LOG) Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos - + " bits=" + switchCodeToString(switchValues, switchMask) - + " mask=" + Integer.toHexString(switchMask)); + if (LOG) { + Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos + + " bits=" + switchCodeToString(switchValues, switchMask) + + " mask=" + Integer.toHexString(switchMask)); + } synchronized (mLock) { int headset; mSwitchValues = (mSwitchValues & ~switchMask) | switchValues; switch (mSwitchValues & - (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) { + (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) { case 0: headset = 0; break; @@ -155,7 +173,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } updateLocked(NAME_H2W, - (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset); + (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset); } } @@ -175,7 +193,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { * results in support for the last one plugged in. Similarly, unplugging either is seen as * unplugging all. * - * @param newName One of the NAME_xxx variables defined above. + * @param newName One of the NAME_xxx variables defined above. * @param newState 0 or one of the BIT_xxx variables defined above. */ private void updateLocked(String newName, int newState) { @@ -186,10 +204,12 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT); boolean h2wStateChange = true; boolean usbStateChange = true; - if (LOG) Slog.v(TAG, "newName=" + newName - + " newState=" + newState - + " headsetState=" + headsetState - + " prev headsetState=" + mHeadsetState); + if (LOG) { + Slog.v(TAG, "newName=" + newName + + " newState=" + newState + + " headsetState=" + headsetState + + " prev headsetState=" + mHeadsetState); + } if (mHeadsetState == headsetState) { Log.e(TAG, "No state change."); @@ -229,7 +249,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { public void handleMessage(Message msg) { switch (msg.what) { case MSG_NEW_DEVICE_STATE: - setDevicesState(msg.arg1, msg.arg2, (String)msg.obj); + setDevicesState(msg.arg1, msg.arg2, (String) msg.obj); mWakeLock.release(); break; case MSG_SYSTEM_READY: @@ -269,9 +289,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { if (headset == BIT_HEADSET) { outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET; inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET; - } else if (headset == BIT_HEADSET_NO_MIC){ + } else if (headset == BIT_HEADSET_NO_MIC) { outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE; - } else if (headset == BIT_LINEOUT){ + } else if (headset == BIT_LINEOUT) { outDevice = AudioManager.DEVICE_OUT_LINE; } else if (headset == BIT_USB_HEADSET_ANLG) { outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET; @@ -280,7 +300,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } else if (headset == BIT_HDMI_AUDIO) { outDevice = AudioManager.DEVICE_OUT_HDMI; } else { - Slog.e(TAG, "setDeviceState() invalid headset type: "+headset); + Slog.e(TAG, "setDeviceState() invalid headset type: " + headset); return; } @@ -290,10 +310,10 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } if (outDevice != 0) { - mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName); + mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName); } if (inDevice != 0) { - mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName); + mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName); } } } @@ -340,7 +360,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { " not found while attempting to determine initial switch state"); } catch (Exception e) { Slog.e(TAG, "Error while attempting to determine initial switch state for " - + uei.getDevName() , e); + + uei.getDevName(), e); } } } @@ -350,7 +370,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { // observe three UEVENTs for (int i = 0; i < mUEventInfo.size(); ++i) { UEventInfo uei = mUEventInfo.get(i); - startObserving("DEVPATH="+uei.getDevPath()); + startObserving("DEVPATH=" + uei.getDevPath()); } } @@ -438,7 +458,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { mStateNbits = stateNbits; } - public String getDevName() { return mDevName; } + public String getDevName() { + return mDevName; + } public String getDevPath() { return String.format(Locale.US, "/devices/virtual/switch/%s", mDevName); @@ -456,11 +478,84 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { public int computeNewHeadsetState(int headsetState, int switchState) { int preserveMask = ~(mState1Bits | mState2Bits | mStateNbits); int setBits = ((switchState == 1) ? mState1Bits : - ((switchState == 2) ? mState2Bits : - ((switchState == mStateNbits) ? mStateNbits : 0))); + ((switchState == 2) ? mState2Bits : + ((switchState == mStateNbits) ? mStateNbits : 0))); return ((headsetState & preserveMask) | setBits); } } } + + private class WiredAccessoryExtconObserver extends ExtconStateObserver<Pair<Integer, Integer>> { + private final List<ExtconInfo> mExtconInfos; + + WiredAccessoryExtconObserver() { + mExtconInfos = ExtconInfo.getExtconInfos(".*audio.*"); + + } + + private void init() { + for (ExtconInfo extconInfo : mExtconInfos) { + Pair<Integer, Integer> state = null; + try { + state = parseStateFromFile(extconInfo); + } catch (FileNotFoundException e) { + Slog.w(TAG, extconInfo.getStatePath() + + " not found while attempting to determine initial state", e); + } catch (IOException e) { + Slog.e( + TAG, + "Error reading " + extconInfo.getStatePath() + + " while attempting to determine initial state", + e); + } + if (state != null) { + updateState(extconInfo, extconInfo.getName(), state); + } + if (LOG) Slog.d(TAG, "observing " + extconInfo.getName()); + startObserving(extconInfo); + } + + } + + @Override + public Pair<Integer, Integer> parseState(ExtconInfo extconInfo, String status) { + if (LOG) Slog.v(TAG, "status " + status); + int []maskAndState = {0,0}; + // extcon event state changes from kernel4.9 + // new state will be like STATE=MICROPHONE=1\nHEADPHONE=0 + updateBit(maskAndState, BIT_HEADSET_NO_MIC, status, "HEADPHONE") ; + updateBit(maskAndState, BIT_HEADSET, status,"MICROPHONE") ; + updateBit(maskAndState, BIT_HDMI_AUDIO, status,"HDMI") ; + updateBit(maskAndState, BIT_LINEOUT, status,"LINE-OUT") ; + if (LOG) Slog.v(TAG, "mask " + maskAndState[0] + " state " + maskAndState[1]); + return Pair.create(maskAndState[0],maskAndState[1]); + } + + @Override + public void updateState(ExtconInfo extconInfo, String name, + Pair<Integer, Integer> maskAndState) { + synchronized (mLock) { + int mask = maskAndState.first; + int state = maskAndState.second; + updateLocked(name, mHeadsetState | (mask & state) & ~(mask & ~state)); + return; + } + } + } + + /** + * Updates the mask bit at {@code position} to 1 and the state bit at {@code position} to true + * if {@code name=1} or false if {}@code name=0} is contained in {@code state}. + */ + private static void updateBit(int[] maskAndState, int position, String state, String name) { + maskAndState[0] |= position; + if (state.contains(name + "=1")) { + maskAndState[0] |= position; + maskAndState[1] |= position; + } else if (state.contains(name + "=0")) { + maskAndState[0] |= position; + maskAndState[1] &= ~position; + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 60a45bfe04bb..c4a9db6b7262 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6630,8 +6630,7 @@ public class ActivityManagerService extends IActivityManager.Stub String msg; if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) { - throw new SecurityException("Content provider lookup " - + cpr.name.flattenToShortString() + throw new SecurityException("Content provider lookup " + name + " failed: association not allowed with package " + msg); } checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission"); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 01946247bd12..8ffb67a1405c 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -65,6 +65,8 @@ final class CoreSettingsObserver extends ContentObserver { Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class); sGlobalSettingToTypeMap.put( Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, String.class); + sGlobalSettingToTypeMap.put( + Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class); sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class); @@ -76,6 +78,7 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_OUT_APPS, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLIST, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_WHITELIST, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLISTS, String.class); // add other global settings here... } diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index a14fd17209e8..19bdc0969e6d 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -96,6 +96,7 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; @@ -180,6 +181,7 @@ public class Tethering extends BaseNetworkObserver { // into a single coherent structure. private final HashSet<IpServer> mForwardedDownstreams; private final VersionedBroadcastListener mCarrierConfigChange; + private final VersionedBroadcastListener mDefaultSubscriptionChange; private final TetheringDependencies mDeps; private final EntitlementManager mEntitlementMgr; @@ -232,6 +234,15 @@ public class Tethering extends BaseNetworkObserver { mEntitlementMgr.reevaluateSimCardProvisioning(); }); + filter = new IntentFilter(); + filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); + mDefaultSubscriptionChange = new VersionedBroadcastListener( + "DefaultSubscriptionChangeListener", mContext, smHandler, filter, + (Intent ignored) -> { + mLog.log("OBSERVED default data subscription change"); + updateConfiguration(); + mEntitlementMgr.reevaluateSimCardProvisioning(); + }); mStateReceiver = new StateReceiver(); // Load tethering configuration. @@ -242,6 +253,7 @@ public class Tethering extends BaseNetworkObserver { private void startStateMachineUpdaters() { mCarrierConfigChange.startListening(); + mDefaultSubscriptionChange.startListening(); final Handler handler = mTetherMasterSM.getHandler(); IntentFilter filter = new IntentFilter(); @@ -270,7 +282,8 @@ public class Tethering extends BaseNetworkObserver { // NOTE: This is always invoked on the mLooper thread. private void updateConfiguration() { - mConfig = new TetheringConfiguration(mContext, mLog); + final int subId = mDeps.getDefaultDataSubscriptionId(); + mConfig = new TetheringConfiguration(mContext, mLog, subId); mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired); mEntitlementMgr.updateConfiguration(mConfig); } diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java index 1e6bb04858a1..8a46ff18979f 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -26,8 +26,8 @@ import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; import static com.android.internal.R.array.config_mobile_hotspot_provision_app; import static com.android.internal.R.array.config_tether_bluetooth_regexs; import static com.android.internal.R.array.config_tether_dhcp_range; -import static com.android.internal.R.array.config_tether_usb_regexs; import static com.android.internal.R.array.config_tether_upstream_types; +import static com.android.internal.R.array.config_tether_usb_regexs; import static com.android.internal.R.array.config_tether_wifi_regexs; import static com.android.internal.R.bool.config_tether_upstream_automatic; import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui; @@ -38,6 +38,7 @@ import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.util.SharedLog; import android.provider.Settings; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -100,29 +101,34 @@ public class TetheringConfiguration { public final String[] provisioningApp; public final String provisioningAppNoUi; - public TetheringConfiguration(Context ctx, SharedLog log) { + public final int subId; + + public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); - tetherableUsbRegexs = getResourceStringArray(ctx, config_tether_usb_regexs); + subId = id; + Resources res = getResources(ctx, subId); + + tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs); // TODO: Evaluate deleting this altogether now that Wi-Fi always passes // us an interface name. Careful consideration needs to be given to // implications for Settings and for provisioning checks. - tetherableWifiRegexs = getResourceStringArray(ctx, config_tether_wifi_regexs); - tetherableBluetoothRegexs = getResourceStringArray(ctx, config_tether_bluetooth_regexs); + tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs); + tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs); dunCheck = checkDunRequired(ctx); configLog.log("DUN check returned: " + dunCheckString(dunCheck)); - chooseUpstreamAutomatically = getResourceBoolean(ctx, config_tether_upstream_automatic); - preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck); + chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic); + preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, dunCheck); isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN); - legacyDhcpRanges = getLegacyDhcpRanges(ctx); + legacyDhcpRanges = getLegacyDhcpRanges(res); defaultIPv4DNS = copy(DEFAULT_IPV4_DNS); enableLegacyDhcpServer = getEnableLegacyDhcpServer(ctx); - provisioningApp = getResourceStringArray(ctx, config_mobile_hotspot_provision_app); - provisioningAppNoUi = getProvisioningAppNoUi(ctx); + provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app); + provisioningAppNoUi = getProvisioningAppNoUi(res); configLog.log(toString()); } @@ -144,6 +150,9 @@ public class TetheringConfiguration { } public void dump(PrintWriter pw) { + pw.print("subId: "); + pw.println(subId); + dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs); dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs); dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs); @@ -169,6 +178,7 @@ public class TetheringConfiguration { public String toString() { final StringJoiner sj = new StringJoiner(" "); + sj.add(String.format("subId:%d", subId)); sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs))); sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs))); sj.add(String.format("tetherableBluetoothRegexs:%s", @@ -235,8 +245,8 @@ public class TetheringConfiguration { } } - private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) { - final int ifaceTypes[] = ctx.getResources().getIntArray(config_tether_upstream_types); + private static Collection<Integer> getUpstreamIfaceTypes(Resources res, int dunCheck) { + final int[] ifaceTypes = res.getIntArray(config_tether_upstream_types); final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); for (int i : ifaceTypes) { switch (i) { @@ -286,33 +296,33 @@ public class TetheringConfiguration { return false; } - private static String[] getLegacyDhcpRanges(Context ctx) { - final String[] fromResource = getResourceStringArray(ctx, config_tether_dhcp_range); + private static String[] getLegacyDhcpRanges(Resources res) { + final String[] fromResource = getResourceStringArray(res, config_tether_dhcp_range); if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) { return fromResource; } return copy(LEGACY_DHCP_DEFAULT_RANGE); } - private static String getProvisioningAppNoUi(Context ctx) { + private static String getProvisioningAppNoUi(Resources res) { try { - return ctx.getResources().getString(config_mobile_hotspot_provision_app_no_ui); + return res.getString(config_mobile_hotspot_provision_app_no_ui); } catch (Resources.NotFoundException e) { return ""; } } - private static boolean getResourceBoolean(Context ctx, int resId) { + private static boolean getResourceBoolean(Resources res, int resId) { try { - return ctx.getResources().getBoolean(resId); + return res.getBoolean(resId); } catch (Resources.NotFoundException e404) { return false; } } - private static String[] getResourceStringArray(Context ctx, int resId) { + private static String[] getResourceStringArray(Resources res, int resId) { try { - final String[] strArray = ctx.getResources().getStringArray(resId); + final String[] strArray = res.getStringArray(resId); return (strArray != null) ? strArray : EMPTY_STRING_ARRAY; } catch (Resources.NotFoundException e404) { return EMPTY_STRING_ARRAY; @@ -325,6 +335,19 @@ public class TetheringConfiguration { return intVal != 0; } + private Resources getResources(Context ctx, int subId) { + if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + return getResourcesForSubIdWrapper(ctx, subId); + } else { + return ctx.getResources(); + } + } + + @VisibleForTesting + protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { + return SubscriptionManager.getResourcesForSubId(ctx, subId); + } + private static String[] copy(String[] strarray) { return Arrays.copyOf(strarray, strarray.length); } diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java index 6d6f81eb98e6..3fddac111ec5 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -21,6 +21,7 @@ import android.net.NetworkRequest; import android.net.ip.IpServer; import android.net.util.SharedLog; import android.os.Handler; +import android.telephony.SubscriptionManager; import com.android.internal.util.StateMachine; import com.android.server.connectivity.MockableSystemProperties; @@ -85,4 +86,11 @@ public class TetheringDependencies { SharedLog log, MockableSystemProperties systemProperties) { return new EntitlementManager(ctx, target, log, systemProperties); } + + /** + * Get default data subscription id to build TetheringConfiguration. + */ + public int getDefaultDataSubscriptionId() { + return SubscriptionManager.getDefaultDataSubscriptionId(); + } } diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java index 8171ad67f2a2..45f169ca0b6f 100644 --- a/services/core/java/com/android/server/display/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/ColorDisplayService.java @@ -558,14 +558,16 @@ public final class ColorDisplayService extends SystemService { if (setting != null) { switch (setting) { case Secure.NIGHT_DISPLAY_ACTIVATED: - final boolean activated = isNightDisplayActivatedSetting(); + final boolean activated = mNightDisplayTintController + .isActivatedSetting(); if (mNightDisplayTintController.isActivatedStateNotSet() || mNightDisplayTintController.isActivated() != activated) { - mNightDisplayTintController.onActivated(activated); + mNightDisplayTintController.setActivated(activated); } break; case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE: - final int temperature = getNightDisplayColorTemperatureSetting(); + final int temperature = mNightDisplayTintController + .getColorTemperatureSetting(); if (mNightDisplayTintController.getColorTemperature() != temperature) { mNightDisplayTintController @@ -641,14 +643,16 @@ public final class ColorDisplayService extends SystemService { // Prepare the night display color transformation matrix. mNightDisplayTintController .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix()); - mNightDisplayTintController.setMatrix(getNightDisplayColorTemperatureSetting()); + mNightDisplayTintController + .setMatrix(mNightDisplayTintController.getColorTemperatureSetting()); // Initialize the current auto mode. onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal()); // Force the initialization of the current saved activation state. if (mNightDisplayTintController.isActivatedStateNotSet()) { - mNightDisplayTintController.onActivated(isNightDisplayActivatedSetting()); + mNightDisplayTintController + .setActivated(mNightDisplayTintController.isActivatedSetting()); } } @@ -728,7 +732,8 @@ public final class ColorDisplayService extends SystemService { if (mNightDisplayTintController.isAvailable(getContext())) { mNightDisplayTintController .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode)); - mNightDisplayTintController.setMatrix(getNightDisplayColorTemperatureSetting()); + mNightDisplayTintController + .setMatrix(mNightDisplayTintController.getColorTemperatureSetting()); } updateDisplayWhiteBalanceStatus(); @@ -1046,8 +1051,7 @@ public final class ColorDisplayService extends SystemService { * * See {@link com.android.server.display.DisplayTransformManager} */ - private @ColorMode - int getCurrentColorModeFromSystemProperties() { + private @ColorMode int getCurrentColorModeFromSystemProperties() { final int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0); if (displayColorSetting == 0) { return "1.0".equals(SystemProperties.get("persist.sys.sf.color_saturation")) @@ -1104,33 +1108,6 @@ public final class ColorDisplayService extends SystemService { pw.println("Color mode: " + getColorModeInternal()); } - private boolean isNightDisplayActivatedSetting() { - return Secure.getIntForUser(getContext().getContentResolver(), - Secure.NIGHT_DISPLAY_ACTIVATED, 0, mCurrentUser) == 1; - } - - private int getNightDisplayColorTemperatureSetting() { - return clampNightDisplayColorTemperature(Secure.getIntForUser( - getContext().getContentResolver(), Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, NOT_SET, - mCurrentUser)); - } - - private int clampNightDisplayColorTemperature(int colorTemperature) { - if (colorTemperature == NOT_SET) { - colorTemperature = getContext().getResources().getInteger( - R.integer.config_nightDisplayColorTemperatureDefault); - } - final int minimumTemperature = ColorDisplayManager.getMinimumColorTemperature(getContext()); - final int maximumTemperature = ColorDisplayManager.getMaximumColorTemperature(getContext()); - if (colorTemperature < minimumTemperature) { - colorTemperature = minimumTemperature; - } else if (colorTemperature > maximumTemperature) { - colorTemperature = maximumTemperature; - } - - return colorTemperature; - } - private abstract class NightDisplayAutoMode { public abstract void onActivated(boolean activated); @@ -1177,7 +1154,7 @@ public final class ColorDisplayService extends SystemService { // Maintain the existing activated state if within the current period. if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start) && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) { - activate = isNightDisplayActivatedSetting(); + activate = mNightDisplayTintController.isActivatedSetting(); } } @@ -1273,7 +1250,7 @@ public final class ColorDisplayService extends SystemService { // Maintain the existing activated state if within the current period. if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise) ^ mLastActivatedTime.isBefore(sunset))) { - activate = isNightDisplayActivatedSetting(); + activate = mNightDisplayTintController.isActivatedSetting(); } } @@ -1472,9 +1449,11 @@ public final class ColorDisplayService extends SystemService { if (isActivatedStateNotSet() || activationStateChanged) { super.setActivated(activated); - Secure.putIntForUser(getContext().getContentResolver(), - Secure.NIGHT_DISPLAY_ACTIVATED, - activated ? 1 : 0, mCurrentUser); + if (isActivatedSetting() != activated) { + Secure.putIntForUser(getContext().getContentResolver(), + Secure.NIGHT_DISPLAY_ACTIVATED, + activated ? 1 : 0, mCurrentUser); + } onActivated(activated); } } @@ -1492,7 +1471,7 @@ public final class ColorDisplayService extends SystemService { return mIsAvailable; } - void onActivated(boolean activated) { + private void onActivated(boolean activated) { Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display"); if (mNightDisplayAutoMode != null) { mNightDisplayAutoMode.onActivated(activated); @@ -1507,7 +1486,7 @@ public final class ColorDisplayService extends SystemService { int getColorTemperature() { return mColorTemp != null ? clampNightDisplayColorTemperature(mColorTemp) - : getNightDisplayColorTemperatureSetting(); + : getColorTemperatureSetting(); } boolean setColorTemperature(int temperature) { @@ -1522,6 +1501,36 @@ public final class ColorDisplayService extends SystemService { setMatrix(temperature); mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE); } + + boolean isActivatedSetting() { + return Secure.getIntForUser(getContext().getContentResolver(), + Secure.NIGHT_DISPLAY_ACTIVATED, 0, mCurrentUser) == 1; + } + + int getColorTemperatureSetting() { + return clampNightDisplayColorTemperature(Secure.getIntForUser( + getContext().getContentResolver(), Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, + NOT_SET, + mCurrentUser)); + } + + private int clampNightDisplayColorTemperature(int colorTemperature) { + if (colorTemperature == NOT_SET) { + colorTemperature = getContext().getResources().getInteger( + R.integer.config_nightDisplayColorTemperatureDefault); + } + final int minimumTemperature = ColorDisplayManager + .getMinimumColorTemperature(getContext()); + final int maximumTemperature = ColorDisplayManager + .getMaximumColorTemperature(getContext()); + if (colorTemperature < minimumTemperature) { + colorTemperature = minimumTemperature; + } else if (colorTemperature > maximumTemperature) { + colorTemperature = maximumTemperature; + } + + return colorTemperature; + } } /** diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java index 7ae00af626c8..b9aa34e89216 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java @@ -18,6 +18,7 @@ package com.android.server.display.whitebalance; import android.annotation.NonNull; import android.util.Slog; +import android.util.Spline; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; @@ -75,6 +76,9 @@ public class DisplayWhiteBalanceController implements // Override the ambient color temperature for debugging purposes. private float mAmbientColorTemperatureOverride; + // A piecewise linear relationship between ambient and display color temperatures + private Spline.LinearSpline mAmbientToDisplayTemperatureSpline; + /** * @param brightnessSensor * The sensor used to detect changes in the ambient brightness. @@ -109,7 +113,8 @@ public class DisplayWhiteBalanceController implements @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor, @NonNull AmbientFilter colorTemperatureFilter, @NonNull DisplayWhiteBalanceThrottler throttler, - float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature) { + float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature, + float[] ambientTemperatures, float[] displayTemperatures) { validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, throttler); mLoggingEnabled = false; @@ -127,6 +132,14 @@ public class DisplayWhiteBalanceController implements mLastAmbientColorTemperature = -1.0f; mAmbientColorTemperatureHistory = new History(HISTORY_SIZE); mAmbientColorTemperatureOverride = -1.0f; + + try { + mAmbientToDisplayTemperatureSpline = new Spline.LinearSpline(ambientTemperatures, + displayTemperatures); + } catch (Exception e) { + mAmbientToDisplayTemperatureSpline = null; + } + mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class); } @@ -227,6 +240,9 @@ public class DisplayWhiteBalanceController implements writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature); writer.println(" mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory); writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride); + writer.println(" mAmbientToDisplayTemperatureSpline=" + + (mAmbientToDisplayTemperatureSpline == null ? "unused" : + mAmbientToDisplayTemperatureSpline)); } @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks @@ -250,6 +266,11 @@ public class DisplayWhiteBalanceController implements final long time = System.currentTimeMillis(); float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time); + if (mAmbientToDisplayTemperatureSpline != null) { + ambientColorTemperature = + mAmbientToDisplayTemperatureSpline.interpolate(ambientColorTemperature); + } + final float ambientBrightness = mBrightnessFilter.getEstimate(time); if (ambientBrightness < mLowLightAmbientBrightnessThreshold) { if (mLoggingEnabled) { diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java index fd78ddbda9c8..56f4ca339eb3 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java @@ -67,9 +67,14 @@ public class DisplayWhiteBalanceFactory { final float lowLightAmbientColorTemperature = getFloat(resources, com.android.internal.R.dimen .config_displayWhiteBalanceLowLightAmbientColorTemperature); + final float[] ambientTemperatures = getFloatArray(resources, + com.android.internal.R.array.config_displayWhiteBalanceAmbientTemperatureValues); + final float[] displayTemperatures = getFloatArray(resources, + com.android.internal.R.array.config_displayWhiteBalanceDisplayTemperatureValues); final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController( brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, - throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature); + throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature, + ambientTemperatures, displayTemperatures); brightnessSensor.setCallbacks(controller); colorTemperatureSensor.setCallbacks(controller); return controller; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 20933db803b9..560f7a03b20f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -342,7 +342,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { super.disableDevice(initiatedByCec, callback); assertRunOnServiceThread(); - if (!initiatedByCec && mIsActiveSource) { + if (!initiatedByCec && mIsActiveSource && mService.isControlEnabled()) { mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource( mAddress, mService.getPhysicalAddress())); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 5e7ea05f799c..28393a209111 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -1805,9 +1805,10 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. - private int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + private int interceptMotionBeforeQueueingNonInteractive(int displayId, + long whenNanos, int policyFlags) { return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive( - whenNanos, policyFlags); + displayId, whenNanos, policyFlags); } // Native callback. @@ -2021,7 +2022,13 @@ public class InputManagerService extends IInputManager.Stub public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags); - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags); + /** + * Provides an opportunity for the window manager policy to intercept early motion event + * processing when the device is in a non-interactive state since these events are normally + * dropped. + */ + int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags); public long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 144f2b6b143f..96ba0841855c 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -667,13 +667,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final int mSequenceNumber; final long mTimestamp; final long mWallTime; + @UserIdInt + final int mImeUserId; @NonNull final IBinder mImeToken; + final int mImeDisplayId; @NonNull final String mImeId; @StartInputReason final int mStartInputReason; final boolean mRestarting; + @UserIdInt + final int mTargetUserId; + final int mTargetDisplayId; @Nullable final IBinder mTargetWindow; @NonNull @@ -682,17 +688,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final int mTargetWindowSoftInputMode; final int mClientBindSequenceNumber; - StartInputInfo(@NonNull IBinder imeToken, @NonNull String imeId, - @StartInputReason int startInputReason, boolean restarting, - @Nullable IBinder targetWindow, @NonNull EditorInfo editorInfo, - @SoftInputModeFlags int targetWindowSoftInputMode, int clientBindSequenceNumber) { + StartInputInfo(@UserIdInt int imeUserId, @NonNull IBinder imeToken, int imeDisplayId, + @NonNull String imeId, @StartInputReason int startInputReason, boolean restarting, + @UserIdInt int targetUserId, int targetDisplayId, @Nullable IBinder targetWindow, + @NonNull EditorInfo editorInfo, @SoftInputModeFlags int targetWindowSoftInputMode, + int clientBindSequenceNumber) { mSequenceNumber = sSequenceNumber.getAndIncrement(); mTimestamp = SystemClock.uptimeMillis(); mWallTime = System.currentTimeMillis(); + mImeUserId = imeUserId; mImeToken = imeToken; + mImeDisplayId = imeDisplayId; mImeId = imeId; mStartInputReason = startInputReason; mRestarting = restarting; + mTargetUserId = targetUserId; + mTargetDisplayId = targetDisplayId; mTargetWindow = targetWindow; mEditorInfo = editorInfo; mTargetWindowSoftInputMode = targetWindowSoftInputMode; @@ -749,13 +760,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub int mSequenceNumber; long mTimestamp; long mWallTime; + @UserIdInt + int mImeUserId; @NonNull String mImeTokenString; + int mImeDisplayId; @NonNull String mImeId; @StartInputReason int mStartInputReason; boolean mRestarting; + @UserIdInt + int mTargetUserId; + int mTargetDisplayId; @NonNull String mTargetWindowString; @NonNull @@ -772,12 +789,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mSequenceNumber = original.mSequenceNumber; mTimestamp = original.mTimestamp; mWallTime = original.mWallTime; + mImeUserId = original.mImeUserId; // Intentionally convert to String so as not to keep a strong reference to a Binder // object. mImeTokenString = String.valueOf(original.mImeToken); + mImeDisplayId = original.mImeDisplayId; mImeId = original.mImeId; mStartInputReason = original.mStartInputReason; mRestarting = original.mRestarting; + mTargetUserId = original.mTargetUserId; + mTargetDisplayId = original.mTargetDisplayId; // Intentionally convert to String so as not to keep a strong reference to a Binder // object. mTargetWindowString = String.valueOf(original.mTargetWindow); @@ -821,11 +842,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " restarting=" + entry.mRestarting); pw.print(prefix); - pw.println(" imeToken=" + entry.mImeTokenString + " [" + entry.mImeId + "]"); + pw.print(" imeToken=" + entry.mImeTokenString + " [" + entry.mImeId + "]"); + pw.print(" imeUserId=" + entry.mImeUserId); + pw.println(" imeDisplayId=" + entry.mImeDisplayId); pw.print(prefix); pw.println(" targetWin=" + entry.mTargetWindowString + " [" + entry.mEditorInfo.packageName + "]" + + " targetUserId=" + entry.mTargetUserId + + " targetDisplayId=" + entry.mTargetDisplayId + " clientBindSeq=" + entry.mClientBindSequenceNumber); pw.print(prefix); @@ -1904,9 +1929,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } final Binder startInputToken = new Binder(); - final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason, - !initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode, - mCurSeq); + final StartInputInfo info = new StartInputInfo(mSettings.getCurrentUserId(), mCurToken, + mCurTokenDisplayId, mCurId, startInputReason, !initial, + UserHandle.getUserId(mCurClient.uid), mCurClient.selfReportedDisplayId, + mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode, mCurSeq); mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow); mStartInputHistory.addEntry(info); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 2f0b388b9a7d..3abacc2c9c10 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -976,7 +976,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest); - if (mProviderRequest.reportLocation && !mDisableGps && isEnabled()) { + if (mProviderRequest.reportLocation && isEnabled()) { // update client uids updateClientUids(mWorkSource); diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java index f03c99b0272e..aa8a25a36333 100644 --- a/services/core/java/com/android/server/location/RemoteListenerHelper.java +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -31,9 +31,11 @@ import java.util.HashMap; import java.util.Map; /** - * A helper class, that handles operations in remote listeners, and tracks for remote process death. + * A helper class that handles operations in remote listeners. + * + * @param <TListener> the type of GNSS data listener. */ -abstract class RemoteListenerHelper<TListener extends IInterface> { +public abstract class RemoteListenerHelper<TListener extends IInterface> { protected static final int RESULT_SUCCESS = 0; protected static final int RESULT_NOT_AVAILABLE = 1; @@ -46,7 +48,7 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { protected final Handler mHandler; private final String mTag; - private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>(); + private final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>(); protected final Context mContext; protected final AppOpsManager mAppOps; @@ -71,24 +73,21 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { return mIsRegistered; } - public boolean addListener(@NonNull TListener listener, CallerIdentity callerIdentity) { + /** + * Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}. + */ + public void addListener(@NonNull TListener listener, CallerIdentity callerIdentity) { Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener."); IBinder binder = listener.asBinder(); - LinkedListener deathListener = new LinkedListener(listener, callerIdentity); synchronized (mListenerMap) { if (mListenerMap.containsKey(binder)) { // listener already added - return true; - } - try { - binder.linkToDeath(deathListener, 0 /* flags */); - } catch (RemoteException e) { - // if the remote process registering the listener is already death, just swallow the - // exception and return - Log.v(mTag, "Remote listener already died.", e); - return false; + return; } - mListenerMap.put(binder, deathListener); + + IdentifiedListener identifiedListener = new IdentifiedListener(listener, + callerIdentity); + mListenerMap.put(binder, identifiedListener); // update statuses we already know about, starting from the ones that will never change int result; @@ -107,26 +106,23 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { } else { // at this point if the supported flag is not set, the notification will be sent // asynchronously in the future - return true; + return; } - post(deathListener, getHandlerOperation(result)); + post(identifiedListener, getHandlerOperation(result)); } - return true; } + /** + * Remove GNSS data listener {@code listener}. + */ public void removeListener(@NonNull TListener listener) { Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener."); - IBinder binder = listener.asBinder(); - LinkedListener linkedListener; synchronized (mListenerMap) { - linkedListener = mListenerMap.remove(binder); + mListenerMap.remove(listener.asBinder()); if (mListenerMap.isEmpty()) { tryUnregister(); } } - if (linkedListener != null) { - binder.unlinkToDeath(linkedListener, 0 /* flags */); - } } protected abstract boolean isAvailableInPlatform(); @@ -198,14 +194,15 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { } private void foreachUnsafe(ListenerOperation<TListener> operation) { - for (LinkedListener linkedListener : mListenerMap.values()) { - post(linkedListener, operation); + for (IdentifiedListener identifiedListener : mListenerMap.values()) { + post(identifiedListener, operation); } } - private void post(LinkedListener linkedListener, ListenerOperation<TListener> operation) { + private void post(IdentifiedListener identifiedListener, + ListenerOperation<TListener> operation) { if (operation != null) { - mHandler.post(new HandlerRunnable(linkedListener, operation)); + mHandler.post(new HandlerRunnable(identifiedListener, operation)); } } @@ -259,35 +256,31 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { return RESULT_SUCCESS; } - private class LinkedListener implements IBinder.DeathRecipient { + private class IdentifiedListener { private final TListener mListener; private final CallerIdentity mCallerIdentity; - LinkedListener(@NonNull TListener listener, CallerIdentity callerIdentity) { + private IdentifiedListener(@NonNull TListener listener, CallerIdentity callerIdentity) { mListener = listener; mCallerIdentity = callerIdentity; } - - @Override - public void binderDied() { - Log.d(mTag, "Remote Listener died: " + mListener); - removeListener(mListener); - } } private class HandlerRunnable implements Runnable { - private final LinkedListener mLinkedListener; + private final IdentifiedListener mIdentifiedListener; private final ListenerOperation<TListener> mOperation; - HandlerRunnable(LinkedListener linkedListener, ListenerOperation<TListener> operation) { - mLinkedListener = linkedListener; + private HandlerRunnable(IdentifiedListener identifiedListener, + ListenerOperation<TListener> operation) { + mIdentifiedListener = identifiedListener; mOperation = operation; } @Override public void run() { try { - mOperation.execute(mLinkedListener.mListener, mLinkedListener.mCallerIdentity); + mOperation.execute(mIdentifiedListener.mListener, + mIdentifiedListener.mCallerIdentity); } catch (RemoteException e) { Log.v(mTag, "Error in monitored listener.", e); } diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java index 5c7317892f3f..62d9b20b636d 100644 --- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -284,13 +284,16 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { * Tells the system UI that volume has changed on an active remote session. */ public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { - if (mRvc == null || !session.isActive()) { - return; - } - try { - mRvc.remoteVolumeChanged(session.getSessionToken(), flags); - } catch (Exception e) { - Log.wtf(TAG, "Error sending volume change to system UI.", e); + synchronized (mLock) { + if (mRvc == null || !session.isActive()) { + return; + } + try { + mRvc.remoteVolumeChanged(session.getSessionToken(), flags); + } catch (Exception e) { + Log.w(TAG, "Error sending volume change to system UI.", e); + mRvc = null; + } } } @@ -563,7 +566,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { String callerPackageName, SessionCallbackLink cb, String tag) { FullUserRecord user = getFullUserRecordLocked(userId); if (user == null) { - Log.wtf(TAG, "Request from invalid user: " + userId); + Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName); throw new RuntimeException("Session request from invalid user."); } @@ -643,7 +646,8 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); mRvc.updateRemoteController(record == null ? null : record.getSessionToken()); } catch (RemoteException e) { - Log.wtf(TAG, "Error sending default remote volume to sys ui.", e); + Log.w(TAG, "Error sending default remote volume to sys ui.", e); + mRvc = null; } } } @@ -1661,7 +1665,9 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { final long token = Binder.clearCallingIdentity(); try { enforceSystemUiPermission("listen for volume changes", pid, uid); - mRvc = rvc; + synchronized (mLock) { + mRvc = rvc; + } } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java new file mode 100644 index 000000000000..dac4b6ff39c3 --- /dev/null +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License.s + */ + +package com.android.server.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.apex.ApexInfo; +import android.apex.ApexInfoList; +import android.apex.ApexSessionInfo; +import android.apex.IApexService; +import android.content.pm.PackageInfo; +import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; + +import com.android.internal.util.IndentingPrintWriter; + +import java.io.File; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * ApexManager class handles communications with the apex service to perform operation and queries, + * as well as providing caching to avoid unnecessary calls to the service. + */ +class ApexManager { + static final String TAG = "ApexManager"; + private final IApexService mApexService; + private final Map<String, PackageInfo> mActivePackagesCache; + + ApexManager() { + mApexService = IApexService.Stub.asInterface( + ServiceManager.getService("apexservice")); + mActivePackagesCache = populateActivePackagesCache(); + } + + @NonNull + private Map<String, PackageInfo> populateActivePackagesCache() { + try { + List<PackageInfo> list = new ArrayList<>(); + final ApexInfo[] activePkgs = mApexService.getActivePackages(); + for (ApexInfo ai : activePkgs) { + // If the device is using flattened APEX, don't report any APEX + // packages since they won't be managed or updated by PackageManager. + if ((new File(ai.packagePath)).isDirectory()) { + break; + } + try { + list.add(PackageParser.generatePackageInfoFromApex( + new File(ai.packagePath), true /* collect certs */)); + } catch (PackageParserException pe) { + throw new IllegalStateException("Unable to parse: " + ai, pe); + } + } + return list.stream().collect(Collectors.toMap(p -> p.packageName, Function.identity())); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); + throw new RuntimeException(re); + } + } + + /** + * Retrieves information about an active APEX package. + * + * @param packageName the package name to look for. Note that this is the package name reported + * in the APK container manifest (i.e. AndroidManifest.xml), which might + * differ from the one reported in the APEX manifest (i.e. + * apex_manifest.json). + * @return a PackageInfo object with the information about the package, or null if the package + * is not found. + */ + @Nullable PackageInfo getActivePackage(String packageName) { + return mActivePackagesCache.get(packageName); + } + + /** + * Retrieves information about all active APEX packages. + * + * @return a Collection of PackageInfo object, each one containing information about a different + * active package. + */ + Collection<PackageInfo> getActivePackages() { + return mActivePackagesCache.values(); + } + + /** + * Retrieves information about an apexd staged session i.e. the internal state used by apexd to + * track the different states of a session. + * + * @param sessionId the identifier of the session. + * @return an ApexSessionInfo object, or null if the session is not known. + */ + @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { + try { + ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId); + if (apexSessionInfo.isUnknown) { + return null; + } + return apexSessionInfo; + } catch (RemoteException re) { + Slog.e(TAG, "Unable to contact apexservice", re); + throw new RuntimeException(re); + } + } + + /** + * Submit a staged session to apex service. This causes the apex service to perform some initial + * verification and accept or reject the session. Submitting a session successfully is not + * enough for it to be activated at the next boot, the caller needs to call + * {@link #markStagedSessionReady(int)}. + * + * @param sessionId the identifier of the {@link PackageInstallerSession} being submitted. + * @param childSessionIds if {@code sessionId} is a multi-package session, this should contain + * an array of identifiers of all the child sessions. Otherwise it should + * be an empty array. + * @param apexInfoList this is an output parameter, which needs to be initialized by tha caller + * and will be filled with a list of {@link ApexInfo} objects, each of which + * contains metadata about one of the packages being submitted as part of + * the session. + * @return whether the submission of the session was successful. + */ + boolean submitStagedSession( + int sessionId, @NonNull int[] childSessionIds, @NonNull ApexInfoList apexInfoList) { + try { + return mApexService.submitStagedSession(sessionId, childSessionIds, apexInfoList); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to contact apexservice", re); + throw new RuntimeException(re); + } + } + + /** + * Mark a staged session previously submitted using {@cde submitStagedSession} as ready to be + * applied at next reboot. + * + * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready. + * @return true upon success, false if the session is unknown. + */ + boolean markStagedSessionReady(int sessionId) { + try { + return mApexService.markStagedSessionReady(sessionId); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to contact apexservice", re); + throw new RuntimeException(re); + } + } + + /** + * Dumps various state information to the provided {@link PrintWriter} object. + * + * @param pw the {@link PrintWriter} object to send information to. + * @param packageName a {@link String} containing a package name, or {@code null}. If set, only + * information about that specific package will be dumped. + */ + void dump(PrintWriter pw, @Nullable String packageName) { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); + ipw.println(); + ipw.println("Active APEX packages:"); + ipw.increaseIndent(); + try { + populateActivePackagesCache(); + for (PackageInfo pi : mActivePackagesCache.values()) { + if (packageName != null && !packageName.equals(pi.packageName)) { + continue; + } + ipw.println(pi.packageName); + ipw.increaseIndent(); + ipw.println("Version: " + pi.versionCode); + ipw.println("Path: " + pi.applicationInfo.sourceDir); + ipw.decreaseIndent(); + } + ipw.decreaseIndent(); + ipw.println(); + ipw.println("APEX session state:"); + ipw.increaseIndent(); + final ApexSessionInfo[] sessions = mApexService.getSessions(); + for (ApexSessionInfo si : sessions) { + ipw.println("Session ID: " + Integer.toString(si.sessionId)); + ipw.increaseIndent(); + if (si.isUnknown) { + ipw.println("State: UNKNOWN"); + } else if (si.isVerified) { + ipw.println("State: VERIFIED"); + } else if (si.isStaged) { + ipw.println("State: STAGED"); + } else if (si.isActivated) { + ipw.println("State: ACTIVATED"); + } else if (si.isActivationPendingRetry) { + ipw.println("State: ACTIVATION PENDING RETRY"); + } else if (si.isActivationFailed) { + ipw.println("State: ACTIVATION FAILED"); + } + ipw.decreaseIndent(); + } + ipw.decreaseIndent(); + } catch (RemoteException e) { + ipw.println("Couldn't communicate with apexd."); + } + } +} diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 21965e4e83a2..86083494f3d6 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -186,7 +186,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } }; - public PackageInstallerService(Context context, PackageManagerService pm) { + public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) { mContext = context; mPm = pm; mPermissionManager = LocalServices.getService(PermissionManagerInternal.class); @@ -204,7 +204,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions"); mSessionsDir.mkdirs(); - mStagingManager = new StagingManager(pm, this); + mStagingManager = new StagingManager(pm, this, am); } private void setBootCompleted() { @@ -481,6 +481,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } + if (params.isStaged) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG); + } + if (!params.isMultiPackage) { // Only system components can circumvent runtime permissions when installing. if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0 diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 874d1a719ee6..400443a16fab 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -119,13 +119,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.apex.ApexInfo; -import android.apex.ApexSessionInfo; -import android.apex.IApexService; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppDetailsActivity; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.IActivityManager; import android.app.ResourcesManager; import android.app.admin.IDevicePolicyManager; @@ -241,6 +239,7 @@ import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; import android.permission.PermissionControllerManager; +import android.provider.DeviceConfig; import android.provider.MediaStore; import android.provider.Settings.Global; import android.provider.Settings.Secure; @@ -733,10 +732,10 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>(); - private PackageManager mPackageManager; - private final ModuleInfoProvider mModuleInfoProvider; + private final ApexManager mApexManager; + class PackageParserCallback implements PackageParser.Callback { @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); @@ -1046,12 +1045,17 @@ public class PackageManagerService extends IPackageManager.Stub verificationIntent.setComponent(mIntentFilterVerifierComponent); verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + final long whitelistTimeout = getVerificationTimeout(); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setTemporaryAppWhitelistDuration(whitelistTimeout); + DeviceIdleController.LocalService idleController = getDeviceIdleController(); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), - mIntentFilterVerifierComponent.getPackageName(), getVerificationTimeout(), + mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout, UserHandle.USER_SYSTEM, true, "intent filter verifier"); - mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM); + mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM, + null, options.toBundle()); if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Sending IntentFilter verification broadcast"); } @@ -3074,7 +3078,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - mInstallerService = new PackageInstallerService(context, this); + mApexManager = new ApexManager(); + mInstallerService = new PackageInstallerService(context, this, mApexManager); final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { @@ -3934,27 +3939,7 @@ public class PackageManagerService extends IPackageManager.Stub } // if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) { - //TODO(b/123052859) Don't do file operations every time there is a query. - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - if (apex != null) { - try { - final ApexInfo activePkg = apex.getActivePackage(packageName); - if (activePkg != null && !TextUtils.isEmpty(activePkg.packagePath)) { - try { - return PackageParser.generatePackageInfoFromApex( - new File(activePkg.packagePath), true /* collect certs */); - } catch (PackageParserException pe) { - Log.e(TAG, "Unable to parse package at " - + activePkg.packagePath, pe); - } - } - } catch (RemoteException e) { - Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString()); - } - } else { - Log.e(TAG, "Unable to connect to apexservice for querying packages."); - } + return mApexManager.getActivePackage(packageName); } } return null; @@ -7851,25 +7836,7 @@ public class PackageManagerService extends IPackageManager.Stub if (listApex) { // TODO(b/119767311): include uninstalled/inactive APEX if // MATCH_UNINSTALLED_PACKAGES is set. - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - if (apex != null) { - try { - final ApexInfo[] activePkgs = apex.getActivePackages(); - for (ApexInfo ai : activePkgs) { - try { - list.add(PackageParser.generatePackageInfoFromApex( - new File(ai.packagePath), true /* collect certs */)); - } catch (PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " + ai, pe); - } - } - } catch (RemoteException e) { - Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString()); - } - } else { - Log.e(TAG, "Unable to connect to apexservice for querying packages."); - } + list.addAll(mApexManager.getActivePackages()); } return new ParceledListSlice<>(list); } @@ -14604,14 +14571,19 @@ public class PackageManagerService extends IPackageManager.Stub new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - // TODO(ruhler) b/112431924 Have a configurable setting to - // allow changing the timeout and fall back to the default - // if no such specified. + // the duration to wait for rollback to be enabled, in millis + long rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT; + try { + rollbackTimeout = Long.valueOf( + DeviceConfig.getProperty( + DeviceConfig.Rollback.NAMESPACE, + DeviceConfig.Rollback.ENABLE_ROLLBACK_TIMEOUT)); + } catch (NumberFormatException ignore) { + } final Message msg = mHandler.obtainMessage( ENABLE_ROLLBACK_TIMEOUT); msg.arg1 = enableRollbackToken; - mHandler.sendMessageDelayed(msg, - DEFAULT_ENABLE_ROLLBACK_TIMEOUT); + mHandler.sendMessageDelayed(msg, rollbackTimeout); } }, null, 0, null, null); @@ -21319,51 +21291,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) { - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); - ipw.println(); - ipw.println("Active APEX packages:"); - ipw.increaseIndent(); - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - try { - final ApexInfo[] activeApexes = apex.getActivePackages(); - for (ApexInfo ai : activeApexes) { - if (packageName != null && !packageName.equals(ai.packageName)) { - continue; - } - ipw.println(ai.packageName); - ipw.increaseIndent(); - ipw.println("Version: " + Long.toString(ai.versionCode)); - ipw.println("Path: " + ai.packagePath); - ipw.decreaseIndent(); - } - ipw.decreaseIndent(); - ipw.println(); - ipw.println("APEX session state:"); - ipw.increaseIndent(); - final ApexSessionInfo[] sessions = apex.getSessions(); - for (ApexSessionInfo si : sessions) { - ipw.println("Session ID: " + Integer.toString(si.sessionId)); - ipw.increaseIndent(); - if (si.isUnknown) { - ipw.println("State: UNKNOWN"); - } else if (si.isVerified) { - ipw.println("State: VERIFIED"); - } else if (si.isStaged) { - ipw.println("State: STAGED"); - } else if (si.isActivated) { - ipw.println("State: ACTIVATED"); - } else if (si.isActivationPendingRetry) { - ipw.println("State: ACTIVATION PENDING RETRY"); - } else if (si.isActivationFailed) { - ipw.println("State: ACTIVATION FAILED"); - } - ipw.decreaseIndent(); - } - ipw.decreaseIndent(); - } catch (RemoteException e) { - ipw.println("Couldn't communicate with apexd."); - } + mApexManager.dump(pw, packageName); } } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 84c8b606a9d9..d9a5eb901344 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -18,6 +18,7 @@ package com.android.server.pm; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.Person; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -33,6 +34,7 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.pm.ShortcutService.DumpFilter; @@ -71,6 +73,7 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String TAG_EXTRAS = "extras"; private static final String TAG_SHORTCUT = "shortcut"; private static final String TAG_CATEGORIES = "categories"; + private static final String TAG_PERSON = "person"; private static final String ATTR_NAME = "name"; private static final String ATTR_CALL_COUNT = "call-count"; @@ -96,6 +99,12 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String ATTR_ICON_RES_NAME = "icon-resname"; private static final String ATTR_BITMAP_PATH = "bitmap-path"; + private static final String ATTR_PERSON_NAME = "name"; + private static final String ATTR_PERSON_URI = "uri"; + private static final String ATTR_PERSON_KEY = "key"; + private static final String ATTR_PERSON_IS_BOT = "is-bot"; + private static final String ATTR_PERSON_IS_IMPORTANT = "is-important"; + private static final String NAME_CATEGORIES = "categories"; private static final String TAG_STRING_ARRAY_XMLUTILS = "string-array"; @@ -1499,6 +1508,22 @@ class ShortcutPackage extends ShortcutPackageItem { out.endTag(null, TAG_CATEGORIES); } } + if (!forBackup) { // Don't backup the persons field. + final Person[] persons = si.getPersons(); + if (!ArrayUtils.isEmpty(persons)) { + for (int i = 0; i < persons.length; i++) { + final Person p = persons[i]; + + out.startTag(null, TAG_PERSON); + ShortcutService.writeAttr(out, ATTR_PERSON_NAME, p.getName()); + ShortcutService.writeAttr(out, ATTR_PERSON_URI, p.getUri()); + ShortcutService.writeAttr(out, ATTR_PERSON_KEY, p.getKey()); + ShortcutService.writeAttr(out, ATTR_PERSON_IS_BOT, p.isBot()); + ShortcutService.writeAttr(out, ATTR_PERSON_IS_IMPORTANT, p.isImportant()); + out.endTag(null, TAG_PERSON); + } + } + } final Intent[] intentsNoExtras = si.getIntentsNoExtras(); final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases(); final int numIntents = intentsNoExtras.length; @@ -1588,6 +1613,7 @@ class ShortcutPackage extends ShortcutPackageItem { String bitmapPath; int backupVersionCode; ArraySet<String> categories = null; + ArrayList<Person> persons = new ArrayList<>(); id = ShortcutService.parseStringAttribute(parser, ATTR_ID); activityComponent = ShortcutService.parseComponentNameAttribute(parser, @@ -1638,6 +1664,9 @@ class ShortcutPackage extends ShortcutPackageItem { case TAG_CATEGORIES: // This just contains string-array. continue; + case TAG_PERSON: + persons.add(parsePerson(parser)); + continue; case TAG_STRING_ARRAY_XMLUTILS: if (NAME_CATEGORIES.equals(ShortcutService.parseStringAttribute(parser, ATTR_NAME_XMLUTILS))) { @@ -1680,7 +1709,8 @@ class ShortcutPackage extends ShortcutPackageItem { categories, intents.toArray(new Intent[intents.size()]), rank, extras, lastChangedTimestamp, flags, - iconResId, iconResName, bitmapPath, disabledReason); + iconResId, iconResName, bitmapPath, disabledReason, + persons.toArray(new Person[persons.size()])); } private static Intent parseIntent(XmlPullParser parser) @@ -1713,6 +1743,20 @@ class ShortcutPackage extends ShortcutPackageItem { return intent; } + private static Person parsePerson(XmlPullParser parser) + throws IOException, XmlPullParserException { + CharSequence name = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_NAME); + String uri = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_URI); + String key = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_KEY); + boolean isBot = ShortcutService.parseBooleanAttribute(parser, ATTR_PERSON_IS_BOT); + boolean isImportant = ShortcutService.parseBooleanAttribute(parser, + ATTR_PERSON_IS_IMPORTANT); + + Person.Builder builder = new Person.Builder(); + builder.setName(name).setUri(uri).setKey(key).setBot(isBot).setImportant(isImportant); + return builder.build(); + } + @VisibleForTesting List<ShortcutInfo> getAllShortcutsForTest() { return new ArrayList<>(mShortcuts.values()); diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java index 90f08c30139a..668fc88b6b58 100644 --- a/services/core/java/com/android/server/pm/ShortcutParser.java +++ b/services/core/java/com/android/server/pm/ShortcutParser.java @@ -449,7 +449,8 @@ public class ShortcutParser { iconResId, null, // icon res name null, // bitmap path - disabledReason); + disabledReason, + null /* persons */); } private static String parseCategory(ShortcutService service, AttributeSet attrs) { diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index fa8360b1e5ba..30c2281b07f1 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -20,12 +20,12 @@ import android.annotation.NonNull; import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; -import android.apex.IApexService; import android.content.Context; import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; @@ -41,7 +41,6 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; -import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.util.apk.ApkSignatureVerifier; @@ -68,14 +67,16 @@ public class StagingManager { private final PackageInstallerService mPi; private final PackageManagerService mPm; + private final ApexManager mApexManager; private final Handler mBgHandler; @GuardedBy("mStagedSessions") private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>(); - StagingManager(PackageManagerService pm, PackageInstallerService pi) { + StagingManager(PackageManagerService pm, PackageInstallerService pi, ApexManager am) { mPm = pm; mPi = pi; + mApexManager = am; mBgHandler = BackgroundThread.getHandler(); } @@ -100,7 +101,7 @@ public class StagingManager { return new ParceledListSlice<>(result); } - private static boolean validateApexSignature(String apexPath, String packageName) { + private boolean validateApexSignature(String apexPath, String packageName) { final SigningDetails signingDetails; try { signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR); @@ -109,17 +110,9 @@ public class StagingManager { return false; } - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - final ApexInfo apexInfo; - try { - apexInfo = apex.getActivePackage(packageName); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact APEXD", re); - return false; - } + final PackageInfo packageInfo = mApexManager.getActivePackage(packageName); - if (apexInfo == null || TextUtils.isEmpty(apexInfo.packageName)) { + if (packageInfo == null) { // TODO: What is the right thing to do here ? This implies there's no active package // with the given name. This should never be the case in production (where we only // accept updates to existing APEXes) but may be required for testing. @@ -129,9 +122,10 @@ public class StagingManager { final SigningDetails existingSigningDetails; try { existingSigningDetails = ApkSignatureVerifier.verify( - apexInfo.packagePath, SignatureSchemeVersion.JAR); + packageInfo.applicationInfo.sourceDir, SignatureSchemeVersion.JAR); } catch (PackageParserException e) { - Slog.e(TAG, "Unable to parse APEX package: " + apexInfo.packagePath, e); + Slog.e(TAG, "Unable to parse APEX package: " + + packageInfo.applicationInfo.sourceDir, e); return false; } @@ -143,10 +137,10 @@ public class StagingManager { return false; } - private static boolean submitSessionToApexService(@NonNull PackageInstallerSession session, - List<PackageInstallerSession> childSessions, - ApexInfoList apexInfoList) { - return sendSubmitStagedSessionRequest( + private boolean submitSessionToApexService(@NonNull PackageInstallerSession session, + List<PackageInstallerSession> childSessions, + ApexInfoList apexInfoList) { + return mApexManager.submitStagedSession( session.sessionId, childSessions != null ? childSessions.stream().mapToInt(s -> s.sessionId).toArray() : @@ -154,33 +148,6 @@ public class StagingManager { apexInfoList); } - private static boolean sendSubmitStagedSessionRequest( - int sessionId, int[] childSessionIds, ApexInfoList apexInfoList) { - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - boolean success; - try { - success = apex.submitStagedSession(sessionId, childSessionIds, apexInfoList); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact apexservice", re); - return false; - } - return success; - } - - private static boolean sendMarkStagedSessionReadyRequest(int sessionId) { - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - boolean success; - try { - success = apex.markStagedSessionReady(sessionId); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact apexservice", re); - return false; - } - return success; - } - private static boolean isApexSession(@NonNull PackageInstallerSession session) { return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0; } @@ -260,7 +227,7 @@ public class StagingManager { } session.setStagedSessionReady(); - if (!sendMarkStagedSessionReadyRequest(session.sessionId)) { + if (!mApexManager.markStagedSessionReady(session.sessionId)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "APEX staging failed, check logcat messages from apexd for more " + "details."); @@ -284,16 +251,12 @@ public class StagingManager { private void resumeSession(@NonNull PackageInstallerSession session) { if (sessionContainsApex(session)) { - // Check with apexservice whether the apex - // packages have been activated. - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - ApexSessionInfo apexSessionInfo; - try { - apexSessionInfo = apex.getStagedSessionInfo(session.sessionId); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact apexservice", re); - // TODO should we retry here? Mark the session as failed? + // Check with apexservice whether the apex packages have been activated. + ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId); + if (apexSessionInfo == null) { + session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + "apexd did not know anything about a staged session supposed to be" + + "activated"); return; } if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) { @@ -323,8 +286,8 @@ public class StagingManager { // The APEX part of the session is activated, proceed with the installation of APKs. if (!installApksInSession(session)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, - "APEX activation failed. Check logcat messages from apexd for " - + "more information."); + "Staged installation of APKs failed. Check logcat messages for" + + "more information."); return; } session.setStagedSessionApplied(); diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index bfa539c9007e..2036ed73fdf2 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -645,7 +645,7 @@ public final class DefaultPermissionGrantPolicy { grantPermissionsToSystemPackage(packageName, userId, CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS, PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS, - SENSORS_PERMISSIONS, STORAGE_PERMISSIONS); + SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS); grantSystemFixedPermissionsToSystemPackage(packageName, userId, LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index b00193f5453f..2e3e3e430839 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -22,7 +22,6 @@ import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.AppOpsManager.OP_TOAST_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.Context.CONTEXT_RESTRICTED; -import static android.content.Context.DISPLAY_SERVICE; import static android.content.Context.WINDOW_SERVICE; import static android.content.pm.PackageManager.FEATURE_HDMI_CEC; import static android.content.pm.PackageManager.FEATURE_LEANBACK; @@ -36,6 +35,7 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.STATE_OFF; +import static android.view.KeyEvent.KEYCODE_UNKNOWN; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; @@ -84,10 +84,8 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs - .CAMERA_LENS_COVER_ABSENT; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs - .CAMERA_LENS_UNCOVERED; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE; @@ -371,6 +369,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { IStatusBarService mStatusBarService; StatusBarManagerInternal mStatusBarManagerInternal; AudioManagerInternal mAudioManagerInternal; + DisplayManager mDisplayManager; boolean mPreloadedRecentApps; final Object mServiceAquireLock = new Object(); Vibrator mVibrator; // Vibrator for giving feedback of orientation changes @@ -1717,7 +1716,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); - mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + mDisplayManager = mContext.getSystemService(DisplayManager.class); mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH); mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK); mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC); @@ -2508,8 +2508,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return context; } - final DisplayManager dm = (DisplayManager) context.getSystemService(DISPLAY_SERVICE); - final Display targetDisplay = dm.getDisplay(displayId); + final Display targetDisplay = mDisplayManager.getDisplay(displayId); if (targetDisplay == null) { // Failed to obtain the non-default display where splash screen should be shown, // lets not show at all. @@ -3655,7 +3654,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Reset the pending key mPendingWakeKey = PENDING_KEY_NULL; } - } else if (!interactive && shouldDispatchInputWhenNonInteractive(event)) { + } else if (!interactive && shouldDispatchInputWhenNonInteractive(displayId, keyCode)) { // If we're currently dozing with the screen on and the keyguard showing, pass the key // to the application but preserve its wake key status to make sure we still move // from dozing to fully interactive if we would normally go from off to fully @@ -4126,7 +4125,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // TODO(b/117479243): handle it in InputPolicy /** {@inheritDoc} */ @Override - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags) { if ((policyFlags & FLAG_WAKE) != 0) { if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion, PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) { @@ -4134,7 +4134,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - if (shouldDispatchInputWhenNonInteractive(null)) { + if (shouldDispatchInputWhenNonInteractive(displayId, KEYCODE_UNKNOWN)) { return ACTION_PASS_TO_USER; } @@ -4149,9 +4149,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } - private boolean shouldDispatchInputWhenNonInteractive(KeyEvent event) { - final boolean displayOff = (mDefaultDisplay == null - || mDefaultDisplay.getState() == STATE_OFF); + private boolean shouldDispatchInputWhenNonInteractive(int displayId, int keyCode) { + // Apply the default display policy to unknown displays as well. + final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY + || displayId == INVALID_DISPLAY; + final Display display = isDefaultDisplay + ? mDefaultDisplay + : mDisplayManager.getDisplay(displayId); + final boolean displayOff = (display == null + || display.getState() == STATE_OFF); if (displayOff && !mHasFeatureWatch) { return false; @@ -4163,25 +4169,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // Watches handle BACK specially - if (mHasFeatureWatch - && event != null - && (event.getKeyCode() == KeyEvent.KEYCODE_BACK - || event.getKeyCode() == KeyEvent.KEYCODE_STEM_PRIMARY)) { + if (mHasFeatureWatch && (keyCode == KeyEvent.KEYCODE_BACK + || keyCode == KeyEvent.KEYCODE_STEM_PRIMARY)) { return false; } - // Send events to a dozing dream even if the screen is off since the dream - // is in control of the state of the screen. - IDreamManager dreamManager = getDreamManager(); + // TODO(b/123372519): Refine when dream can support multi display. + if (isDefaultDisplay) { + // Send events to a dozing dream even if the screen is off since the dream + // is in control of the state of the screen. + IDreamManager dreamManager = getDreamManager(); - try { - if (dreamManager != null && dreamManager.isDreaming()) { - return true; + try { + if (dreamManager != null && dreamManager.isDreaming()) { + return true; + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException when checking if dreaming", e); } - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException when checking if dreaming", e); } - // Otherwise, consume events since the user can't see what is being // interacted with. return false; diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index d1bd102f11dc..870d61b2ab90 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -1003,11 +1003,13 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * affect the power state of the device, for example, waking on motions. * Generally, it's best to keep as little as possible in the queue thread * because it's the most fragile. + * @param displayId The display ID of the motion event. * @param policyFlags The policy flags associated with the motion. * * @return Actions flags: may be {@link #ACTION_PASS_TO_USER}. */ - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags); + int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags); /** * Called from the input dispatcher thread before a key is dispatched to a window. diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index dd2cda20e0f1..f3393e2f29da 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -167,6 +167,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { public static final int CODE_DATA_BROADCAST = 1; public static final int CODE_SUBSCRIBER_BROADCAST = 1; + public static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1; /** * The last report time is provided with each intent registered to * StatsManager#setFetchReportsOperation. This allows easy de-duping in the receiver if @@ -356,6 +357,22 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } @Override + public void sendActiveConfigsChangedBroadcast(IBinder intentSenderBinder, long[] configIds) { + enforceCallingPermission(); + IntentSender intentSender = new IntentSender(intentSenderBinder); + Intent intent = new Intent(); + intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds); + try { + intentSender.sendIntent(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null); + if (DEBUG) { + Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds)); + } + } catch (IntentSender.SendIntentException e) { + Slog.w(TAG, "Unable to send active configs changed broadcast using IntentSender"); + } + } + + @Override public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey, long subscriptionId, long subscriptionRuleId, String[] cookies, StatsDimensionsValue dimensionsValue) { @@ -1169,7 +1186,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { BinderCallsStatsService.Internal binderStats = LocalServices.getService(BinderCallsStatsService.Internal.class); if (binderStats == null) { - return; + throw new IllegalStateException("binderStats is null"); } List<ExportedCallStat> callStats = binderStats.getExportedCallStats(); @@ -1200,7 +1217,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { BinderCallsStatsService.Internal binderStats = LocalServices.getService(BinderCallsStatsService.Internal.class); if (binderStats == null) { - return; + throw new IllegalStateException("binderStats is null"); } ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats(); @@ -1218,7 +1235,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { List<StatsLogEventWrapper> pulledData) { LooperStats looperStats = LocalServices.getService(LooperStats.class); if (looperStats == null) { - return; + throw new IllegalStateException("looperStats null"); } List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); @@ -1689,18 +1706,19 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private void pullCpuTimePerThreadFreq(int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { if (this.mKernelCpuThreadReader == null) { - return; + throw new IllegalStateException("mKernelCpuThreadReader is null"); } ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages = this.mKernelCpuThreadReader.getProcessCpuUsageByUids(); if (processCpuUsages == null) { - return; + throw new IllegalStateException("processCpuUsages is null"); } int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz(); if (cpuFrequencies.length > CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES) { - Slog.w(TAG, "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES - + " frequencies, but got " + cpuFrequencies.length); - return; + String message = "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES + + " frequencies, but got " + cpuFrequencies.length; + Slog.w(TAG, message); + throw new IllegalStateException(message); } for (int i = 0; i < processCpuUsages.size(); i++) { KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i); @@ -1709,10 +1727,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { for (int j = 0; j < threadCpuUsages.size(); j++) { KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j); if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) { - Slog.w(TAG, "Unexpected number of usage times," + String message = "Unexpected number of usage times," + " expected " + cpuFrequencies.length - + " but got " + threadCpuUsage.usageTimesMillis.length); - continue; + + " but got " + threadCpuUsage.usageTimesMillis.length; + Slog.w(TAG, message); + throw new IllegalStateException(message); } StatsLogEventWrapper e = diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 4faf910f52ea..0251efb872bc 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -75,12 +75,12 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.isFixedOrientationLandscape; import static android.content.pm.ActivityInfo.isFixedOrientationPortrait; import static android.content.res.Configuration.EMPTY; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET; import static android.os.Build.VERSION_CODES.HONEYCOMB; @@ -2610,10 +2610,6 @@ final class ActivityRecord extends ConfigurationContainer { } } - int getRequestedOrientation() { - return getOrientation(); - } - void setRequestedOrientation(int requestedOrientation) { setOrientation(requestedOrientation, mayFreezeScreenLocked(app)); mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( @@ -2641,7 +2637,7 @@ final class ActivityRecord extends ConfigurationContainer { int getOrientation() { if (mAppWindowToken == null) { - return SCREEN_ORIENTATION_UNSPECIFIED; + return info.screenOrientation; } return mAppWindowToken.getOrientationIgnoreVisibility(); @@ -2677,25 +2673,92 @@ final class ActivityRecord extends ConfigurationContainer { mLastReportedConfiguration.setConfiguration(global, override); } + /** + * Get the configuration orientation by the requested screen orientation + * ({@link ActivityInfo.ScreenOrientation}) of this activity. + * + * @return orientation in ({@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}, + * {@link #ORIENTATION_UNDEFINED}). + */ + int getRequestedConfigurationOrientation() { + final int screenOrientation = getOrientation(); + if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { + // NOSENSOR means the display's "natural" orientation, so return that. + final ActivityDisplay display = getDisplay(); + if (display != null && display.mDisplayContent != null) { + return display.mDisplayContent.getNaturalOrientation(); + } + } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { + // LOCKED means the activity's orientation remains unchanged, so return existing value. + return getConfiguration().orientation; + } else if (isFixedOrientationLandscape(screenOrientation)) { + return ORIENTATION_LANDSCAPE; + } else if (isFixedOrientationPortrait(screenOrientation)) { + return ORIENTATION_PORTRAIT; + } + return ORIENTATION_UNDEFINED; + } + + /** + * Indicates the activity will keep the bounds and screen configuration when it was first + * launched, no matter how its parent changes. + * + * @return {@code true} if this activity is declared as non-resizable and fixed orientation or + * aspect ratio. + */ + private boolean inSizeCompatMode() { + return !isResizeable() && (info.isFixedOrientation() || info.hasFixedAspectRatio()) + // The configuration of non-standard type should be enforced by system. + && isActivityTypeStandard() + && !mAtmService.mForceResizableActivities; + } + // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private void updateOverrideConfiguration() { + final boolean inSizeCompatMode = inSizeCompatMode(); + if (inSizeCompatMode) { + if (!matchParentBounds()) { + // The override configuration is set only once in size compatible mode. + return; + } + if (!hasProcess() && !isConfigurationCompatible(task.getConfiguration())) { + // Don't compute when launching in fullscreen and the fixed orientation is not the + // current orientation. It is more accurately to compute the override bounds from + // the updated configuration after the fixed orientation is applied. + return; + } + } + computeBounds(mTmpBounds); + if (inSizeCompatMode && mTmpBounds.isEmpty()) { + mTmpBounds.set(task.getWindowConfiguration().getBounds()); + } if (mTmpBounds.equals(getRequestedOverrideBounds())) { + // The bounds is not changed or the activity is resizable (both the 2 bounds are empty). return; } - setBounds(mTmpBounds); - - // Bounds changed...update configuration to match. - if (!matchParentBounds()) { - mTmpConfig.setTo(getRequestedOverrideConfiguration()); - task.computeConfigResourceOverrides(mTmpConfig, task.getParent().getConfiguration()); - } else { - mTmpConfig.unset(); + final Configuration overrideConfig = mTmpConfig; + overrideConfig.unset(); + if (!mTmpBounds.isEmpty()) { + overrideConfig.windowConfiguration.setBounds(mTmpBounds); + if (inSizeCompatMode) { + // Ensure the screen related fields are set. It is used to prevent activity relaunch + // when moving between displays. For screenWidthDp and screenWidthDp, because they + // are relative to bounds and density, they will be calculated in + // {@link TaskRecord#computeConfigResourceOverrides} and the result will also be + // relatively fixed. + final Configuration srcConfig = task.getConfiguration(); + overrideConfig.colorMode = srcConfig.colorMode; + overrideConfig.densityDpi = srcConfig.densityDpi; + overrideConfig.screenLayout = srcConfig.screenLayout; + // The smallest screen width is the short side of screen bounds. Because the bounds + // and density won't be changed, smallestScreenWidthDp is also fixed. + overrideConfig.smallestScreenWidthDp = srcConfig.smallestScreenWidthDp; + } } - - onRequestedOverrideConfigurationChanged(mTmpConfig); + onRequestedOverrideConfigurationChanged(overrideConfig); } @Override @@ -2707,6 +2770,86 @@ final class ActivityRecord extends ConfigurationContainer { // layout traversals. mConfigurationSeq = Math.max(++mConfigurationSeq, 1); getResolvedOverrideConfiguration().seq = mConfigurationSeq; + + if (matchParentBounds()) { + return; + } + + final Configuration resolvedConfig = getResolvedOverrideConfiguration(); + if (!inSizeCompatMode()) { + computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, + ORIENTATION_UNDEFINED, true /* insideParentBounds */); + return; + } + + final Configuration displayConfig = getDisplay().getConfiguration(); + int orientation = getConfiguration().orientation; + if (orientation != displayConfig.orientation && isConfigurationCompatible(displayConfig)) { + // The activity is compatible to apply the orientation change or it requests different + // fixed orientation. + orientation = displayConfig.orientation; + } else { + if (resolvedConfig.windowConfiguration.getAppBounds() != null) { + // Keep the computed resolved override configuration. + return; + } + final int requestedOrientation = getRequestedConfigurationOrientation(); + if (requestedOrientation != ORIENTATION_UNDEFINED) { + orientation = requestedOrientation; + } + } + + // Adjust the bounds to match the current orientation. + if (orientation != ORIENTATION_UNDEFINED) { + final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); + final int longSide = Math.max(resolvedBounds.height(), resolvedBounds.width()); + final int shortSide = Math.min(resolvedBounds.height(), resolvedBounds.width()); + final boolean toBeLandscape = orientation == ORIENTATION_LANDSCAPE; + final int width = toBeLandscape ? longSide : shortSide; + final int height = toBeLandscape ? shortSide : longSide; + // Assume the bounds is always started from zero because the size may be bigger than its + // parent (task ~ display). The actual letterboxing will be done by surface offset. + resolvedBounds.set(0, 0, width, height); + } + + // In size compatible mode, activity is allowed to have larger bounds than its parent. + computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, orientation, + false /* insideParentBounds */); + } + + private void computeConfigResourceOverrides(Configuration inOutConfig, + Configuration parentConfig, int orientation, boolean insideParentBounds) { + // Set the real orientation or undefined value to ensure the output orientation won't be the + // old value. Also reset app bounds so it will be updated according to bounds. + inOutConfig.orientation = orientation; + final Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); + if (outAppBounds != null) { + outAppBounds.setEmpty(); + } + + // TODO(b/112288258): Remove below calculation because the position information in bounds + // will be replaced by the offset of surface. + final Rect appBounds = parentConfig.windowConfiguration.getAppBounds(); + if (appBounds != null) { + final Rect outBounds = inOutConfig.windowConfiguration.getBounds(); + final int activityWidth = outBounds.width(); + final int navBarPosition = mAtmService.mWindowManager.getNavBarPosition(getDisplayId()); + if (navBarPosition == NAV_BAR_LEFT) { + // Position the activity frame on the opposite side of the nav bar. + outBounds.left = appBounds.right - activityWidth; + outBounds.right = appBounds.right; + } else if (navBarPosition == NAV_BAR_RIGHT) { + // Position the activity frame on the opposite side of the nav bar. + outBounds.left = 0; + outBounds.right = activityWidth + appBounds.left; + } else if (appBounds.width() > activityWidth) { + // Horizontally center the frame. + outBounds.left = appBounds.left + (appBounds.width() - activityWidth) / 2; + outBounds.right = outBounds.left + activityWidth; + } + } + + task.computeConfigResourceOverrides(inOutConfig, parentConfig, insideParentBounds); } @Override @@ -2739,8 +2882,7 @@ final class ActivityRecord extends ConfigurationContainer { /** Returns true if the configuration is compatible with this activity. */ boolean isConfigurationCompatible(Configuration config) { - final int orientation = mAppWindowToken != null - ? getOrientation() : info.screenOrientation; + final int orientation = getOrientation(); if (isFixedOrientationPortrait(orientation) && config.orientation != ORIENTATION_PORTRAIT) { return false; @@ -2822,21 +2964,6 @@ final class ActivityRecord extends ConfigurationContainer { // away later in StackWindowController.adjustConfigurationForBounds(). Otherwise, the app // bounds would end up too small. outBounds.set(0, 0, activityWidth + appBounds.left, activityHeight + appBounds.top); - - final int navBarPosition = mAtmService.mWindowManager.getNavBarPosition(getDisplayId()); - if (navBarPosition == NAV_BAR_LEFT) { - // Position the activity frame on the opposite side of the nav bar. - outBounds.left = appBounds.right - activityWidth; - outBounds.right = appBounds.right; - } else if (navBarPosition == NAV_BAR_RIGHT) { - // Position the activity frame on the opposite side of the nav bar. - outBounds.left = 0; - outBounds.right = activityWidth + appBounds.left; - } else { - // Horizontally center the frame. - outBounds.left = appBounds.left + (containingAppWidth - activityWidth) / 2; - outBounds.right = outBounds.left + activityWidth; - } } /** diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index cc78588eeb84..932cfd3ae007 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -5631,6 +5631,19 @@ class ActivityStack extends ConfigurationContainer { } } + /** + * Get current bounds of this stack, return empty when it is unavailable. + * @see TaskStack#getAnimationOrCurrentBounds(Rect) + */ + void getAnimationOrCurrentBounds(Rect outBounds) { + final TaskStack stack = getTaskStack(); + if (stack == null) { + outBounds.setEmpty(); + return; + } + stack.getAnimationOrCurrentBounds(outBounds); + } + private boolean skipResizeAnimation(boolean toFullscreen) { if (!toFullscreen) { return false; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 678a896bddbc..c33a2c179ab7 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -989,8 +989,8 @@ class ActivityStarter { if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) { return false; } - // don't abort if the callingPackage is a device owner - if (mService.getDevicePolicyManager().isDeviceOwnerApp(callingPackage)) { + // don't abort if the callingPackage is the device owner + if (mService.isDeviceOwner(callingPackage)) { return false; } // anything that has fallen through would currently be aborted diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 1a5e6a14e733..5a20959dcdbf 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -492,4 +492,9 @@ public abstract class ActivityTaskManagerInternal { /** Returns true if uid has a visible window or its process is in a top state. */ public abstract boolean isUidForeground(int uid); + + /** + * Called by DevicePolicyManagerService to set the package name of the device owner. + */ + public abstract void setDeviceOwnerPackageName(String deviceOwnerPkg); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 875fc4eab273..258819fdece9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -151,7 +151,6 @@ import android.app.RemoteAction; import android.app.WaitResult; import android.app.WindowConfiguration; import android.app.admin.DevicePolicyCache; -import android.app.admin.DevicePolicyManager; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.app.usage.UsageStatsManagerInternal; @@ -363,7 +362,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { WindowManagerService mWindowManager; private UserManagerService mUserManager; private AppOpsService mAppOpsService; - private DevicePolicyManager mDpm; /** All active uids in the system. */ private final SparseArray<Integer> mActiveUids = new SparseArray<>(); private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>(); @@ -623,6 +621,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private FontScaleSettingObserver mFontScaleSettingObserver; + private String mDeviceOwnerPackageName; + private final class FontScaleSettingObserver extends ContentObserver { private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE); private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS); @@ -838,13 +838,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mAppOpsService; } - DevicePolicyManager getDevicePolicyManager() { - if (mDpm == null) { - mDpm = mContext.getSystemService(DevicePolicyManager.class); - } - return mDpm; - } - boolean hasUserRestriction(String restriction, int userId) { return getUserManager().hasUserRestriction(restriction, userId); } @@ -1720,7 +1713,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (r == null) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - return r.getRequestedOrientation(); + return r.getOrientation(); } } @@ -2452,6 +2445,40 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @Override + public void offsetPinnedStackBounds(int stackId, Rect compareBounds, int xOffset, int yOffset, + int animationDuration) { + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "offsetPinnedStackBounds()"); + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + if (xOffset == 0 && yOffset == 0) { + return; + } + final ActivityStack stack = mRootActivityContainer.getStack(stackId); + if (stack == null) { + Slog.w(TAG, "offsetPinnedStackBounds: stackId " + stackId + " not found."); + return; + } + if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { + throw new IllegalArgumentException("Stack: " + stackId + + " doesn't support animated resize."); + } + final Rect destBounds = new Rect(); + stack.getAnimationOrCurrentBounds(destBounds); + if (!destBounds.isEmpty() || !destBounds.equals(compareBounds)) { + Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete."); + return; + } + destBounds.offset(xOffset, yOffset); + stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds, + animationDuration, false /* fromFullscreen */); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } /** * Moves the specified task to the primary-split-screen stack. * @@ -5691,6 +5718,17 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { || mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid); } + boolean isDeviceOwner(String packageName) { + if (packageName == null) { + return false; + } + return packageName.equals(mDeviceOwnerPackageName); + } + + void setDeviceOwnerPackageName(String deviceOwnerPkg) { + mDeviceOwnerPackageName = deviceOwnerPkg; + } + /** * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on * the whitelist @@ -7108,5 +7146,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return ActivityTaskManagerService.this.isUidForeground(uid); } } + + @Override + public void setDeviceOwnerPackageName(String deviceOwnerPkg) { + synchronized (mGlobalLock) { + ActivityTaskManagerService.this.setDeviceOwnerPackageName(deviceOwnerPkg); + } + } } } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index bcf6abaac5da..88c8b953a2e9 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -239,6 +239,17 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>(); ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>(); + /** + * The scale to fit at least one side of the activity to its parent. If the activity uses + * 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5. + */ + private float mSizeCompatScale = 1f; + /** + * The bounds in global coordinates for activity in size compatibility mode. + * @see ActivityRecord#inSizeCompatMode + */ + private Rect mSizeCompatBounds; + private boolean mDisablePreviewScreenshots; private Task mLastParent; @@ -884,6 +895,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree dc.setFocusedApp(null); mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); } + if (mLetterbox != null) { + mLetterbox.destroy(); + mLetterbox = null; + } if (!delayed) { updateReportedVisibilityLocked(); @@ -1297,6 +1312,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } } + + if (prevDc != mDisplayContent && mLetterbox != null) { + mLetterbox.onMovedToDisplay(mDisplayContent.getDisplayId()); + } } /** @@ -1555,11 +1574,52 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return mOrientation; } + /** @return {@code true} if the compatibility bounds is taking effect. */ + boolean inSizeCompatMode() { + return mSizeCompatBounds != null; + } + + @Override + float getSizeCompatScale() { + return inSizeCompatMode() ? mSizeCompatScale : super.getSizeCompatScale(); + } + + /** + * @return Non-empty bounds if the activity has override bounds. + * @see ActivityRecord#resolveOverrideConfiguration(Configuration) + */ + Rect getResolvedOverrideBounds() { + // Get bounds from resolved override configuration because it is computed with orientation. + return getResolvedOverrideConfiguration().windowConfiguration.getBounds(); + } + @Override public void onConfigurationChanged(Configuration newParentConfig) { final int prevWinMode = getWindowingMode(); mTmpPrevBounds.set(getBounds()); super.onConfigurationChanged(newParentConfig); + + final Task task = getTask(); + final Rect overrideBounds = getResolvedOverrideBounds(); + if (task != null && !overrideBounds.isEmpty() + // If the changes come from change-listener, the incoming parent configuration is + // still the old one. Make sure their orientations are the same to reduce computing + // the compatibility bounds for the intermediate state. + && getResolvedOverrideConfiguration().orientation == newParentConfig.orientation) { + final Rect taskBounds = task.getBounds(); + // Since we only center the activity horizontally, if only the fixed height is smaller + // than its container, the override bounds don't need to take effect. + if ((overrideBounds.width() != taskBounds.width() + || overrideBounds.height() > taskBounds.height())) { + calculateCompatBoundsTransformation(newParentConfig); + updateSurfacePosition(); + } else if (mSizeCompatBounds != null) { + mSizeCompatBounds = null; + mSizeCompatScale = 1f; + updateSurfacePosition(); + } + } + final int winMode = getWindowingMode(); if (prevWinMode == winMode) { @@ -1657,8 +1717,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final ActivityManager.TaskSnapshot snapshot = snapshotCtrl.getSnapshot( getTask().mTaskId, getTask().mUserId, false /* restoreFromDisk */, false /* reducedResolution */); - mThumbnail = new AppWindowThumbnail(t, this, snapshot.getSnapshot(), - true /* relative */); + if (snapshot != null) { + mThumbnail = new AppWindowThumbnail(t, this, snapshot.getSnapshot(), + true /* relative */); + } } } @@ -1671,6 +1733,54 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return mThumbnail; } + /** + * Calculates the scale and offset to horizontal center the size compatibility bounds into the + * region which is available to application. + */ + private void calculateCompatBoundsTransformation(Configuration newParentConfig) { + final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds(); + final Rect viewportBounds = parentAppBounds != null + ? parentAppBounds : newParentConfig.windowConfiguration.getBounds(); + final Rect contentBounds = getResolvedOverrideBounds(); + final float contentW = contentBounds.width(); + final float contentH = contentBounds.height(); + final float viewportW = viewportBounds.width(); + final float viewportH = viewportBounds.height(); + // Only allow to scale down. + mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH) + ? 1 : Math.min(viewportW / contentW, viewportH / contentH); + final int offsetX = (int) ((viewportW - contentW * mSizeCompatScale + 1) * 0.5f) + + viewportBounds.left; + + if (mSizeCompatBounds == null) { + mSizeCompatBounds = new Rect(); + } + mSizeCompatBounds.set(contentBounds); + mSizeCompatBounds.offsetTo(0, 0); + mSizeCompatBounds.scale(mSizeCompatScale); + mSizeCompatBounds.left += offsetX; + mSizeCompatBounds.right += offsetX; + } + + @Override + public Rect getBounds() { + if (mSizeCompatBounds != null) { + return mSizeCompatBounds; + } + return super.getBounds(); + } + + @Override + public boolean matchParentBounds() { + if (super.matchParentBounds()) { + return true; + } + // An activity in size compatibility mode may have override bounds which equals to its + // parent bounds, so the exact bounds should also be checked. + final WindowContainer parent = getParent(); + return parent == null || parent.getBounds().equals(getResolvedOverrideBounds()); + } + @Override void checkAppWindowsReadyToShow() { if (allDrawn == mLastAllDrawn) { @@ -1844,6 +1954,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (needsLetterbox) { if (mLetterbox == null) { mLetterbox = new Letterbox(() -> makeChildSurface(null)); + mLetterbox.attachInput(w); } getPosition(mTmpPoint); mLetterbox.layout(getParent().getBounds(), w.getFrameLw(), mTmpPoint); @@ -2864,6 +2975,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (mPendingRelaunchCount != 0) { pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount); } + if (mSizeCompatScale != 1f || mSizeCompatBounds != null) { + pw.println(prefix + "mSizeCompatScale=" + mSizeCompatScale + " mSizeCompatBounds=" + + mSizeCompatBounds); + } if (mRemovingFromDisplay) { pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay); } diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index f9c9d33c561a..f46835eb51fc 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -163,15 +163,12 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags); } - /** - * Provides an opportunity for the window manager policy to intercept early motion event - * processing when the device is in a non-interactive state since these events are normally - * dropped. - */ + /** {@inheritDoc} */ @Override - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags) { return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive( - whenNanos, policyFlags); + displayId, whenNanos, policyFlags); } /** diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index 987492024546..d67193ea9e69 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -20,7 +20,15 @@ import static android.view.SurfaceControl.HIDDEN; import android.graphics.Point; import android.graphics.Rect; +import android.os.Binder; +import android.os.Process; +import android.view.InputChannel; +import android.view.InputEventReceiver; +import android.view.InputWindowHandle; import android.view.SurfaceControl; +import android.view.WindowManager; + +import com.android.server.UiThread; import java.util.function.Supplier; @@ -40,6 +48,7 @@ public class Letterbox { private final LetterboxSurface mLeft = new LetterboxSurface("left"); private final LetterboxSurface mBottom = new LetterboxSurface("bottom"); private final LetterboxSurface mRight = new LetterboxSurface("right"); + private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom }; /** * Constructs a Letterbox. @@ -87,8 +96,12 @@ public class Letterbox { * Returns true if any part of the letterbox overlaps with the given {@code rect}. */ public boolean isOverlappingWith(Rect rect) { - return mTop.isOverlappingWith(rect) || mLeft.isOverlappingWith(rect) - || mBottom.isOverlappingWith(rect) || mRight.isOverlappingWith(rect); + for (LetterboxSurface surface : mSurfaces) { + if (surface.isOverlappingWith(rect)) { + return true; + } + } + return false; } /** @@ -107,25 +120,94 @@ public class Letterbox { mOuter.setEmpty(); mInner.setEmpty(); - mTop.remove(); - mLeft.remove(); - mBottom.remove(); - mRight.remove(); + for (LetterboxSurface surface : mSurfaces) { + surface.remove(); + } } /** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */ public boolean needsApplySurfaceChanges() { - return mTop.needsApplySurfaceChanges() - || mLeft.needsApplySurfaceChanges() - || mBottom.needsApplySurfaceChanges() - || mRight.needsApplySurfaceChanges(); + for (LetterboxSurface surface : mSurfaces) { + if (surface.needsApplySurfaceChanges()) { + return true; + } + } + return false; } public void applySurfaceChanges(SurfaceControl.Transaction t) { - mTop.applySurfaceChanges(t); - mLeft.applySurfaceChanges(t); - mBottom.applySurfaceChanges(t); - mRight.applySurfaceChanges(t); + for (LetterboxSurface surface : mSurfaces) { + surface.applySurfaceChanges(t); + } + } + + /** Enables touches to slide into other neighboring surfaces. */ + void attachInput(WindowState win) { + for (LetterboxSurface surface : mSurfaces) { + surface.attachInput(win); + } + } + + void onMovedToDisplay(int displayId) { + for (LetterboxSurface surface : mSurfaces) { + if (surface.mInputInterceptor != null) { + surface.mInputInterceptor.mWindowHandle.displayId = displayId; + } + } + } + + private static class InputInterceptor { + final InputChannel mServerChannel; + final InputChannel mClientChannel; + final InputWindowHandle mWindowHandle; + final InputEventReceiver mInputEventReceiver; + final WindowManagerService mWmService; + + InputInterceptor(String namePrefix, WindowState win) { + mWmService = win.mWmService; + final String name = namePrefix + (win.mAppToken != null ? win.mAppToken : win); + final InputChannel[] channels = InputChannel.openInputChannelPair(name); + mServerChannel = channels[0]; + mClientChannel = channels[1]; + mInputEventReceiver = new SimpleInputReceiver(mClientChannel); + + final Binder token = new Binder(); + mWmService.mInputManager.registerInputChannel(mServerChannel, token); + + mWindowHandle = new InputWindowHandle(null /* inputApplicationHandle */, + null /* clientWindow */, win.getDisplayId()); + mWindowHandle.name = name; + mWindowHandle.token = token; + mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_SLIPPERY; + mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; + mWindowHandle.dispatchingTimeoutNanos = + WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + mWindowHandle.visible = true; + mWindowHandle.ownerPid = Process.myPid(); + mWindowHandle.ownerUid = Process.myUid(); + mWindowHandle.scaleFactor = 1.0f; + } + + void updateTouchableRegion(Rect frame) { + mWindowHandle.touchableRegion.set(frame); + mWindowHandle.touchableRegion.translate(-frame.left, -frame.top); + } + + void dispose() { + mWmService.mInputManager.unregisterInputChannel(mServerChannel); + mInputEventReceiver.dispose(); + mServerChannel.dispose(); + mClientChannel.dispose(); + } + + private static class SimpleInputReceiver extends InputEventReceiver { + SimpleInputReceiver(InputChannel inputChannel) { + super(inputChannel, UiThread.getHandler().getLooper()); + } + } } private class LetterboxSurface { @@ -137,6 +219,8 @@ public class Letterbox { private final Rect mLayoutFrameGlobal = new Rect(); private final Rect mLayoutFrameRelative = new Rect(); + private InputInterceptor mInputInterceptor; + public LetterboxSurface(String type) { mType = type; } @@ -154,11 +238,22 @@ public class Letterbox { mSurface.setColor(new float[]{0, 0, 0}); } + void attachInput(WindowState win) { + if (mInputInterceptor != null) { + mInputInterceptor.dispose(); + } + mInputInterceptor = new InputInterceptor("Letterbox_" + mType + "_", win); + } + public void remove() { if (mSurface != null) { mSurface.remove(); mSurface = null; } + if (mInputInterceptor != null) { + mInputInterceptor.dispose(); + mInputInterceptor = null; + } } public int getWidth() { @@ -193,6 +288,10 @@ public class Letterbox { t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top); t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(), mSurfaceFrameRelative.height()); + if (mInputInterceptor != null) { + mInputInterceptor.updateTouchableRegion(mSurfaceFrameRelative); + t.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle); + } t.show(mSurface); } else if (mSurface != null) { t.hide(mSurface); diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index fb5b1d85a4a2..5c91d9e6fff2 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -300,7 +300,7 @@ class RootActivityContainer extends ConfigurationContainer * corresponding record in display manager. */ // TODO: Look into consolidating with getActivityDisplay() - ActivityDisplay getActivityDisplayOrCreate(int displayId) { + @Nullable ActivityDisplay getActivityDisplayOrCreate(int displayId) { ActivityDisplay activityDisplay = getActivityDisplay(displayId); if (activityDisplay != null) { return activityDisplay; @@ -1317,6 +1317,9 @@ class RootActivityContainer extends ConfigurationContainer if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId); synchronized (mService.mGlobalLock) { final ActivityDisplay display = getActivityDisplayOrCreate(displayId); + if (display == null) { + return; + } // Do not start home before booting, or it may accidentally finish booting before it // starts. Instead, we expect home activities to be launched when the system is ready // (ActivityManagerService#systemReady). diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 5ea24518370b..35b8641371be 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -251,7 +251,7 @@ class SurfaceAnimator { if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting); final SurfaceControl leash = mLeash; final AnimationAdapter animation = mAnimation; - reset(t, forwardCancel); + reset(t, false); if (animation != null) { if (!mAnimationStartDelayed && forwardCancel) { animation.onAnimationCancelled(leash); @@ -260,6 +260,12 @@ class SurfaceAnimator { mAnimationFinishedCallback.run(); } } + + if (forwardCancel && leash != null) { + t.remove(leash); + mService.scheduleAnimationLocked(); + } + if (!restarting) { mAnimationStartDelayed = false; } diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 69f0012b69ba..59d7560fc5b0 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -119,6 +119,7 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import android.view.Display; import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; @@ -1288,22 +1289,7 @@ class TaskRecord extends ConfigurationContainer { || top == null) { return getRequestedOverrideConfiguration().orientation; } - int screenOrientation = top.getOrientation(); - if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { - // NOSENSOR means the display's "natural" orientation, so return that. - ActivityDisplay display = mStack != null ? mStack.getDisplay() : null; - if (display != null && display.mDisplayContent != null) { - return mStack.getDisplay().mDisplayContent.getNaturalOrientation(); - } - } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { - // LOCKED means the activity's orientation remains unchanged, so return existing value. - return top.getConfiguration().orientation; - } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) { - return ORIENTATION_LANDSCAPE; - } else if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) { - return ORIENTATION_PORTRAIT; - } - return ORIENTATION_UNDEFINED; + return top.getRequestedConfigurationOrientation(); } /** @@ -2084,6 +2070,11 @@ class TaskRecord extends ConfigurationContainer { return Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; } + void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, + @NonNull Configuration parentConfig) { + computeConfigResourceOverrides(inOutConfig, parentConfig, true /* insideParentBounds */); + } + /** * Calculates configuration values used by the client to get resources. This should be run * using app-facing bounds (bounds unmodified by animations or transient interactions). @@ -2093,7 +2084,7 @@ class TaskRecord extends ConfigurationContainer { * just be inherited from the parent configuration. **/ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, - @NonNull Configuration parentConfig) { + @NonNull Configuration parentConfig, boolean insideParentBounds) { int windowingMode = inOutConfig.windowConfiguration.getWindowingMode(); if (windowingMode == WINDOWING_MODE_UNDEFINED) { windowingMode = parentConfig.windowConfiguration.getWindowingMode(); @@ -2111,7 +2102,7 @@ class TaskRecord extends ConfigurationContainer { inOutConfig.windowConfiguration.setAppBounds(bounds); outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); } - if (windowingMode != WINDOWING_MODE_FREEFORM) { + if (insideParentBounds && windowingMode != WINDOWING_MODE_FREEFORM) { final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds(); if (parentAppBounds != null && !parentAppBounds.isEmpty()) { outAppBounds.intersect(parentAppBounds); @@ -2120,7 +2111,7 @@ class TaskRecord extends ConfigurationContainer { if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - if (mStack != null) { + if (insideParentBounds && mStack != null) { final DisplayInfo di = new DisplayInfo(); mStack.getDisplay().mDisplay.getDisplayInfo(di); @@ -2135,12 +2126,16 @@ class TaskRecord extends ConfigurationContainer { } if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - inOutConfig.screenWidthDp = Math.min((int) (mTmpStableBounds.width() / density), - parentConfig.screenWidthDp); + final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density); + inOutConfig.screenWidthDp = insideParentBounds + ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp) + : overrideScreenWidthDp; } if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - inOutConfig.screenHeightDp = Math.min((int) (mTmpStableBounds.height() / density), - parentConfig.screenHeightDp); + final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density); + inOutConfig.screenHeightDp = insideParentBounds + ? Math.min(overrideScreenHeightDp, parentConfig.screenWidthDp) + : overrideScreenHeightDp; } if (inOutConfig.smallestScreenWidthDp @@ -2162,7 +2157,7 @@ class TaskRecord extends ConfigurationContainer { if (inOutConfig.orientation == ORIENTATION_UNDEFINED) { inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp) - ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; + ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; } if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) { // For calculating screen layout, we need to use the non-decor inset screen area for the @@ -2332,6 +2327,7 @@ class TaskRecord extends ConfigurationContainer { info.userId = userId; info.stackId = getStackId(); info.taskId = taskId; + info.displayId = mStack == null ? Display.INVALID_DISPLAY : mStack.mDisplayId; info.isRunning = getTopActivity() != null; info.baseIntent = new Intent(getBaseIntent()); info.baseActivity = reuseActivitiesReport.base != null diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 00105be87cd4..c747c6d95fcd 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -47,7 +47,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; @@ -1839,6 +1838,9 @@ public class WindowManagerService extends IWindowManager.Stub return; } outDisplayFrame.set(win.getDisplayFrameLw()); + if (win.inSizeCompatMode()) { + outDisplayFrame.scale(win.mInvGlobalScale); + } } } @@ -1963,8 +1965,6 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs); winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0; - win.mEnforceSizeCompat = - (win.mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0; if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) { winAnimator.mAlpha = attrs.alpha; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 85b251a99f83..48cd6b68c416 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -246,7 +246,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final boolean mIsWallpaper; private final boolean mIsFloatingLayer; int mSeq; - boolean mEnforceSizeCompat; int mViewVisibility; int mSystemUiVisibility; /** @@ -505,7 +504,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private PowerManager.WakeLock mDrawLock; - final private Rect mTmpRect = new Rect(); + private final Rect mTmpRect = new Rect(); + private final Point mTmpPoint = new Point(); /** * Whether the window was resized by us while it was gone for layout. @@ -655,7 +655,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mContext = mWmService.mContext; DeathRecipient deathRecipient = new DeathRecipient(); mSeq = seq; - mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0; mPowerManagerWrapper = powerManagerWrapper; mForceSeamlesslyRotate = token.mRoundedCornerOverlay; if (localLOGV) Slog.v( @@ -733,6 +732,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } /** + * @return {@code true} if the application runs in size compatibility mode. + * @see android.content.res.CompatibilityInfo#supportsScreen + * @see ActivityRecord#inSizeCompatMode + */ + boolean inSizeCompatMode() { + return (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0 + || (mAppToken != null && mAppToken.inSizeCompatMode() + // Exclude starting window because it is not displayed by the application. + && mAttrs.type != TYPE_APPLICATION_STARTING); + } + + /** * Returns whether this {@link WindowState} has been considered for drawing by its parent. */ boolean getDrawnStateEvaluated() { @@ -995,7 +1006,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff); mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame); - if (mEnforceSizeCompat) { + if (inSizeCompatMode()) { // If there is a size compatibility scale being applied to the // window, we need to apply this to its insets so that they are // reported to the app in its coordinate space. @@ -1354,8 +1365,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } void prelayout() { - if (mEnforceSizeCompat) { - mGlobalScale = getDisplayContent().mCompatibleScreenScale; + if (inSizeCompatMode()) { + mGlobalScale = mToken.getSizeCompatScale(); mInvGlobalScale = 1 / mGlobalScale; } else { mGlobalScale = mInvGlobalScale = 1; @@ -2145,6 +2156,30 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP int getSurfaceTouchableRegion(Region region, int flags) { final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0; + if (mAppToken != null && !mAppToken.getResolvedOverrideBounds().isEmpty()) { + // There may have touchable letterboxes around the activity, so in order to let the + // letterboxes are able to receive touch event and slip to activity, the activity with + // compatibility bounds cannot occupy full screen touchable region. + if (modal) { + // A modal window uses the whole compatibility bounds. + flags |= FLAG_NOT_TOUCH_MODAL; + mTmpRect.set(mAppToken.getResolvedOverrideBounds()); + // TODO(b/112288258): Remove the forced offset when the override bounds always + // starts from zero (See {@link ActivityRecord#resolveOverrideConfiguration}). + mTmpRect.offsetTo(0, 0); + } else { + // Non-modal uses the application based frame. + mTmpRect.set(mWindowFrames.mCompatFrame); + } + // The offset of compatibility bounds is applied to surface of {@link #AppWindowToken} + // and frame, so it is unnecessary to translate twice in surface based coordinates. + final int surfaceOffsetX = mAppToken.inSizeCompatMode() + ? mAppToken.getBounds().left : 0; + mTmpRect.offset(surfaceOffsetX - mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top); + region.set(mTmpRect); + return flags; + } + if (modal && mAppToken != null) { // Limit the outer touch to the activity stack region. flags |= FLAG_NOT_TOUCH_MODAL; @@ -2943,7 +2978,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING) Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING"); - final Rect frame = mWindowFrames.mFrame; + final Rect frame = mWindowFrames.mCompatFrame; final Rect overscanInsets = mWindowFrames.mLastOverscanInsets; final Rect contentInsets = mWindowFrames.mLastContentInsets; final Rect visibleInsets = mWindowFrames.mLastVisibleInsets; @@ -3353,7 +3388,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.println(prefix + "mHasSurface=" + mHasSurface + " isReadyForDisplay()=" + isReadyForDisplay() + " mWindowRemovalAllowed=" + mWindowRemovalAllowed); - if (mEnforceSizeCompat) { + if (inSizeCompatMode()) { pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB)); } if (dumpAll) { @@ -3477,17 +3512,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float x, y; int w,h; + final boolean inSizeCompatMode = inSizeCompatMode(); if ((mAttrs.flags & FLAG_SCALED) != 0) { if (mAttrs.width < 0) { w = pw; - } else if (mEnforceSizeCompat) { + } else if (inSizeCompatMode) { w = (int)(mAttrs.width * mGlobalScale + .5f); } else { w = mAttrs.width; } if (mAttrs.height < 0) { h = ph; - } else if (mEnforceSizeCompat) { + } else if (inSizeCompatMode) { h = (int)(mAttrs.height * mGlobalScale + .5f); } else { h = mAttrs.height; @@ -3495,21 +3531,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } else { if (mAttrs.width == MATCH_PARENT) { w = pw; - } else if (mEnforceSizeCompat) { + } else if (inSizeCompatMode) { w = (int)(mRequestedWidth * mGlobalScale + .5f); } else { w = mRequestedWidth; } if (mAttrs.height == MATCH_PARENT) { h = ph; - } else if (mEnforceSizeCompat) { + } else if (inSizeCompatMode) { h = (int)(mRequestedHeight * mGlobalScale + .5f); } else { h = mRequestedHeight; } } - if (mEnforceSizeCompat) { + if (inSizeCompatMode) { x = mAttrs.x * mGlobalScale; y = mAttrs.y * mGlobalScale; } else { @@ -3537,7 +3573,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // We need to make sure we update the CompatFrame as it is used for // cropping decisions, etc, on systems where we lack a decor layer. mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame); - if (mEnforceSizeCompat) { + if (inSizeCompatMode) { // See comparable block in computeFrameLw. mWindowFrames.mCompatFrame.scale(mInvGlobalScale); } @@ -3650,7 +3686,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float translateToWindowX(float x) { float winX = x - mWindowFrames.mFrame.left; - if (mEnforceSizeCompat) { + if (inSizeCompatMode()) { winX *= mGlobalScale; } return winX; @@ -3658,7 +3694,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float translateToWindowY(float y) { float winY = y - mWindowFrames.mFrame.top; - if (mEnforceSizeCompat) { + if (inSizeCompatMode()) { winY *= mGlobalScale; } return winY; @@ -4233,12 +4269,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ void calculatePolicyCrop(Rect policyCrop) { final DisplayContent displayContent = getDisplayContent(); - final DisplayInfo displayInfo = displayContent.getDisplayInfo(); - if (!isDefaultDisplay()) { + if (!displayContent.isDefaultDisplay && !displayContent.supportsSystemDecorations()) { // On a different display there is no system decor. Crop the window // by the screen boundaries. - // TODO(multi-display) + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(), mWindowFrames.mCompatFrame.height()); policyCrop.intersect(-mWindowFrames.mCompatFrame.left, -mWindowFrames.mCompatFrame.top, @@ -4304,7 +4339,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // scale function because we want to round things to make the crop // always round to a larger rect to ensure we don't crop too // much and hide part of the window that should be seen. - if (mEnforceSizeCompat && mInvGlobalScale != 1.0f) { + if (inSizeCompatMode() && mInvGlobalScale != 1.0f) { final float scale = mInvGlobalScale; systemDecorRect.left = (int) (systemDecorRect.left * scale - 0.5f); systemDecorRect.top = (int) (systemDecorRect.top * scale - 0.5f); @@ -4664,8 +4699,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Since the parent was outset by its surface insets, we need to undo the outsetting // with insetting by the same amount. final WindowState parent = getParentWindow(); - outPoint.offset(-parent.mWindowFrames.mFrame.left + parent.mAttrs.surfaceInsets.left, - -parent.mWindowFrames.mFrame.top + parent.mAttrs.surfaceInsets.top); + transformSurfaceInsetsPosition(mTmpPoint, parent.mAttrs.surfaceInsets); + outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x, + -parent.mWindowFrames.mFrame.top + mTmpPoint.y); } else if (parentWindowContainer != null) { final Rect parentBounds = parentWindowContainer.getDisplayedBounds(); outPoint.offset(-parentBounds.left, -parentBounds.top); @@ -4683,7 +4719,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } // Expand for surface insets. See WindowState.expandForSurfaceInsets. - outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top); + transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets); + outPoint.offset(-mTmpPoint.x, -mTmpPoint.y); + } + + /** + * The surface insets from layout parameter are in application coordinate. If the window is + * scaled, the insets also need to be scaled for surface position in global coordinate. + */ + private void transformSurfaceInsetsPosition(Point outPos, Rect surfaceInsets) { + if (!inSizeCompatMode()) { + outPos.x = surfaceInsets.left; + outPos.y = surfaceInsets.top; + return; + } + outPos.x = (int) (surfaceInsets.left * mGlobalScale + 0.5f); + outPos.y = (int) (surfaceInsets.top * mGlobalScale + 0.5f); } boolean needsRelativeLayeringToIme() { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 3b9b8bab9e61..f0b9c62f2843 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -182,6 +182,14 @@ class WindowToken extends WindowContainer<WindowState> { } /** + * @return The scale for applications running in compatibility mode. Multiply the size in the + * application by this scale will be the size in the screen. + */ + float getSizeCompatScale() { + return mDisplayContent.mCompatibleScreenScale; + } + + /** * Returns true if the new window is considered greater than the existing window in terms of * z-order. */ diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index c18e98bab9c4..57377c633c9a 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -249,7 +249,8 @@ public: virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags); virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig); virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags); - virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags); + virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, + uint32_t& policyFlags); virtual nsecs_t interceptKeyBeforeDispatching( const sp<IBinder>& token, const KeyEvent* keyEvent, uint32_t policyFlags); @@ -1066,7 +1067,8 @@ void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent, } } -void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { +void NativeInputManager::interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, + uint32_t& policyFlags) { ATRACE_CALL(); // Policy: // - Ignore untrusted events and pass them along. @@ -1084,7 +1086,7 @@ void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& p JNIEnv* env = jniEnv(); jint wmActions = env->CallIntMethod(mServiceObj, gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, - when, policyFlags); + displayId, when, policyFlags); if (checkAndClearExceptionFromCallback(env, "interceptMotionBeforeQueueingNonInteractive")) { wmActions = 0; @@ -1794,7 +1796,7 @@ int register_android_server_InputManager(JNIEnv* env) { "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;I)I"); GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, clazz, - "interceptMotionBeforeQueueingNonInteractive", "(JI)I"); + "interceptMotionBeforeQueueingNonInteractive", "(IJI)I"); GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz, "interceptKeyBeforeDispatching", diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index cbc3791264bf..d178c3abc906 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -973,7 +973,10 @@ void GnssMeasurementCallback::translateSingleGnssMeasurement JavaObject& object) { translateSingleGnssMeasurement(&(measurement_V2_0->v1_1), object); - SET(CodeType, (static_cast<int32_t>(measurement_V2_0->codeType))); + SET(CodeType, static_cast<int32_t>(measurement_V2_0->codeType)); + + // Overwrite with v2_0.state since v2_0->v1_1->v1_0.state is deprecated. + SET(State, static_cast<int32_t>(measurement_V2_0->state)); } jobject GnssMeasurementCallback::translateGnssClock( diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e30acf71d328..9523202cbb0e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -17,8 +17,8 @@ package com.android.server.devicepolicy; import static android.Manifest.permission.BIND_DEVICE_ADMIN; -import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY; import static android.Manifest.permission.MANAGE_CA_CERTIFICATES; +import static android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER; @@ -247,6 +247,7 @@ import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.pm.UserRestrictionsUtils; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.wm.ActivityTaskManagerInternal; import com.google.android.collect.Sets; @@ -1870,7 +1871,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Owners newOwners() { return new Owners(getUserManager(), getUserManagerInternal(), - getPackageManagerInternal()); + getPackageManagerInternal(), getActivityTaskManagerInternal()); } UserManager getUserManager() { @@ -1885,6 +1886,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return LocalServices.getService(PackageManagerInternal.class); } + ActivityTaskManagerInternal getActivityTaskManagerInternal() { + return LocalServices.getService(ActivityTaskManagerInternal.class); + } + UsageStatsManagerInternal getUsageStatsManagerInternal() { return LocalServices.getService(UsageStatsManagerInternal.class); } @@ -4773,8 +4778,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int callingUserId = mInjector.userHandleGetCallingUserId(); enforceUserUnlocked(callingUserId); mContext.enforceCallingOrSelfPermission( - GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY, - "Must have " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY + " permission."); + REQUEST_SCREEN_LOCK_COMPLEXITY, + "Must have " + REQUEST_SCREEN_LOCK_COMPLEXITY + " permission."); synchronized (getLockObject()) { int targetUserId = getCredentialOwner(callingUserId, /* parent= */ false); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index ee1c1df3f162..27cd70c9a606 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -41,6 +41,7 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.server.LocalServices; +import com.android.server.wm.ActivityTaskManagerInternal; import libcore.io.IoUtils; @@ -104,6 +105,7 @@ class Owners { private final UserManager mUserManager; private final UserManagerInternal mUserManagerInternal; private final PackageManagerInternal mPackageManagerInternal; + private final ActivityTaskManagerInternal mActivityTaskManagerInternal; private boolean mSystemReady; @@ -129,18 +131,22 @@ class Owners { public Owners(UserManager userManager, UserManagerInternal userManagerInternal, - PackageManagerInternal packageManagerInternal) { - this(userManager, userManagerInternal, packageManagerInternal, new Injector()); + PackageManagerInternal packageManagerInternal, + ActivityTaskManagerInternal activityTaskManagerInternal) { + this(userManager, userManagerInternal, packageManagerInternal, + activityTaskManagerInternal, new Injector()); } @VisibleForTesting Owners(UserManager userManager, UserManagerInternal userManagerInternal, PackageManagerInternal packageManagerInternal, + ActivityTaskManagerInternal activityTaskManagerInternal, Injector injector) { mUserManager = userManager; mUserManagerInternal = userManagerInternal; mPackageManagerInternal = packageManagerInternal; + mActivityTaskManagerInternal = activityTaskManagerInternal; mInjector = injector; } @@ -187,6 +193,7 @@ class Owners { getDeviceOwnerUserId())); } pushToPackageManagerLocked(); + pushToActivityTaskManagerLocked(); pushToAppOpsLocked(); } } @@ -201,6 +208,11 @@ class Owners { po); } + private void pushToActivityTaskManagerLocked() { + mActivityTaskManagerInternal.setDeviceOwnerPackageName(mDeviceOwner != null + ? mDeviceOwner.packageName : null); + } + String getDeviceOwnerPackageName() { synchronized (mLock) { return mDeviceOwner != null ? mDeviceOwner.packageName : null; @@ -275,6 +287,7 @@ class Owners { mUserManagerInternal.setDeviceManaged(true); pushToPackageManagerLocked(); + pushToActivityTaskManagerLocked(); pushToAppOpsLocked(); } } @@ -286,6 +299,7 @@ class Owners { mUserManagerInternal.setDeviceManaged(false); pushToPackageManagerLocked(); + pushToActivityTaskManagerLocked(); pushToAppOpsLocked(); } } @@ -333,6 +347,7 @@ class Owners { mDeviceOwner.remoteBugreportHash, /* canAccessDeviceIds =*/ mDeviceOwner.canAccessDeviceIds); pushToPackageManagerLocked(); + pushToActivityTaskManagerLocked(); pushToAppOpsLocked(); } } diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java index 4354db72554a..24e5573b891d 100644 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ * 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 + * limitations under the License. */ package com.android.server.backup.encryption.chunk; @@ -36,7 +36,7 @@ import java.util.Arrays; @RunWith(RobolectricTestRunner.class) @Presubmit -public class ChunkListingTest { +public class ChunkListingMapTest { private static final String CHUNK_A = "CHUNK_A"; private static final String CHUNK_B = "CHUNK_B"; private static final String CHUNK_C = "CHUNK_C"; @@ -62,13 +62,13 @@ public class ChunkListingTest { createChunkListingProto( new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); - ChunkListing chunkListing = - ChunkListing.readFromProto( + ChunkListingMap chunkListingMap = + ChunkListingMap.readFromProto( new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - boolean chunkAInList = chunkListing.hasChunk(mChunkHashA); - boolean chunkBInList = chunkListing.hasChunk(mChunkHashB); - boolean chunkCInList = chunkListing.hasChunk(mChunkHashC); + boolean chunkAInList = chunkListingMap.hasChunk(mChunkHashA); + boolean chunkBInList = chunkListingMap.hasChunk(mChunkHashB); + boolean chunkCInList = chunkListingMap.hasChunk(mChunkHashC); assertThat(chunkAInList).isTrue(); assertThat(chunkBInList).isTrue(); @@ -81,13 +81,13 @@ public class ChunkListingTest { createChunkListingProto( new ChunkHash[] {mChunkHashA, mChunkHashB}, new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH}); - ChunkListing chunkListing = - ChunkListing.readFromProto( + ChunkListingMap chunkListingMap = + ChunkListingMap.readFromProto( new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); ChunkHash chunkHashEmpty = getHash(""); - boolean chunkCInList = chunkListing.hasChunk(mChunkHashC); - boolean emptyChunkInList = chunkListing.hasChunk(chunkHashEmpty); + boolean chunkCInList = chunkListingMap.hasChunk(mChunkHashC); + boolean emptyChunkInList = chunkListingMap.hasChunk(chunkHashEmpty); assertThat(chunkCInList).isFalse(); assertThat(emptyChunkInList).isFalse(); @@ -99,13 +99,13 @@ public class ChunkListingTest { createChunkListingProto( new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); - ChunkListing chunkListing = - ChunkListing.readFromProto( + ChunkListingMap chunkListingMap = + ChunkListingMap.readFromProto( new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - ChunkListing.Entry entryA = chunkListing.getChunkEntry(mChunkHashA); - ChunkListing.Entry entryB = chunkListing.getChunkEntry(mChunkHashB); - ChunkListing.Entry entryC = chunkListing.getChunkEntry(mChunkHashC); + ChunkListingMap.Entry entryA = chunkListingMap.getChunkEntry(mChunkHashA); + ChunkListingMap.Entry entryB = chunkListingMap.getChunkEntry(mChunkHashB); + ChunkListingMap.Entry entryC = chunkListingMap.getChunkEntry(mChunkHashC); assertThat(entryA.getLength()).isEqualTo(CHUNK_A_LENGTH); assertThat(entryB.getLength()).isEqualTo(CHUNK_B_LENGTH); @@ -118,13 +118,13 @@ public class ChunkListingTest { createChunkListingProto( new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); - ChunkListing chunkListing = - ChunkListing.readFromProto( + ChunkListingMap chunkListingMap = + ChunkListingMap.readFromProto( new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - ChunkListing.Entry entryA = chunkListing.getChunkEntry(mChunkHashA); - ChunkListing.Entry entryB = chunkListing.getChunkEntry(mChunkHashB); - ChunkListing.Entry entryC = chunkListing.getChunkEntry(mChunkHashC); + ChunkListingMap.Entry entryA = chunkListingMap.getChunkEntry(mChunkHashA); + ChunkListingMap.Entry entryB = chunkListingMap.getChunkEntry(mChunkHashB); + ChunkListingMap.Entry entryC = chunkListingMap.getChunkEntry(mChunkHashC); assertThat(entryA.getStart()).isEqualTo(0); assertThat(entryB.getStart()).isEqualTo(CHUNK_A_LENGTH); @@ -137,22 +137,24 @@ public class ChunkListingTest { createChunkListingProto( new ChunkHash[] {mChunkHashA, mChunkHashB}, new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH}); - ChunkListing chunkListing = - ChunkListing.readFromProto( + ChunkListingMap chunkListingMap = + ChunkListingMap.readFromProto( new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - ChunkListing.Entry chunkEntryNonexistentChunk = chunkListing.getChunkEntry(mChunkHashC); + ChunkListingMap.Entry chunkEntryNonexistentChunk = + chunkListingMap.getChunkEntry(mChunkHashC); assertThat(chunkEntryNonexistentChunk).isNull(); } @Test - public void testReadFromProto_whenEmptyProto_returnsChunkListingWith0Chunks() throws Exception { + public void testReadFromProto_whenEmptyProto_returnsChunkListingMapWith0Chunks() + throws Exception { ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {})); - ChunkListing chunkListing = ChunkListing.readFromProto(emptyProto); + ChunkListingMap chunkListingMap = ChunkListingMap.readFromProto(emptyProto); - assertThat(chunkListing.getChunkCount()).isEqualTo(0); + assertThat(chunkListingMap.getChunkCount()).isEqualTo(0); } @Test @@ -162,11 +164,11 @@ public class ChunkListingTest { new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); - ChunkListing chunkListing = - ChunkListing.readFromProto( + ChunkListingMap chunkListingMap = + ChunkListingMap.readFromProto( new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - assertThat(chunkListing.getChunkCount()).isEqualTo(3); + assertThat(chunkListingMap.getChunkCount()).isEqualTo(3); } private byte[] createChunkListingProto(ChunkHash[] hashes, int[] lengths) { diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index 53d72bb9a415..e51ee947cba1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -19,6 +19,7 @@ import static androidx.test.InstrumentationRegistry.getContext; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -51,6 +52,9 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import android.app.ActivityManagerInternal; import android.app.AlarmManager; @@ -82,6 +86,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; @@ -105,6 +110,8 @@ public class DeviceIdleControllerTest { @Mock private ContentResolver mContentResolver; @Mock + private DeviceIdleController.MyHandler mHandler; + @Mock private IActivityManager mIActivityManager; @Mock private LocationManager mLocationManager; @@ -154,7 +161,7 @@ public class DeviceIdleControllerTest { @Override DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) { - return mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS); + return mHandler; } @Override @@ -232,10 +239,12 @@ public class DeviceIdleControllerTest { .when(() -> LocalServices.getService(ActivityManagerInternal.class)); doReturn(mock(ActivityTaskManagerInternal.class)) .when(() -> LocalServices.getService(ActivityTaskManagerInternal.class)); + doReturn(mock(AlarmManagerInternal.class)) + .when(() -> LocalServices.getService(AlarmManagerInternal.class)); doReturn(mPowerManagerInternal) .when(() -> LocalServices.getService(PowerManagerInternal.class)); - when(mPowerManagerInternal.getLowPowerState(anyInt())).thenReturn( - mock(PowerSaveState.class)); + when(mPowerManagerInternal.getLowPowerState(anyInt())) + .thenReturn(mock(PowerSaveState.class)); doReturn(mock(NetworkPolicyManagerInternal.class)) .when(() -> LocalServices.getService(NetworkPolicyManagerInternal.class)); when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock); @@ -246,8 +255,11 @@ public class DeviceIdleControllerTest { doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt()); mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper()); mAnyMotionDetector = new AnyMotionDetectorForTest(); + mHandler = mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS); + doNothing().when(mHandler).handleMessage(any()); mInjector = new InjectorForTest(getContext()); doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any()); + mDeviceIdleController = new DeviceIdleController(getContext(), mInjector); spyOn(mDeviceIdleController); doNothing().when(mDeviceIdleController).publishBinderService(any(), any()); @@ -423,6 +435,29 @@ public class DeviceIdleControllerTest { mDeviceIdleController.becomeInactiveIfAppropriateLocked(); verifyStateConditions(STATE_ACTIVE); + + mConstants.WAIT_FOR_UNLOCK = false; + setScreenLocked(true); + setScreenOn(true); + setChargingOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_ACTIVE); + + setScreenLocked(false); + setScreenOn(true); + setChargingOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_ACTIVE); + + mConstants.WAIT_FOR_UNLOCK = true; + setScreenLocked(false); + setScreenOn(true); + setChargingOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_ACTIVE); } @Test @@ -1307,7 +1342,7 @@ public class DeviceIdleControllerTest { } @Test - public void testbecomeActiveLocked_deep() { + public void testBecomeActiveLocked_deep() { // becomeActiveLocked should put everything into ACTIVE. enterDeepState(STATE_ACTIVE); @@ -1344,7 +1379,7 @@ public class DeviceIdleControllerTest { } @Test - public void testbecomeActiveLocked_light() { + public void testBecomeActiveLocked_light() { // becomeActiveLocked should put everything into ACTIVE. enterLightState(LIGHT_STATE_ACTIVE); @@ -1376,6 +1411,163 @@ public class DeviceIdleControllerTest { verifyLightStateConditions(LIGHT_STATE_ACTIVE); } + /** Test based on b/119058625. */ + @Test + public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOn_ScreenThenMotion() { + mConstants.WAIT_FOR_UNLOCK = true; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + mDeviceIdleController.keyguardShowingLocked(true); + setScreenOn(true); + // With WAIT_FOR_UNLOCK = true and the screen locked, turning the screen on by itself + // shouldn't bring the device out of deep IDLE. + verifyStateConditions(STATE_IDLE); + mDeviceIdleController.handleMotionDetectedLocked(1000, "test"); + // Motion should bring the device out of Doze. Since the screen is still locked (albeit + // on), the states should go back into INACTIVE. + verifyStateConditions(STATE_INACTIVE); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + verify(mAlarmManager).cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + verify(mDeviceIdleController).scheduleReportActiveLocked(anyString(), anyInt()); + } + + /** Test based on b/119058625. */ + @Test + public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOff_ScreenThenMotion() { + mConstants.WAIT_FOR_UNLOCK = true; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + mDeviceIdleController.keyguardShowingLocked(false); + setScreenOn(true); + // With WAIT_FOR_UNLOCK = true and the screen unlocked, turning the screen on by itself + // should bring the device out of deep IDLE. + verifyStateConditions(STATE_ACTIVE); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + verify(mAlarmManager).cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + verify(mDeviceIdleController).scheduleReportActiveLocked(anyString(), anyInt()); + } + + /** Test based on b/119058625. */ + @Test + public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOn_MotionThenScreen() { + mConstants.WAIT_FOR_UNLOCK = true; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + InOrder alarmManagerInOrder = inOrder(mAlarmManager); + InOrder controllerInOrder = inOrder(mDeviceIdleController); + + mDeviceIdleController.keyguardShowingLocked(true); + mDeviceIdleController.handleMotionDetectedLocked(1000, "test"); + // The screen is still off, so motion should result in the INACTIVE state. + verifyStateConditions(STATE_INACTIVE); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + + setScreenOn(true); + // With WAIT_FOR_UNLOCK = true and the screen locked, turning the screen on by itself + // shouldn't bring the device all the way to ACTIVE. + verifyStateConditions(STATE_INACTIVE); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + alarmManagerInOrder.verify(mAlarmManager, never()).cancel( + eq(mDeviceIdleController.mDeepAlarmListener)); + + // User finally unlocks the device. Device should be fully active. + mDeviceIdleController.keyguardShowingLocked(false); + verifyStateConditions(STATE_ACTIVE); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + } + + /** Test based on b/119058625. */ + @Test + public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOff_MotionThenScreen() { + mConstants.WAIT_FOR_UNLOCK = true; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + InOrder alarmManagerInOrder = inOrder(mAlarmManager); + InOrder controllerInOrder = inOrder(mDeviceIdleController); + + mDeviceIdleController.keyguardShowingLocked(false); + mDeviceIdleController.handleMotionDetectedLocked(1000, "test"); + // The screen is still off, so motion should result in the INACTIVE state. + verifyStateConditions(STATE_INACTIVE); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + + setScreenOn(true); + // With WAIT_FOR_UNLOCK = true and the screen unlocked, turning the screen on by itself + // should bring the device out of deep IDLE. + verifyStateConditions(STATE_ACTIVE); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + } + + @Test + public void testExitNotifiesDependencies_WaitForUnlockOff_Screen() { + mConstants.WAIT_FOR_UNLOCK = false; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + setScreenOn(true); + // With WAIT_FOR_UNLOCK = false and the screen locked, turning the screen on by itself + // should bring the device out of deep IDLE. + verifyStateConditions(STATE_ACTIVE); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + verify(mAlarmManager).cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + verify(mDeviceIdleController).scheduleReportActiveLocked(anyString(), anyInt()); + } + + @Test + public void testExitNotifiesDependencies_WaitForUnlockOff_MotionThenScreen() { + mConstants.WAIT_FOR_UNLOCK = false; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + InOrder alarmManagerInOrder = inOrder(mAlarmManager); + InOrder controllerInOrder = inOrder(mDeviceIdleController); + + mDeviceIdleController.handleMotionDetectedLocked(1000, "test"); + // The screen is still off, so motion should result in the INACTIVE state. + verifyStateConditions(STATE_INACTIVE); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + + setScreenOn(true); + // With WAIT_FOR_UNLOCK = false and the screen locked, turning the screen on by itself + // should bring the device out of deep IDLE. + verifyStateConditions(STATE_ACTIVE); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + } + @Test public void testStepToIdleMode() { float delta = mDeviceIdleController.MIN_PRE_IDLE_FACTOR_CHANGE; @@ -1508,6 +1700,10 @@ public class DeviceIdleControllerTest { mDeviceIdleController.updateChargingLocked(on); } + private void setScreenLocked(boolean locked) { + mDeviceIdleController.keyguardShowingLocked(locked); + } + private void setScreenOn(boolean on) { doReturn(on).when(mPowerManager).isInteractive(); mDeviceIdleController.updateInteractivityLocked(); @@ -1549,7 +1745,8 @@ public class DeviceIdleControllerTest { assertFalse(mDeviceIdleController.mMotionListener.isActive()); assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; case STATE_IDLE_PENDING: assertEquals( @@ -1557,7 +1754,8 @@ public class DeviceIdleControllerTest { mDeviceIdleController.mMotionListener.isActive()); assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; case STATE_SENSING: assertEquals( @@ -1567,14 +1765,16 @@ public class DeviceIdleControllerTest { mDeviceIdleController.hasMotionSensor(), mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; case STATE_LOCATING: assertEquals( mDeviceIdleController.hasMotionSensor(), mDeviceIdleController.mMotionListener.isActive()); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; case STATE_IDLE: if (mDeviceIdleController.hasMotionSensor()) { @@ -1584,7 +1784,8 @@ public class DeviceIdleControllerTest { } assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); // Light state should be OVERRIDE at this point. verifyLightStateConditions(LIGHT_STATE_OVERRIDE); break; @@ -1596,14 +1797,16 @@ public class DeviceIdleControllerTest { } assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; case STATE_QUICK_DOZE_DELAY: // If quick doze is enabled, the motion listener should NOT be active. assertFalse(mDeviceIdleController.mMotionListener.isActive()); assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; default: fail("Conditions for " + stateToString(expectedState) + " unknown."); @@ -1632,7 +1835,8 @@ public class DeviceIdleControllerTest { case LIGHT_STATE_IDLE_MAINTENANCE: case LIGHT_STATE_OVERRIDE: assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; default: fail("Conditions for " + lightStateToString(expectedLightState) + " unknown."); diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index dc31c0f7ebbb..fce7599d0b59 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -66,6 +66,8 @@ <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/> <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/> <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index a847b6ab105c..2ce4c54a932b 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -65,7 +65,8 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi public OwnersTestable(MockSystemServices services) { super(services.userManager, services.userManagerInternal, - services.packageManagerInternal, new MockInjector(services)); + services.packageManagerInternal, services.activityTaskManagerInternal, + new MockInjector(services)); } static class MockInjector extends Injector { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 9ac91ddba5ec..de782a52eb53 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -5199,7 +5199,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testGetPasswordComplexity_currentUserNoPassword() { when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(true); - mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY); + mServiceContext.permissions.add(permission.REQUEST_SCREEN_LOCK_COMPLEXITY); when(getServices().userManager.getCredentialOwnerProfile(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(DpmMockContext.CALLER_USER_HANDLE); @@ -5209,7 +5209,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testGetPasswordComplexity_currentUserHasPassword() { when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(true); - mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY); + mServiceContext.permissions.add(permission.REQUEST_SCREEN_LOCK_COMPLEXITY); when(getServices().userManager.getCredentialOwnerProfile(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(DpmMockContext.CALLER_USER_HANDLE); dpms.mUserPasswordMetrics.put( @@ -5222,7 +5222,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testGetPasswordComplexity_unifiedChallengeReturnsParentUserPassword() { when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(true); - mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY); + mServiceContext.permissions.add(permission.REQUEST_SCREEN_LOCK_COMPLEXITY); UserInfo parentUser = new UserInfo(); parentUser.id = DpmMockContext.CALLER_USER_HANDLE + 10; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 4724f1cdd324..8f0aeea3dbf8 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -64,6 +64,7 @@ import android.view.IWindowManager; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.widget.LockPatternUtils; import com.android.server.net.NetworkPolicyManagerInternal; +import com.android.server.wm.ActivityTaskManagerInternal; import java.io.File; import java.io.IOException; @@ -94,6 +95,7 @@ public class MockSystemServices { public final IActivityManager iactivityManager; public final IActivityTaskManager iactivityTaskManager; public ActivityManagerInternal activityManagerInternal; + public ActivityTaskManagerInternal activityTaskManagerInternal; public final IPackageManager ipackageManager; public final IBackupManager ibackupManager; public final IAudioService iaudioService; @@ -133,6 +135,7 @@ public class MockSystemServices { iactivityManager = mock(IActivityManager.class); iactivityTaskManager = mock(IActivityTaskManager.class); activityManagerInternal = mock(ActivityManagerInternal.class); + activityTaskManagerInternal = mock(ActivityTaskManagerInternal.class); ipackageManager = mock(IPackageManager.class); ibackupManager = mock(IBackupManager.class); iaudioService = mock(IAudioService.class); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 8d0365b534b5..95043810128a 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -32,6 +32,7 @@ import static org.mockito.Mockito.verify; import android.Manifest.permission; import android.app.ActivityManager; +import android.app.Person; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ShortcutInfo; @@ -890,6 +891,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setText("text") .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) + .setPerson(makePerson("person", "personKey", "personUri")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) @@ -901,6 +903,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setTitle("x") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setPersons(list(makePerson("person1", "personKey1", "personUri1"), + makePerson("person2", "personKey2", "personUri2")).toArray(new Person[2])) .setRank(456) .build(); sorig2.setTimestamp(mInjectedCurrentTimeMillis); @@ -936,6 +940,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(1, si.getPersons().length); + assertEquals("person", si.getPersons()[0].getName()); + assertEquals("personKey", si.getPersons()[0].getKey()); + assertEquals("personUri", si.getPersons()[0].getUri()); assertEquals(0, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); @@ -949,6 +957,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { // to test it. si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10); assertEquals(1, si.getRank()); + assertEquals(2, si.getPersons().length); + assertEquals("personUri2", si.getPersons()[1].getUri()); dumpUserFile(USER_10); } @@ -1114,6 +1124,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setPerson(makePerson("person", "personKey", "personUri")) .setRank(123) .setExtras(pb) .build(); @@ -1150,6 +1161,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(0, si.getPersons().length); // Don't backup the persons field assertEquals(0, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 2de4ae02828c..23bae8881aa1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; + import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.anyInt; @@ -35,9 +36,7 @@ import android.content.Intent; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.SparseIntArray; -import android.util.proto.ProtoOutputStream; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 4073ff106592..8c36905d8422 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -183,11 +183,14 @@ public class ActivityRecordTests extends ActivityTestsBase { .thenReturn(navBarPosition); mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds); mActivity.info.maxAspectRatio = aspectRatio; - mActivity.ensureActivityConfiguration( - 0 /* globalChanges */, false /* preserveWindow */); + ensureActivityConfiguration(); assertEquals(expectedActivityBounds, mActivity.getBounds()); } + private void ensureActivityConfiguration() { + mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); + } + @Test public void testCanBeLaunchedOnDisplay() { mService.mSupportsMultiWindow = true; @@ -281,7 +284,7 @@ public class ActivityRecordTests extends ActivityTestsBase { mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; - mActivity.ensureActivityConfiguration(0, false, false); + ensureActivityConfiguration(); assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE, mActivity.mRelaunchReason); @@ -305,7 +308,7 @@ public class ActivityRecordTests extends ActivityTestsBase { mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; - mActivity.ensureActivityConfiguration(0, false, false); + ensureActivityConfiguration(); assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE, mActivity.mRelaunchReason); @@ -327,7 +330,7 @@ public class ActivityRecordTests extends ActivityTestsBase { mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; - mActivity.ensureActivityConfiguration(0, false, false); + ensureActivityConfiguration(); assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_NONE, mActivity.mRelaunchReason); @@ -433,4 +436,46 @@ public class ActivityRecordTests extends ActivityTestsBase { stack.getDisplay().removeChild(stack); } } + + @Test + public void testFixedScreenConfigurationWhenMovingToDisplay() { + // Initialize different bounds on a new display. + final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP); + newDisplay.setBounds(0, 0, 1000, 2000); + newDisplay.getConfiguration().densityDpi = 300; + + mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds()); + mTask.getConfiguration().densityDpi = 200; + when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn( + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE; + mActivity.info.maxAspectRatio = 1.5f; + ensureActivityConfiguration(); + final Rect originalBounds = new Rect(mActivity.getBounds()); + final int originalDpi = mActivity.getConfiguration().densityDpi; + + // Move the non-resizable activity to the new display. + mStack.reparent(newDisplay, true /* onTop */, false /* displayRemoved */); + ensureActivityConfiguration(); + + assertEquals(originalBounds, mActivity.getBounds()); + assertEquals(originalDpi, mActivity.getConfiguration().densityDpi); + } + + @Test + public void testFixedScreenBoundsWhenDisplaySizeChanged() { + when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn( + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds()); + mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE; + ensureActivityConfiguration(); + final Rect originalBounds = new Rect(mActivity.getBounds()); + + // Change the size of current display. + mStack.getDisplay().setBounds(0, 0, 1000, 2000); + ensureActivityConfiguration(); + + assertEquals(originalBounds, mActivity.getBounds()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 60f1ae26f5dd..392b0106c8e5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -61,7 +61,6 @@ import static org.mockito.ArgumentMatchers.eq; import android.app.ActivityOptions; import android.app.IApplicationThread; -import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -643,7 +642,7 @@ public class ActivityStarterTests extends ActivityTestsBase { UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, false, false, false, true, false); runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_callingPackageIsDeviceOwner_notAborted", false, + "disallowed_callingPackageNameIsDeviceOwner_notAborted", false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, false, false, false, false, true); @@ -655,7 +654,7 @@ public class ActivityStarterTests extends ActivityTestsBase { boolean hasForegroundActivities, boolean callerIsRecents, boolean callerIsTempWhitelisted, boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges, - boolean isCallingPackageDeviceOwner) { + boolean isCallingPackageNameDeviceOwner) { // window visibility doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager.mRoot) .isAnyNonToastWindowVisibleForUid(callingUid); @@ -681,9 +680,8 @@ public class ActivityStarterTests extends ActivityTestsBase { // caller is instrumenting with background activity starts privileges callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges, callerIsInstrumentingWithBackgroundActivityStartPrivileges); - // caller is device owner - DevicePolicyManager dpmMock = mService.getDevicePolicyManager(); - doReturn(isCallingPackageDeviceOwner).when(dpmMock).isDeviceOwnerApp(any()); + // calling package name is whitelisted + doReturn(isCallingPackageNameDeviceOwner).when(mService).isDeviceOwner(any()); final ActivityOptions options = spy(ActivityOptions.makeBasic()); ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK) diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 21a4e8417bab..abc0bd64c0e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -42,7 +42,6 @@ import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.IApplicationThread; -import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -437,7 +436,6 @@ class ActivityTestsBase { spyOn(getLifecycleManager()); spyOn(getLockTaskController()); doReturn(mock(IPackageManager.class)).when(this).getPackageManager(); - doReturn(mock(DevicePolicyManager.class)).when(this).getDevicePolicyManager(); // allow background activity starts by default doReturn(true).when(this).isBackgroundActivityStartsEnabled(); doNothing().when(this).updateCpuStats(); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index bc62de12373d..cd1320986972 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -44,6 +44,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; +import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -62,7 +63,6 @@ import org.junit.Test; * Build/Install/Run: * atest FrameworksServicesTests:AppWindowTokenTests */ -@FlakyTest(bugId = 68267650) @SmallTest @Presubmit public class AppWindowTokenTests extends WindowTestsBase { @@ -79,6 +79,7 @@ public class AppWindowTokenTests extends WindowTestsBase { mTask = createTaskInStack(mStack, 0 /* userId */); mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + mToken.mSkipOnParentChanged = false; mTask.addChild(mToken, 0); } @@ -141,6 +142,7 @@ public class AppWindowTokenTests extends WindowTestsBase { mToken.removeImmediately(); } + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") @Test public void testLandscapeSeascapeRotationByApp() { // Some plumbing to get the service ready for rotation updates. @@ -166,6 +168,8 @@ public class AppWindowTokenTests extends WindowTestsBase { mDisplayContent.updateOrientationFromAppTokens( mDisplayContent.getRequestedOverrideConfiguration(), null /* freezeThisOneIfNeeded */, false /* forceUpdate */); + // In this test, DC will not get config update. Set the waiting flag to false. + mDisplayContent.mWaitingForConfig = false; mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation()); assertTrue(appWindow.mResizeReported); @@ -211,6 +215,48 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test + public void testSizeCompatBounds() { + // The real surface transaction is unnecessary. + mToken.setSkipPrepareSurfaces(true); + + final Rect fixedBounds = mToken.getRequestedOverrideConfiguration().windowConfiguration + .getBounds(); + fixedBounds.set(0, 0, 1200, 1600); + final Configuration newParentConfig = mTask.getConfiguration(); + + // Change the size of the container to two times smaller with insets. + newParentConfig.windowConfiguration.setAppBounds(200, 0, 800, 800); + final Rect containerAppBounds = newParentConfig.windowConfiguration.getAppBounds(); + final Rect containerBounds = newParentConfig.windowConfiguration.getBounds(); + containerBounds.set(0, 0, 600, 800); + mToken.onConfigurationChanged(newParentConfig); + + assertTrue(mToken.inSizeCompatMode()); + assertEquals(containerAppBounds, mToken.getBounds()); + assertEquals((float) containerAppBounds.width() / fixedBounds.width(), + mToken.getSizeCompatScale(), 0.0001f /* delta */); + + // Change the width of the container to two times bigger. + containerAppBounds.set(0, 0, 2400, 1600); + containerBounds.set(containerAppBounds); + mToken.onConfigurationChanged(newParentConfig); + + assertTrue(mToken.inSizeCompatMode()); + // Don't scale up, so the bounds keep the same as the fixed width. + assertEquals(fixedBounds.width(), mToken.getBounds().width()); + // Assert the position is horizontal center. + assertEquals((containerAppBounds.width() - fixedBounds.width()) / 2, + mToken.getBounds().left); + assertEquals(1f, mToken.getSizeCompatScale(), 0.0001f /* delta */); + + // Change the width of the container to fit the fixed bounds. + containerBounds.set(0, 0, 1200, 2000); + mToken.onConfigurationChanged(newParentConfig); + // Assert don't use fixed bounds because the region is enough. + assertFalse(mToken.inSizeCompatMode()); + } + + @Test @Presubmit public void testGetOrientation() { mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); @@ -300,6 +346,7 @@ public class AppWindowTokenTests extends WindowTestsBase { assertNoStartingWindow(mToken); } + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") @Test public void testAddRemoveRace() { // There was once a race condition between adding and removing starting windows @@ -384,6 +431,7 @@ public class AppWindowTokenTests extends WindowTestsBase { // bottom one. tokenTop.setVisibility(false, false); tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded(); + waitUntilHandlersIdle(); // Assert that the bottom window now has the starting window. assertNoStartingWindow(tokenTop); diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java index 2f90baaceb99..3f83caea613a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java @@ -71,7 +71,6 @@ import java.util.concurrent.TimeUnit; * atest WmTests:AssistDataRequesterTest */ @MediumTest -@FlakyTest(bugId = 113616538) public class AssistDataRequesterTest extends ActivityTestsBase { private static final String TAG = AssistDataRequesterTest.class.getSimpleName(); @@ -154,6 +153,7 @@ public class AssistDataRequesterTest extends ActivityTestsBase { .checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString()); } + @FlakyTest(bugId = 124088319) @Test public void testRequestData() throws Exception { setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, @@ -174,6 +174,7 @@ public class AssistDataRequesterTest extends ActivityTestsBase { assertReceivedDataCount(0, 0, 0, 0); } + @FlakyTest(bugId = 124088319) @Test public void testCurrentAppDisallow_expectNullCallbacks() throws Exception { setupMocks(!CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, @@ -184,6 +185,7 @@ public class AssistDataRequesterTest extends ActivityTestsBase { assertReceivedDataCount(0, 1, 0, 1); } + @FlakyTest(bugId = 124088319) @Test public void testProcessPendingData() throws Exception { setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, @@ -241,6 +243,7 @@ public class AssistDataRequesterTest extends ActivityTestsBase { assertReceivedDataCount(0, 1, 0, 1); } + @FlakyTest(bugId = 124088319) @Test public void testDisallowAssistContextExtras_expectNullDataCallbacks() throws Exception { setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, @@ -254,6 +257,7 @@ public class AssistDataRequesterTest extends ActivityTestsBase { assertReceivedDataCount(0, 1, 0, 1); } + @FlakyTest(bugId = 124088319) @Test public void testNoFetchScreenshots_expectNoScreenshotCallbacks() throws Exception { setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, @@ -264,6 +268,7 @@ public class AssistDataRequesterTest extends ActivityTestsBase { assertReceivedDataCount(5, 5, 0, 0); } + @FlakyTest(bugId = 124088319) @Test public void testDisallowAssistScreenshot_expectNullScreenshotCallback() throws Exception { setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index 0c363de36328..a0546d7623e7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -19,11 +19,11 @@ package com.android.server.wm; import static android.view.InsetsState.TYPE_TOP_BAR; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import android.graphics.Insets; import android.graphics.Rect; @@ -31,8 +31,6 @@ import android.platform.test.annotations.Presubmit; import android.view.InsetsSource; import android.view.InsetsState; -import org.junit.Before; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 11526a85aafb..bc62e8c5ab24 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -35,7 +35,6 @@ import androidx.test.filters.SmallTest; import org.junit.Test; @SmallTest -@FlakyTest(detail = "Promote once confirmed non-flaky") @Presubmit public class InsetsStateControllerTest extends WindowTestsBase { @@ -48,6 +47,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR)); } + @FlakyTest(bugId = 69229402) @Test public void testStripForDispatch_own() { final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); @@ -57,6 +57,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertEquals(new InsetsState(), getController().getInsetsForDispatch(topBar)); } + @FlakyTest(bugId = 124088319) @Test public void testStripForDispatch_navBar() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); @@ -68,6 +69,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertEquals(new InsetsState(), getController().getInsetsForDispatch(navBar)); } + @FlakyTest(bugId = 124088319) @Test public void testBarControllingWinChanged() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); @@ -80,6 +82,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertEquals(2, controls.length); } + @FlakyTest(bugId = 124088319) @Test public void testControlRevoked() { final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); @@ -91,6 +94,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertNull(getController().getControlsForDispatch(app)); } + @FlakyTest(bugId = 124088319) @Test public void testControlRevoked_animation() { final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); diff --git a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java index 86bf3dbb6973..b769fcecc469 100644 --- a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java @@ -24,7 +24,6 @@ import android.app.ActivityOptions; import android.platform.test.annotations.Presubmit; import android.view.RemoteAnimationAdapter; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.testutils.OffsettableClock; diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java index c3d2f33b17dd..83274401f13d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java @@ -28,7 +28,6 @@ import static org.junit.Assert.assertTrue; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; -import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import org.junit.After; diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java index dad6c952d7ab..71118521acf5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertEquals; import android.app.ActivityOptions; import android.platform.test.annotations.Presubmit; -import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import org.junit.Test; diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java index dfdbf323365f..6cce9f088ee4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java @@ -167,7 +167,6 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase { verify(mMockAnimationSpec, atLeastOnce()).apply(any(), any(), eq(0L)); } - @FlakyTest(bugId = 74780584) @Test public void testDeferStartingAnimations() throws Exception { mSurfaceAnimationRunner.deferStartingAnimations(); diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java index 8c32e8cea93c..88ac96d74df6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java @@ -34,7 +34,6 @@ import android.view.SurfaceControl.Builder; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.wm.SurfaceAnimator.Animatable; @@ -51,7 +50,7 @@ import org.mockito.MockitoAnnotations; * Test class for {@link SurfaceAnimatorTest}. * * Build/Install/Run: - * atest FrameworksServicesTests:SurfaceAnimatorTest + * atest WmTests:SurfaceAnimatorTest */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java index 12ed3c28161f..9dfeadf878e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java @@ -29,8 +29,6 @@ import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.util.SparseBooleanArray; -import androidx.test.filters.FlakyTest; - import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java index 8c6ac23c9202..1e58e413dd1b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -43,7 +43,6 @@ import org.junit.Test; * Build/Install/Run: * atest FrameworksServicesTests:TaskPositioningControllerTests */ -@FlakyTest(bugId = 117924387) @SmallTest @Presubmit public class TaskPositioningControllerTests extends WindowTestsBase { @@ -90,6 +89,7 @@ public class TaskPositioningControllerTests extends WindowTestsBase { assertNull(mTarget.getDragWindowHandleLocked()); } + @FlakyTest(bugId = 69229402) @Test public void testHandleTapOutsideTask() { synchronized (mWm.mGlobalLock) { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java index 4a734e5256f6..dcca3167c752 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java @@ -35,9 +35,11 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; import android.support.test.uiautomator.UiDevice; import android.text.TextUtils; +import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import com.android.internal.annotations.GuardedBy; @@ -76,6 +78,7 @@ public class TaskStackChangedListenerTest { } @Test + @Presubmit public void testTaskStackChanged_afterFinish() throws Exception { registerTaskStackChangedListener(new TaskStackListener() { @Override @@ -87,7 +90,8 @@ public class TaskStackChangedListenerTest { }); Context context = getInstrumentation().getContext(); - context.startActivity(new Intent(context, ActivityA.class)); + context.startActivity( + new Intent(context, ActivityA.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); UiDevice.getInstance(getInstrumentation()).waitForIdle(); synchronized (sLock) { assertTrue(sTaskStackChangedCalled); @@ -96,6 +100,7 @@ public class TaskStackChangedListenerTest { } @Test + @FlakyTest(bugId = 119893767) public void testTaskDescriptionChanged() throws Exception { final Object[] params = new Object[2]; final CountDownLatch latch = new CountDownLatch(1); @@ -124,6 +129,7 @@ public class TaskStackChangedListenerTest { } @Test + @FlakyTest(bugId = 119893767) public void testActivityRequestedOrientationChanged() throws Exception { final int[] params = new int[2]; final CountDownLatch latch = new CountDownLatch(1); @@ -146,6 +152,7 @@ public class TaskStackChangedListenerTest { * Tests for onTaskCreated, onTaskMovedToFront, onTaskRemoved and onTaskRemovalStarted. */ @Test + @FlakyTest(bugId = 119893767) public void testTaskChangeCallBacks() throws Exception { final Object[] params = new Object[2]; final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1); @@ -221,7 +228,8 @@ public class TaskStackChangedListenerTest { final ActivityMonitor monitor = new ActivityMonitor(activityClass.getName(), null, false); getInstrumentation().addMonitor(monitor); final Context context = getInstrumentation().getContext(); - context.startActivity(new Intent(context, activityClass)); + context.startActivity( + new Intent(context, activityClass).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); final TestActivity activity = (TestActivity) monitor.waitForActivityWithTimeout(1000); if (activity == null) { throw new RuntimeException("Timed out waiting for Activity"); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java b/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java index 1c1fe29bd7cc..e540b3a9db89 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java @@ -61,7 +61,7 @@ import com.android.server.policy.WindowManagerPolicy; import org.mockito.invocation.InvocationOnMock; import org.mockito.quality.Strictness; -import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; /** * A Test utility class to create a mock {@link WindowManagerService} instance for tests. @@ -71,6 +71,8 @@ class TestSystemServices { private static WindowManagerService sService; private static TestWindowManagerPolicy sPolicy; + static AtomicBoolean sCurrentMessagesProcessed = new AtomicBoolean(false); + static void setUpWindowManagerService() { sMockitoSession = mockitoSession() .spyStatic(LockGuard.class) @@ -195,21 +197,23 @@ class TestSystemServices { } private static void waitHandlerIdle(Handler handler) { - if (!handler.hasMessagesOrCallbacks()) { - return; - } - final CountDownLatch latch = new CountDownLatch(1); - // Wait for delayed messages are processed. - handler.getLooper().getQueue().addIdleHandler(() -> { - if (handler.hasMessagesOrCallbacks()) { - return true; // keep idle handler. + synchronized (sCurrentMessagesProcessed) { + // Add a message to the handler queue and make sure it is fully processed before we move + // on. This makes sure all previous messages in the handler are fully processed vs. just + // popping them from the message queue. + sCurrentMessagesProcessed.set(false); + handler.post(() -> { + synchronized (sCurrentMessagesProcessed) { + sCurrentMessagesProcessed.set(true); + sCurrentMessagesProcessed.notifyAll(); + } + }); + while (!sCurrentMessagesProcessed.get()) { + try { + sCurrentMessagesProcessed.wait(); + } catch (InterruptedException e) { + } } - latch.countDown(); - return false; // remove idle handler. - }); - try { - latch.await(); - } catch (InterruptedException e) { } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index bfb9193551f2..849772a4a26d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -156,7 +156,8 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags) { return 0; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java index 64ceb1bf8dff..d6608f1cabdc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java @@ -49,7 +49,7 @@ import org.junit.Test; * Test class to for {@link android.app.WindowConfiguration}. * * Build/Install/Run: - * atest FrameworksServicesTests:WindowConfigurationTests + * atest WmTests:WindowConfigurationTests */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java index af8ccc981bae..fc7863524240 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java @@ -26,7 +26,6 @@ import static org.junit.Assert.assertTrue; import android.content.res.Configuration; import android.platform.test.annotations.Presubmit; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Test; @@ -35,7 +34,7 @@ import org.junit.Test; * Test class for {@link WindowContainerController}. * * Build/Install/Run: - * atest FrameworksServicesTests:WindowContainerControllerTests + * atest WmTests:WindowContainerControllerTests */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index a9a76c2e9506..b93c994f4d8f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -47,7 +47,6 @@ import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; import android.view.SurfaceSession; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Test; @@ -59,11 +58,10 @@ import java.util.Comparator; * Test class for {@link WindowContainer}. * * Build/Install/Run: - * atest FrameworksServicesTests:WindowContainerTests + * atest WmTests:WindowContainerTests */ @SmallTest @Presubmit -@FlakyTest(bugId = 74078662) public class WindowContainerTests extends WindowTestsBase { @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index 0a4a8a43fbc5..fb30f8b2b107 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -449,8 +449,7 @@ public class WindowFrameTests extends WindowTestsBase { // Now simulate switch to fullscreen for letterboxed app. final int xInset = logicalWidth / 10; - final int yInset = logicalWidth / 10; - final Rect cf = new Rect(xInset, yInset, logicalWidth - xInset, logicalHeight - yInset); + final Rect cf = new Rect(xInset, 0, logicalWidth - xInset, logicalHeight); Configuration config = new Configuration(w.mAppToken.getRequestedOverrideConfiguration()); config.windowConfiguration.setBounds(cf); w.mAppToken.onRequestedOverrideConfigurationChanged(config); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java index a4948899c855..114eac911bd7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java @@ -151,6 +151,7 @@ public class WindowTestUtils { /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ public static class TestAppWindowToken extends AppWindowToken { boolean mOnTop = false; + private boolean mSkipPrepareSurfaces; private Transaction mPendingTransactionOverride; boolean mSkipOnParentChanged = true; @@ -213,6 +214,17 @@ public class WindowTestUtils { return mOnTop; } + @Override + void prepareSurfaces() { + if (!mSkipPrepareSurfaces) { + super.prepareSurfaces(); + } + } + + void setSkipPrepareSurfaces(boolean ignore) { + mSkipPrepareSurfaces = ignore; + } + void setPendingTransaction(Transaction transaction) { mPendingTransactionOverride = transaction; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index d55688665f70..4cdbea0bfb56 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -28,7 +28,6 @@ import static org.junit.Assert.assertTrue; import android.platform.test.annotations.Presubmit; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Test; @@ -37,7 +36,7 @@ import org.junit.Test; * Tests for the {@link WindowToken} class. * * Build/Install/Run: - * atest FrameworksServicesTests:WindowTokenTests + * atest WmTests:WindowTokenTests */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java index 2970c211e29c..3c6e2405adff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java @@ -58,7 +58,7 @@ import java.nio.charset.StandardCharsets; * Test class for {@link WindowTracing}. * * Build/Install/Run: - * atest FrameworksServicesTests:WindowTracingTest + * atest WmTests:WindowTracingTest */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index 3dcea75b8ae5..f3b8a6265eb3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -51,9 +51,8 @@ import java.util.LinkedList; * Tests for the {@link DisplayContent#assignChildLayers(SurfaceControl.Transaction)} method. * * Build/Install/Run: - * atest FrameworksServicesTests:ZOrderingTests + * atest WmTests:ZOrderingTests */ -@FlakyTest(bugId = 74078662) @SmallTest @Presubmit public class ZOrderingTests extends WindowTestsBase { @@ -207,6 +206,7 @@ public class ZOrderingTests extends WindowTestsBase { return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name); } + @FlakyTest(bugId = 124088319) @Test public void testAssignWindowLayers_ForImeWithNoTarget() { mDisplayContent.mInputMethodTarget = null; @@ -224,6 +224,7 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mImeDialogWindow, mImeWindow); } + @FlakyTest(bugId = 124088319) @Test public void testAssignWindowLayers_ForImeWithAppTarget() { final WindowState imeAppTarget = createWindow("imeAppTarget"); @@ -243,6 +244,7 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mImeDialogWindow, mImeWindow); } + @FlakyTest(bugId = 124088319) @Test public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() { final WindowState imeAppTarget = createWindow("imeAppTarget"); @@ -269,6 +271,7 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mImeDialogWindow, mImeWindow); } + @FlakyTest(bugId = 124088319) @Test public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() { final WindowState appBelowImeTarget = createWindow("appBelowImeTarget"); @@ -292,6 +295,7 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mImeDialogWindow, mImeWindow); } + @FlakyTest(bugId = 124088319) @Test public void testAssignWindowLayers_ForImeNonAppImeTarget() { final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY, @@ -319,6 +323,7 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mImeDialogWindow, mImeWindow); } + @FlakyTest(bugId = 124088319) @Test public void testAssignWindowLayers_ForStatusBarImeTarget() { mDisplayContent.mInputMethodTarget = mStatusBarWindow; @@ -333,6 +338,7 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mImeDialogWindow, mImeWindow); } + @FlakyTest(bugId = 124088319) @Test public void testStackLayers() { final WindowState anyWindow1 = createWindow("anyWindow"); @@ -398,6 +404,7 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mediaOverlayChild, child); } + @FlakyTest(bugId = 124088319) @Test public void testDockedDividerPosition() { final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java index aac0956d4339..e43b2b715321 100644 --- a/telecomm/java/android/telecom/DefaultDialerManager.java +++ b/telecomm/java/android/telecom/DefaultDialerManager.java @@ -22,7 +22,6 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; import android.net.Uri; import android.os.AsyncTask; import android.os.Binder; @@ -163,9 +162,7 @@ public class DefaultDialerManager { final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL); dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null)); - packageNames = filterByIntent(context, packageNames, dialIntentWithTelScheme, userId); - packageNames = requireInCallService(packageNames, userId, context); - return packageNames; + return filterByIntent(context, packageNames, dialIntentWithTelScheme, userId); } public static List<String> getInstalledDialerApplications(Context context) { @@ -223,35 +220,6 @@ public class DefaultDialerManager { return result; } - private static List<String> requireInCallService(List<String> packageNames, int userId, - Context context) { - if (packageNames == null || packageNames.isEmpty()) { - return new ArrayList<>(); - } - - final Intent intent = new Intent(InCallService.SERVICE_INTERFACE); - final List<ResolveInfo> resolveInfoList = context.getPackageManager() - .queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, userId); - final List<String> result = new ArrayList<>(); - final int length = resolveInfoList.size(); - for (int i = 0; i < length; i++) { - final ServiceInfo info = resolveInfoList.get(i).serviceInfo; - if (info == null || info.metaData == null) { - continue; - } - if (!info.metaData.getBoolean(TelecomManager.METADATA_IN_CALL_SERVICE_UI)) { - continue; - } - if (info.metaData.getBoolean(TelecomManager.METADATA_IN_CALL_SERVICE_CAR_MODE_UI)) { - continue; - } - if (packageNames.contains(info.packageName) && !result.contains(info.packageName)) { - result.add(info.packageName); - } - } - - return result; - } private static TelecomManager getTelecomManager(Context context) { return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index 4539ab3dc310..a1c32b5f80a3 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -2717,6 +2717,41 @@ public final class Telephony { Uri RCS_EVENT_QUERY_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, RCS_EVENT_QUERY_URI_PATH); } + + /** + * Allows RCS specific canonical address handling. + */ + interface RcsCanonicalAddressHelper { + /** + * Returns the canonical address ID for a canonical address, if now row exists, this + * will add a row and return its ID. This helper works against the same table used by + * the SMS and MMS threads, but is accessible only by the phone process for use by RCS + * message storage. + * + * @throws IllegalArgumentException if unable to retrieve or create the canonical + * address entry. + */ + static long getOrCreateCanonicalAddressId( + ContentResolver contentResolver, String canonicalAddress) { + + Uri.Builder uriBuilder = CONTENT_AND_AUTHORITY.buildUpon(); + uriBuilder.appendPath("canonical-address"); + uriBuilder.appendQueryParameter("address", canonicalAddress); + Uri uri = uriBuilder.build(); + + try (Cursor cursor = contentResolver.query(uri, null, null, null)) { + if (cursor != null && cursor.moveToFirst()) { + return cursor.getLong(cursor.getColumnIndex(CanonicalAddressesColumns._ID)); + } else { + Rlog.e(TAG, "getOrCreateCanonicalAddressId returned no rows"); + } + } + + Rlog.e(TAG, "getOrCreateCanonicalAddressId failed"); + throw new IllegalArgumentException( + "Unable to find or allocate a canonical address ID"); + } + } } /** diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 0c63b60b030d..1b7228ae9a33 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2424,6 +2424,14 @@ public class CarrierConfigManager { "5g_icon_configuration_string"; /** + * Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable + * this feature. + * @hide + */ + public static final String KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL = + "ascii_7_bit_support_for_long_message_bool"; + + /** * Controls RSRP threshold at which OpportunisticNetworkService will decide whether * the opportunistic network is good enough for internet data. */ @@ -2961,6 +2969,7 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */); sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING, "connected_mmwave:None,connected:5G,not_restricted:None,restricted:None"); + sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false); /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108); /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */ diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java index 37847aef9167..d47b55ca4372 100644 --- a/telephony/java/android/telephony/CarrierRestrictionRules.java +++ b/telephony/java/android/telephony/CarrierRestrictionRules.java @@ -27,6 +27,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Contains the list of carrier restrictions. @@ -93,6 +94,9 @@ public final class CarrierRestrictionRules implements Parcelable { value = {CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED, CARRIER_RESTRICTION_DEFAULT_ALLOWED}) public @interface CarrierRestrictionDefault {} + /* Wild character for comparison */ + private static final char WILD_CHARACTER = '?'; + private List<CarrierIdentifier> mAllowedCarriers; private List<CarrierIdentifier> mExcludedCarriers; @CarrierRestrictionDefault @@ -166,6 +170,124 @@ public final class CarrierRestrictionRules implements Parcelable { } /** + * Tests an array of carriers with the carrier restriction configuration. The list of carrier + * ids passed as argument does not need to be the same as currently present in the device. + * + * @param carrierIds list of {@link CarrierIdentifier}, one for each SIM slot on the device + * @return a list of boolean with the same size as input, indicating if each + * {@link CarrierIdentifier} is allowed or not. + */ + public List<Boolean> isCarrierIdentifiersAllowed(@NonNull List<CarrierIdentifier> carrierIds) { + ArrayList<Boolean> result = new ArrayList<>(carrierIds.size()); + + // First calculate the result for each slot independently + for (int i = 0; i < carrierIds.size(); i++) { + boolean inAllowedList = isCarrierIdInList(carrierIds.get(i), mAllowedCarriers); + boolean inExcludedList = isCarrierIdInList(carrierIds.get(i), mExcludedCarriers); + if (mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED) { + result.add((inAllowedList && !inExcludedList) ? true : false); + } else { + result.add((inExcludedList && !inAllowedList) ? false : true); + } + } + // Apply the multi-slot policy, if needed. + if (mMultiSimPolicy == MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT) { + for (boolean b : result) { + if (b) { + result.replaceAll(x -> true); + break; + } + } + } + return result; + } + + /** + * Indicates if a certain carrier {@code id} is present inside a {@code list} + * + * @return true if the carrier {@code id} is present, false otherwise + */ + private static boolean isCarrierIdInList(CarrierIdentifier id, List<CarrierIdentifier> list) { + for (CarrierIdentifier listItem : list) { + // Compare MCC and MNC + if (!patternMatch(id.getMcc(), listItem.getMcc()) + || !patternMatch(id.getMnc(), listItem.getMnc())) { + continue; + } + + // Compare SPN. Comparison is on the complete strings, case insensitive and with wild + // characters. + String listItemValue = convertNullToEmpty(listItem.getSpn()); + String idValue = convertNullToEmpty(id.getSpn()); + if (!listItemValue.isEmpty()) { + if (!patternMatch(idValue, listItemValue)) { + continue; + } + } + + // The IMSI of the configuration can be shorter than actual IMSI in the SIM card. + listItemValue = convertNullToEmpty(listItem.getImsi()); + idValue = convertNullToEmpty(id.getImsi()); + if (!patternMatch( + idValue.substring(0, Math.min(idValue.length(), listItemValue.length())), + listItemValue)) { + continue; + } + + // The GID1 of the configuration can be shorter than actual GID1 in the SIM card. + listItemValue = convertNullToEmpty(listItem.getGid1()); + idValue = convertNullToEmpty(id.getGid1()); + if (!patternMatch( + idValue.substring(0, Math.min(idValue.length(), listItemValue.length())), + listItemValue)) { + continue; + } + + // The GID2 of the configuration can be shorter than actual GID2 in the SIM card. + listItemValue = convertNullToEmpty(listItem.getGid2()); + idValue = convertNullToEmpty(id.getGid2()); + if (!patternMatch( + idValue.substring(0, Math.min(idValue.length(), listItemValue.length())), + listItemValue)) { + continue; + } + + // Valid match was found in the list + return true; + } + return false; + } + + private static String convertNullToEmpty(String value) { + return Objects.toString(value, ""); + } + + /** + * Performs a case insensitive string comparison against a given pattern. The character '?' + * is used in the pattern as wild character in the comparison. The string must have the same + * length as the pattern. + * + * @param str string to match + * @param pattern string containing the pattern + * @return true in case of match, false otherwise + */ + private static boolean patternMatch(String str, String pattern) { + if (str.length() != pattern.length()) { + return false; + } + String lowerCaseStr = str.toLowerCase(); + String lowerCasePattern = pattern.toLowerCase(); + + for (int i = 0; i < lowerCasePattern.length(); i++) { + if (lowerCasePattern.charAt(i) != lowerCaseStr.charAt(i) + && lowerCasePattern.charAt(i) != WILD_CHARACTER) { + return false; + } + } + return true; + } + + /** * {@link Parcelable#writeToParcel} */ @Override diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index 6b1b84cd3458..856f08107fd7 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -30,6 +30,7 @@ public final class CellIdentityNr extends CellIdentity { private final int mNrArfcn; private final int mPci; private final int mTac; + private final long mNci; /** * @@ -44,11 +45,12 @@ public final class CellIdentityNr extends CellIdentity { * @hide */ public CellIdentityNr(int pci, int tac, int nrArfcn, String mccStr, String mncStr, - String alphal, String alphas) { + long nci, String alphal, String alphas) { super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas); mPci = pci; mTac = tac; mNrArfcn = nrArfcn; + mNci = nci; } /** @@ -62,7 +64,7 @@ public final class CellIdentityNr extends CellIdentity { @Override public int hashCode() { - return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn); + return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn, mNci); } @Override @@ -72,7 +74,17 @@ public final class CellIdentityNr extends CellIdentity { } CellIdentityNr o = (CellIdentityNr) other; - return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn; + return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn + && mNci == o.mNci; + } + + /** + * Get the NR Cell Identity. + * + * @return The NR Cell Identity in range [0, 68719476735] or Long.MAX_VALUE if unknown. + */ + public long getNci() { + return mNci; } /** @@ -122,6 +134,7 @@ public final class CellIdentityNr extends CellIdentity { .append(" mNrArfcn = ").append(mNrArfcn) .append(" mMcc = ").append(mMccStr) .append(" mMnc = ").append(mMncStr) + .append(" mNci = ").append(mNci) .append(" mAlphaLong = ").append(mAlphaLong) .append(" mAlphaShort = ").append(mAlphaShort) .append(" }") @@ -134,6 +147,7 @@ public final class CellIdentityNr extends CellIdentity { dest.writeInt(mPci); dest.writeInt(mTac); dest.writeInt(mNrArfcn); + dest.writeLong(mNci); } /** Construct from Parcel, type has already been processed */ @@ -142,6 +156,7 @@ public final class CellIdentityNr extends CellIdentity { mPci = in.readInt(); mTac = in.readInt(); mNrArfcn = in.readInt(); + mNci = in.readLong(); } /** Implement the Parcelable interface */ diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java index ceb76b57ae0c..c37b492a9cf1 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationState.java @@ -27,6 +27,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; +import java.util.stream.Collectors; /** * Description of a mobile network registration state @@ -360,7 +361,34 @@ public class NetworkRegistrationState implements Parcelable { return 0; } - private static String regStateToString(int regState) { + /** + * Convert service type to string + * + * @hide + * + * @param serviceType The service type + * @return The service type in string format + */ + public static String serviceTypeToString(@ServiceType int serviceType) { + switch (serviceType) { + case SERVICE_TYPE_VOICE: return "VOICE"; + case SERVICE_TYPE_DATA: return "DATA"; + case SERVICE_TYPE_SMS: return "SMS"; + case SERVICE_TYPE_VIDEO: return "VIDEO"; + case SERVICE_TYPE_EMERGENCY: return "EMERGENCY"; + } + return "Unknown service type " + serviceType; + } + + /** + * Convert registration state to string + * + * @hide + * + * @param regState The registration state + * @return The reg state in string + */ + public static String regStateToString(@RegState int regState) { switch (regState) { case REG_STATE_NOT_REG_NOT_SEARCHING: return "NOT_REG_NOT_SEARCHING"; case REG_STATE_HOME: return "HOME"; @@ -389,14 +417,17 @@ public class NetworkRegistrationState implements Parcelable { public String toString() { return new StringBuilder("NetworkRegistrationState{") .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS") - .append("transportType=").append(mTransportType) + .append(" transportType=").append(TransportType.toString(mTransportType)) .append(" regState=").append(regStateToString(mRegState)) - .append(" roamingType=").append(mRoamingType) + .append(" roamingType=").append(ServiceState.roamingTypeToString(mRoamingType)) .append(" accessNetworkTechnology=") .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology)) .append(" rejectCause=").append(mRejectCause) .append(" emergencyEnabled=").append(mEmergencyOnly) - .append(" supportedServices=").append(mAvailableServices) + .append(" availableServices=").append("[" + (mAvailableServices != null + ? Arrays.stream(mAvailableServices) + .mapToObj(type -> serviceTypeToString(type)) + .collect(Collectors.joining(",")) : null) + "]") .append(" cellIdentity=").append(mCellIdentity) .append(" voiceSpecificStates=").append(mVoiceSpecificStates) .append(" dataSpecificStates=").append(mDataSpecificStates) diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index fea1b7b08a20..2c9ba1dfff7b 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -45,7 +45,8 @@ import java.util.concurrent.Executor; * <p> * Override the methods for the state that you wish to receive updates for, and * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_ - * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. + * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are + * called when the state changes, os well as once on initial registration. * <p> * Note that access to some telephony information is * permission-protected. Your application won't receive updates for protected diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 33178766f3a3..402763e52cfa 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -887,6 +887,24 @@ public class ServiceState implements Parcelable { } /** + * Convert roaming type to string + * + * @param roamingType roaming type + * @return The roaming type in string format + * + * @hide + */ + public static String roamingTypeToString(@RoamingType int roamingType) { + switch (roamingType) { + case ROAMING_TYPE_NOT_ROAMING: return "NOT_ROAMING"; + case ROAMING_TYPE_UNKNOWN: return "UNKNOWN"; + case ROAMING_TYPE_DOMESTIC: return "DOMESTIC"; + case ROAMING_TYPE_INTERNATIONAL: return "INTERNATIONAL"; + } + return "Unknown roaming type " + roamingType; + } + + /** * Convert radio technology to String * * @param rt radioTechnology diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 313146d5538b..3a4d33c18485 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -59,6 +59,7 @@ import android.util.DisplayMetrics; import android.util.Log; import com.android.internal.telephony.IOnSubscriptionsChangedListener; +import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.PhoneConstants; @@ -72,6 +73,7 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import java.util.stream.Collectors; /** @@ -2573,17 +2575,35 @@ public class SubscriptionManager { * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, it means * it's unset and {@link SubscriptionManager#getDefaultDataSubscriptionId()} * is used to determine which modem is preferred. + * @param needValidation whether validation is needed before switch happens. + * @param executor The executor of where the callback will execute. + * @param callback Callback will be triggered once it succeeds or failed. + * See {@link TelephonyManager.SetOpportunisticSubscriptionResult} + * for more details. Pass null if don't care about the result. + * * @hide * */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void setPreferredDataSubscriptionId(int subId) { + public void setPreferredDataSubscriptionId(int subId, boolean needValidation, + @NonNull @CallbackExecutor Executor executor, Consumer<Integer> callback) { if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId); try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); - if (iSub != null) { - iSub.setPreferredDataSubscriptionId(subId); - } + if (iSub == null) return; + + ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() { + @Override + public void onComplete(int result) { + Binder.withCleanCallingIdentity(() -> executor.execute(() -> { + if (callback != null) { + callback.accept(result); + } + })); + } + }; + iSub.setPreferredDataSubscriptionId(subId, needValidation, callbackStub); } catch (RemoteException ex) { // ignore it } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f5d452e9721d..6690bd0f10b5 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -10094,6 +10094,29 @@ public class TelephonyManager { return false; } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SET_OPPORTUNISTIC_SUB"}, value = { + SET_OPPORTUNISTIC_SUB_SUCCESS, + SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED, + SET_OPPORTUNISTIC_SUB_INVALID_PARAMETER}) + public @interface SetOpportunisticSubscriptionResult {} + + /** + * No error. Operation succeeded. + */ + public static final int SET_OPPORTUNISTIC_SUB_SUCCESS = 0; + + /** + * Validation failed when trying to switch to preferred subscription. + */ + public static final int SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED = 1; + + /** + * The parameter passed in is invalid. + */ + public static final int SET_OPPORTUNISTIC_SUB_INVALID_PARAMETER = 2; + /** * Set preferred opportunistic data subscription id. * @@ -10309,11 +10332,14 @@ public class TelephonyManager { /** * Get whether reboot is required or not after making changes to modem configurations. - * @Return {@code True} if reboot is required after making changes to modem configurations, - * otherwise return {@code False}. + * The modem configuration change refers to switching from single SIM configuration to DSDS + * or the other way around. + * @Return {@code true} if reboot is required after making changes to modem configurations, + * otherwise return {@code false}. * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRebootRequiredForModemConfigChange() { try { diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java index 3255f8d0786d..d4a78ffb77db 100644 --- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java +++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java @@ -22,8 +22,6 @@ import android.annotation.WorkerThread; * Rcs1To1Thread represents a single RCS conversation thread with a total of two * {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal * Profile Service Definition Document) - * - * @hide - TODO: make public */ public class Rcs1To1Thread extends RcsThread { private int mThreadId; diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java index ef359a124d47..df62277f9ac1 100644 --- a/telephony/java/android/telephony/ims/RcsEvent.java +++ b/telephony/java/android/telephony/ims/RcsEvent.java @@ -19,8 +19,6 @@ import android.os.Parcel; /** * The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s. - * - * @hide - TODO: make public */ public abstract class RcsEvent { /** diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.java b/telephony/java/android/telephony/ims/RcsEventQueryParams.java index 5249becd476e..9dbfe4393213 100644 --- a/telephony/java/android/telephony/ims/RcsEventQueryParams.java +++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.java @@ -37,8 +37,6 @@ import java.security.InvalidParameterException; * The parameters to pass into * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} in order to select a * subset of {@link RcsEvent}s present in the message store. - * - * @hide - TODO: make public */ public final class RcsEventQueryParams implements Parcelable { /** @@ -152,8 +150,8 @@ public final class RcsEventQueryParams implements Parcelable { } /** - * @return Returns the type of {@link RcsEvent}s that this {@link RcsEventQueryParams} is - * set to query for. + * @return Returns the number of {@link RcsEvent}s to be returned from the query. A value of + * 0 means there is no set limit. */ public int getLimit() { return mLimit; diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java index f8d57fa5c09d..c30e4ccd7aa2 100644 --- a/telephony/java/android/telephony/ims/RcsEventQueryResult.java +++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.java @@ -25,8 +25,6 @@ import java.util.List; * The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} * call. This class allows getting the token for querying the next batch of events in order to * prevent handling large amounts of data at once. - * - * @hide - TODO: make public */ public final class RcsEventQueryResult implements Parcelable { private RcsQueryContinuationToken mContinuationToken; diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java index 663def5df50f..14af8ea63a67 100644 --- a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java +++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java @@ -24,8 +24,6 @@ import android.os.Parcelable; * Pass an instance of this class to * {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)} create an * {@link RcsFileTransferPart} and save it into storage. - * - * @hide - TODO: make public */ public final class RcsFileTransferCreationParams implements Parcelable { private String mRcsFileTransferSessionId; diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java index 1ce799919e09..9531c2e2f981 100644 --- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java +++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java @@ -26,8 +26,6 @@ import java.lang.annotation.RetentionPolicy; /** * A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7 * (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document) - * - * @hide - TODO: make public */ public class RcsFileTransferPart { /** diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java index 2c42494ee924..6e17bc2a685f 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThread.java +++ b/telephony/java/android/telephony/ims/RcsGroupThread.java @@ -29,8 +29,6 @@ import java.util.Set; * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service * Definition Document) - * - * @hide - TODO: make public */ public class RcsGroupThread extends RcsThread { /** diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java index bc61877a81d6..609b1740a536 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java +++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java @@ -20,8 +20,6 @@ import android.os.Parcel; /** * An event that happened on an {@link RcsGroupThread}. - * - * @hide - TODO: make public */ public abstract class RcsGroupThreadEvent extends RcsEvent { private final int mRcsGroupThreadId; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java index 74af9738e976..e768439d6cfa 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java +++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java @@ -24,8 +24,6 @@ import android.os.Parcelable; /** * An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA * RCC.71 (RCS Universal Profile Service Definition Document) - * - * @hide - TODO: make public */ public final class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent implements Parcelable { diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java index 06f4d5b10bb4..02030bc6a2ec 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java +++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java @@ -23,8 +23,6 @@ import android.os.Parcelable; /** * An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA * RCC.71 (RCS Universal Profile Service Definition Document) - * - * @hide - TODO: make public */ public final class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent implements Parcelable { diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java index 493270795e01..0d1a5730f0a0 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java @@ -22,8 +22,6 @@ import android.os.Parcelable; /** * An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 - * GSMA RCC.71 (RCS Universal Profile Service Definition Document) - * - * @hide - TODO: make public */ public final class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent implements Parcelable { diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java index 970a046e1105..cd525086749a 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java @@ -22,8 +22,6 @@ import android.os.Parcelable; /** * An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 - * GSMA RCC.71 (RCS Universal Profile Service Definition Document) - * - * @hide - TODO: make public */ public final class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent implements Parcelable { diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java index f3b7815c2453..61911abd00c5 100644 --- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java +++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java @@ -19,8 +19,6 @@ import android.annotation.WorkerThread; /** * This is a single instance of a message received over RCS. - * - * @hide - TODO: make public */ public class RcsIncomingMessage extends RcsMessage { /** diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java index 64b2339905eb..61dedbc1578a 100644 --- a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java +++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java @@ -24,8 +24,6 @@ import android.os.Parcelable; * {@link RcsIncomingMessageCreationParams} is a collection of parameters that should be passed * into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} to generate an * {@link RcsIncomingMessage} on that {@link RcsThread} - * - * @hide - TODO: make public */ public final class RcsIncomingMessageCreationParams extends RcsMessageCreationParams implements Parcelable { diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java index 90bd0446d6f3..22e4b2249c36 100644 --- a/telephony/java/android/telephony/ims/RcsManager.java +++ b/telephony/java/android/telephony/ims/RcsManager.java @@ -20,8 +20,6 @@ import android.content.Context; /** * The manager class for RCS related utilities. - * - * @hide - TODO: make public */ @SystemService(Context.TELEPHONY_RCS_SERVICE) public class RcsManager { diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java index 1700941b94ed..32274131a5ad 100644 --- a/telephony/java/android/telephony/ims/RcsMessage.java +++ b/telephony/java/android/telephony/ims/RcsMessage.java @@ -27,8 +27,6 @@ import java.util.Set; /** * This is a single instance of a message sent or received over RCS. - * - * @hide - TODO: make public */ public abstract class RcsMessage { /** diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java index 9ac4dcdf2d74..c46c605d861d 100644 --- a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java +++ b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java @@ -27,8 +27,6 @@ import android.os.Parcel; * {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} and * {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to create and persist * {@link RcsMessage}s on an {@link RcsThread} - * - * @hide - TODO: make public */ public class RcsMessageCreationParams { // The globally unique id of the RcsMessage to be created. diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java index fae0d97cd722..535a597f5e1e 100644 --- a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java +++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java @@ -31,8 +31,6 @@ import java.security.InvalidParameterException; * The parameters to pass into * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} in order to select a * subset of {@link RcsMessage}s present in the message store. - * - * @hide - TODO: make public */ public final class RcsMessageQueryParams implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java index 5adab760d594..3514b48e80a1 100644 --- a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java +++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java @@ -32,8 +32,6 @@ import java.util.List; * The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} * call. This class allows getting the token for querying the next batch of messages in order to * prevent handling large amounts of data at once. - * - * @hide - TODO: make public */ public final class RcsMessageQueryResult implements Parcelable { // The token to continue the query to get the next batch of results diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java index 565bb99de89a..b0b930c56e91 100644 --- a/telephony/java/android/telephony/ims/RcsMessageSnippet.java +++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.java @@ -23,8 +23,6 @@ import android.telephony.ims.RcsMessage.RcsMessageStatus; /** * An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread} - * - * @hide - TODO: make public */ public final class RcsMessageSnippet implements Parcelable { private final String mText; diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java index eca5ed518521..d811c6e93a56 100644 --- a/telephony/java/android/telephony/ims/RcsMessageStore.java +++ b/telephony/java/android/telephony/ims/RcsMessageStore.java @@ -26,8 +26,6 @@ import java.util.List; /** * RcsMessageStore is the application interface to RcsProvider and provides access methods to * RCS related database tables. - * - * @hide - TODO: make public */ public class RcsMessageStore { /** diff --git a/telephony/java/android/telephony/ims/RcsMessageStoreException.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java index 7c00749b1690..f25bb173be37 100644 --- a/telephony/java/android/telephony/ims/RcsMessageStoreException.java +++ b/telephony/java/android/telephony/ims/RcsMessageStoreException.java @@ -19,8 +19,6 @@ package android.telephony.ims; /** * An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in * {@link android.telephony.ims} - * - * @hide - TODO: make public */ public class RcsMessageStoreException extends Exception { diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java index dfcdee4a803d..06fb83268afb 100644 --- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java @@ -23,8 +23,6 @@ import java.util.List; /** * This is a single instance of a message sent over RCS. - * - * @hide - TODO: make public */ public class RcsOutgoingMessage extends RcsMessage { RcsOutgoingMessage(int id) { diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java index ca466e8c9d3e..979634a069df 100644 --- a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java @@ -23,8 +23,6 @@ import android.os.Parcelable; * {@link RcsOutgoingMessageCreationParams} is a collection of parameters that should be passed * into {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to generate an * {@link RcsOutgoingMessage} on that {@link RcsThread} - * - * @hide - TODO: make public */ public final class RcsOutgoingMessageCreationParams extends RcsMessageCreationParams implements Parcelable { diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java index 5a3062a69e3f..1c87b13f0dfb 100644 --- a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java @@ -21,8 +21,6 @@ import android.annotation.WorkerThread; /** * This class holds the delivery information of an {@link RcsOutgoingMessage} for each * {@link RcsParticipant} that the message was intended for. - * - * @hide - TODO: make public */ public class RcsOutgoingMessageDelivery { // The participant that this delivery is intended for diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java index 37b827b8e83c..7ba5d8e65f76 100644 --- a/telephony/java/android/telephony/ims/RcsParticipant.java +++ b/telephony/java/android/telephony/ims/RcsParticipant.java @@ -20,8 +20,6 @@ import android.annotation.WorkerThread; /** * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s. - * - * @hide - TODO: make public */ public class RcsParticipant { // The row ID of this participant in the database diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java index aa278b38cf81..c9a2b0d07bc8 100644 --- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java +++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java @@ -23,8 +23,6 @@ import android.os.Parcelable; /** * An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA * RCC.71 (RCS Universal Profile Service Definition Document) - * - * @hide - TODO: make public */ public final class RcsParticipantAliasChangedEvent extends RcsEvent implements Parcelable { // The ID of the participant that changed their alias diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java index 57c67fa7aa03..d24d079d7038 100644 --- a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java @@ -30,8 +30,6 @@ import java.security.InvalidParameterException; * The parameters to pass into * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} in order to select a * subset of {@link RcsThread}s present in the message store. - * - * @hide - TODO: make public */ public final class RcsParticipantQueryParams implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java index 4eae39d1a2c6..505f1a55d1f0 100644 --- a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java @@ -28,8 +28,6 @@ import java.util.List; * The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} * call. This class allows getting the token for querying the next batch of participants in order to * prevent handling large amounts of data at once. - * - * @hide - TODO: make public */ public final class RcsParticipantQueryResult implements Parcelable { // A token for the caller to continue their query for the next batch of results diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java index c1ff39652397..08643de51d40 100644 --- a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java +++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java @@ -31,8 +31,6 @@ import java.lang.annotation.RetentionPolicy; * @see RcsMessageQueryResult#getContinuationToken() * @see RcsParticipantQueryResult#getContinuationToken() * @see RcsThreadQueryResult#getContinuationToken() - * - * @hide - TODO: make public */ public final class RcsQueryContinuationToken implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java index 88655865f47b..e015dd3e9c0a 100644 --- a/telephony/java/android/telephony/ims/RcsThread.java +++ b/telephony/java/android/telephony/ims/RcsThread.java @@ -27,8 +27,6 @@ import com.android.internal.annotations.VisibleForTesting; /** * RcsThread represents a single RCS conversation thread. It holds messages that were sent and * received and events that occurred on that thread. - * - * @hide - TODO: make public */ public abstract class RcsThread { /** diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java index ba32e320c95b..05a5a3917691 100644 --- a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java +++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java @@ -35,8 +35,6 @@ import java.util.Set; /** * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} in * order to select a subset of {@link RcsThread}s present in the message store. - * - * @hide - TODO: make public */ public final class RcsThreadQueryParams implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java index a91126b89cce..1cac61d1aa64 100644 --- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java +++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java @@ -32,8 +32,6 @@ import java.util.List; * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} * call. This class allows getting the token for querying the next batch of threads in order to * prevent handling large amounts of data at once. - * - * @hide - TODO: make public */ public final class RcsThreadQueryResult implements Parcelable { // A token for the caller to continue their query for the next batch of results diff --git a/telephony/java/com/android/internal/telephony/ISetOpportunisticDataCallback.aidl b/telephony/java/com/android/internal/telephony/ISetOpportunisticDataCallback.aidl new file mode 100644 index 000000000000..7a78f3454aac --- /dev/null +++ b/telephony/java/com/android/internal/telephony/ISetOpportunisticDataCallback.aidl @@ -0,0 +1,25 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +/** + * Callback to provide asynchronous result of setPreferredOpportunisticData. + * @hide + */ +oneway interface ISetOpportunisticDataCallback { + void onComplete(int result); +} diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index 6ce9de4ca677..75a4d8227e23 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -17,6 +17,7 @@ package com.android.internal.telephony; import android.telephony.SubscriptionInfo; +import com.android.internal.telephony.ISetOpportunisticDataCallback; interface ISub { /** @@ -217,10 +218,14 @@ interface ISub { * designed to overwrite default data subscription temporarily. * * @param subId which subscription is preferred to for cellular data. + * @param needValidation whether validation is needed before switching. + * @param callback callback upon request completion. + * * @hide * */ - void setPreferredDataSubscriptionId(int subId); + void setPreferredDataSubscriptionId(int subId, boolean needValidation, + ISetOpportunisticDataCallback callback); /** * Get which subscription is preferred for cellular data. diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index bc43feaf0e15..caa367fbaafe 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1837,7 +1837,7 @@ interface ITelephony { * @hide */ boolean isMultisimCarrierRestricted(); - + /** * Switch configs to enable multi-sim or switch back to single-sim * @hide @@ -1846,6 +1846,7 @@ interface ITelephony { /** * Get if reboot is required upon altering modems configurations + * @hide */ boolean isRebootRequiredForModemConfigChange(); diff --git a/telephony/java/com/android/internal/telephony/cdma/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/BearerData.java index a4cd56b9e3e2..694cc69c2b3f 100644 --- a/telephony/java/com/android/internal/telephony/cdma/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/BearerData.java @@ -596,6 +596,45 @@ public final class BearerData { System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length); } + private static void encode7bitAsciiEms(UserData uData, byte[] udhData, boolean force) + throws CodingException + { + try { + Rlog.d(LOG_TAG, "encode7bitAsciiEms"); + int udhBytes = udhData.length + 1; // Add length octet. + int udhSeptets = ((udhBytes * 8) + 6) / 7; + int paddingBits = (udhSeptets * 7) - (udhBytes * 8); + String msg = uData.payloadStr; + byte[] payload ; + int msgLen = msg.length(); + BitwiseOutputStream outStream = new BitwiseOutputStream(msgLen + + (paddingBits > 0 ? 1 : 0)); + outStream.write(paddingBits, 0); + for (int i = 0; i < msgLen; i++) { + int charCode = UserData.charToAscii.get(msg.charAt(i), -1); + if (charCode == -1) { + if (force) { + outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR); + } else { + throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")"); + } + } else { + outStream.write(7, charCode); + } + } + payload = outStream.toByteArray(); + uData.msgEncoding = UserData.ENCODING_7BIT_ASCII; + uData.msgEncodingSet = true; + uData.numFields = udhSeptets + uData.payloadStr.length(); + uData.payload = new byte[udhBytes + payload.length]; + uData.payload[0] = (byte)udhData.length; + System.arraycopy(udhData, 0, uData.payload, 1, udhData.length); + System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length); + } catch (BitwiseOutputStream.AccessException ex) { + throw new CodingException("7bit ASCII encode failed: " + ex); + } + } + private static void encodeEmsUserDataPayload(UserData uData) throws CodingException { @@ -605,6 +644,8 @@ public final class BearerData { encode7bitEms(uData, headerData, true); } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) { encode16bitEms(uData, headerData); + } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) { + encode7bitAsciiEms(uData, headerData, true); } else { throw new CodingException("unsupported EMS user data encoding (" + uData.msgEncoding + ")"); @@ -1056,15 +1097,18 @@ public final class BearerData { throws CodingException { try { - offset *= 8; + int offsetBits = offset * 8; + int offsetSeptets = (offsetBits + 6) / 7; + numFields -= offsetSeptets; + StringBuffer strBuf = new StringBuffer(numFields); BitwiseInputStream inStream = new BitwiseInputStream(data); - int wantedBits = (offset * 8) + (numFields * 7); + int wantedBits = (offsetSeptets * 7) + (numFields * 7); if (inStream.available() < wantedBits) { throw new CodingException("insufficient data (wanted " + wantedBits + " bits, but only have " + inStream.available() + ")"); } - inStream.skip(offset); + inStream.skip(offsetSeptets * 7); for (int i = 0; i < numFields; i++) { int charCode = inStream.read(7); if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) && diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 9080e23eb88f..1da5eac27002 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -864,8 +864,9 @@ public class SmsMessage extends SmsMessageBase { Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'"); } - int teleservice = bearerData.hasUserDataHeader ? - SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT; + int teleservice = (bearerData.hasUserDataHeader + && userData.msgEncoding != UserData.ENCODING_7BIT_ASCII) + ? SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT; SmsEnvelope envelope = new SmsEnvelope(); envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT; diff --git a/test-runner/Android.mk b/test-runner/Android.mk deleted file mode 100644 index 18bde8517351..000000000000 --- a/test-runner/Android.mk +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright (C) 2008 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. -# - -LOCAL_PATH:= $(call my-dir) - -# additionally, build unit tests in a separate .apk -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/test-runner/tests/Android.bp b/test-runner/tests/Android.bp new file mode 100644 index 000000000000..03c73986118d --- /dev/null +++ b/test-runner/tests/Android.bp @@ -0,0 +1,40 @@ +// Copyright 2010, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test { + name: "FrameworkTestRunnerTests", + + // We only want this apk build for tests. + // + // Run the tests using the following commands: + // adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworkTestRunnerTests/FrameworkTestRunnerTests.apk + // adb shell am instrument \ + // -e notAnnotation android.test.suitebuilder.examples.error.RunAsPartOfSeparateTest \ + // -w com.android.frameworks.testrunner.tests/android.test.InstrumentationTestRunner \ + // + + libs: [ + "android.test.runner", + "android.test.base", + "android.test.mock", + ], + static_libs: ["junit"], + + // Include all test java files. + srcs: ["src/**/*.java"], + + // Because of android.test.mock. + platform_apis: true, + +} diff --git a/test-runner/tests/Android.mk b/test-runner/tests/Android.mk deleted file mode 100644 index f97d1c986b1c..000000000000 --- a/test-runner/tests/Android.mk +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2010, The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -# -# Run the tests using the following commands: -# adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworkTestRunnerTests/FrameworkTestRunnerTests.apk -# adb shell am instrument \ - -e notAnnotation android.test.suitebuilder.examples.error.RunAsPartOfSeparateTest \ - -w com.android.frameworks.testrunner.tests/android.test.InstrumentationTestRunner -# -LOCAL_MODULE_TAGS := tests - -LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock -LOCAL_STATIC_JAVA_LIBRARIES := junit - -# Include all test java files. -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := FrameworkTestRunnerTests -# Because of android.test.mock. -LOCAL_PRIVATE_PLATFORM_APIS := true - -include $(BUILD_PACKAGE) - diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index e2d59d648563..a7c95c78d05d 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -554,7 +554,7 @@ public class ConnectivityServiceTest { if (mNmValidationRedirectUrl != null) { mNmCallbacks.showProvisioningNotification( - "test_provisioning_notif_action"); + "test_provisioning_notif_action", "com.android.test.package"); mNmProvNotificationRequested = true; } } catch (RemoteException e) { diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index b6356076db60..a4a735d1a89d 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -35,6 +35,7 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -274,6 +275,11 @@ public class TetheringTest { isTetheringSupportedCalls++; return true; } + + @Override + public int getDefaultDataSubscriptionId() { + return INVALID_SUBSCRIPTION_ID; + } } private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6, diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java index ec286759354a..193f3806dbf6 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -21,6 +21,7 @@ import static android.net.ConnectivityManager.TETHERING_WIFI; import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -140,7 +141,8 @@ public final class EntitlementManagerTest { mMockContext = new MockContext(mContext); mSM = new TestStateMachine(); mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, mSystemProperties); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); } @After @@ -168,7 +170,8 @@ public final class EntitlementManagerTest { @Test public void canRequireProvisioning() { setupForRequiredProvisioning(); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); assertTrue(mEnMgr.isTetherProvisioningRequired()); } @@ -177,7 +180,8 @@ public final class EntitlementManagerTest { setupForRequiredProvisioning(); when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(null); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); // Couldn't get the CarrierConfigManager, but still had a declared provisioning app. // Therefore provisioning still be required. assertTrue(mEnMgr.isTetherProvisioningRequired()); @@ -187,7 +191,8 @@ public final class EntitlementManagerTest { public void toleratesCarrierConfigMissing() { setupForRequiredProvisioning(); when(mCarrierConfigManager.getConfig()).thenReturn(null); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); // We still have a provisioning app configured, so still require provisioning. assertTrue(mEnMgr.isTetherProvisioningRequired()); } @@ -197,11 +202,13 @@ public final class EntitlementManagerTest { setupForRequiredProvisioning(); when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) .thenReturn(null); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); assertFalse(mEnMgr.isTetherProvisioningRequired()); when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) .thenReturn(new String[] {"malformedApp"}); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); assertFalse(mEnMgr.isTetherProvisioningRequired()); } @@ -223,7 +230,8 @@ public final class EntitlementManagerTest { assertFalse(mEnMgr.everRunUiEntitlement); setupForRequiredProvisioning(); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog, + INVALID_SUBSCRIPTION_ID)); // 2. No cache value and don't need to run entitlement check. mEnMgr.everRunUiEntitlement = false; receiver = new ResultReceiver(null) { diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index 521778484d91..01b904d8f088 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -22,10 +22,12 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_NOT_REQUIRED; import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_REQUIRED; import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_UNSPECIFIED; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -44,26 +46,39 @@ import android.test.mock.MockContentResolver; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; -import java.util.Iterator; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Iterator; @RunWith(AndroidJUnit4.class) @SmallTest public class TetheringConfigurationTest { private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); + + private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; @Mock private Context mContext; @Mock private TelephonyManager mTelephonyManager; @Mock private Resources mResources; + @Mock private Resources mResourcesForSubId; private MockContentResolver mContentResolver; private Context mMockContext; private boolean mHasTelephonyManager; + private class MockTetheringConfiguration extends TetheringConfiguration { + MockTetheringConfiguration(Context ctx, SharedLog log, int id) { + super(ctx, log, id); + } + + @Override + protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { + return mResourcesForSubId; + } + } + private class MockContext extends BroadcastInterceptingContext { MockContext(Context base) { super(base); @@ -99,6 +114,9 @@ public class TetheringConfigurationTest { .thenReturn(new String[0]); when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types)) .thenReturn(new int[0]); + when(mResources.getStringArray( + com.android.internal.R.array.config_mobile_hotspot_provision_app)) + .thenReturn(new String[0]); mContentResolver = new MockContentResolver(); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); mMockContext = new MockContext(mContext); @@ -111,7 +129,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = true; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_REQUIRED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertTrue(cfg.isDunRequired); assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); @@ -127,7 +146,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = true; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_NOT_REQUIRED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertFalse(cfg.isDunRequired); assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); @@ -143,7 +163,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = false; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertTrue(cfg.isDunRequired); assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); // Just to prove we haven't clobbered Wi-Fi: @@ -160,7 +181,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = false; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); assertTrue(upstreamIterator.hasNext()); assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); @@ -181,7 +203,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = false; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); assertTrue(upstreamIterator.hasNext()); assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); @@ -199,7 +222,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = false; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); assertTrue(upstreamIterator.hasNext()); assertEquals(TYPE_WIFI, upstreamIterator.next().intValue()); @@ -214,7 +238,8 @@ public class TetheringConfigurationTest { public void testNewDhcpServerDisabled() { Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertTrue(cfg.enableLegacyDhcpServer); } @@ -222,7 +247,41 @@ public class TetheringConfigurationTest { public void testNewDhcpServerEnabled() { Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertFalse(cfg.enableLegacyDhcpServer); } + + @Test + public void testGetResourcesBySubId() { + setUpResourceForSubId(); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(cfg.provisioningApp.length == 0); + final int anyValidSubId = 1; + final MockTetheringConfiguration mockCfg = + new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); + assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); + assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); + } + + private void setUpResourceForSubId() { + when(mResourcesForSubId.getStringArray( + com.android.internal.R.array.config_tether_dhcp_range)).thenReturn(new String[0]); + when(mResourcesForSubId.getStringArray( + com.android.internal.R.array.config_tether_usb_regexs)).thenReturn(new String[0]); + when(mResourcesForSubId.getStringArray( + com.android.internal.R.array.config_tether_wifi_regexs)) + .thenReturn(new String[]{ "test_wlan\\d" }); + when(mResourcesForSubId.getStringArray( + com.android.internal.R.array.config_tether_bluetooth_regexs)) + .thenReturn(new String[0]); + when(mResourcesForSubId.getIntArray( + com.android.internal.R.array.config_tether_upstream_types)) + .thenReturn(new int[0]); + when(mResourcesForSubId.getStringArray( + com.android.internal.R.array.config_mobile_hotspot_provision_app)) + .thenReturn(PROVISIONING_APP_NAME); + } + } diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 85bf6f218ba0..582a5b869c65 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -294,22 +294,6 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, } } - if (el->FindAttribute("", "platformBuildVersionCode") == nullptr) { - auto versionCode = el->FindAttribute(xml::kSchemaAndroid, "versionCode"); - if (versionCode != nullptr) { - el->attributes.push_back(xml::Attribute{"", "platformBuildVersionCode", - versionCode->value}); - } - } - - if (el->FindAttribute("", "platformBuildVersionName") == nullptr) { - auto versionName = el->FindAttribute(xml::kSchemaAndroid, "versionName"); - if (versionName != nullptr) { - el->attributes.push_back(xml::Attribute{"", "platformBuildVersionName", - versionName->value}); - } - } - return true; }); @@ -489,8 +473,14 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { // Make sure we un-compile the value if it was set to something else. attr->compiled_value = {}; + attr->value = options_.compile_sdk_version.value(); + + attr = root->FindOrCreateAttribute("", "platformBuildVersionCode"); + // Make sure we un-compile the value if it was set to something else. + attr->compiled_value = {}; attr->value = options_.compile_sdk_version.value(); + } if (options_.compile_sdk_version_codename) { @@ -499,7 +489,12 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { // Make sure we un-compile the value if it was set to something else. attr->compiled_value = {}; + attr->value = options_.compile_sdk_version_codename.value(); + attr = root->FindOrCreateAttribute("", "platformBuildVersionName"); + + // Make sure we un-compile the value if it was set to something else. + attr->compiled_value = {}; attr->value = options_.compile_sdk_version_codename.value(); } diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index adea6273bc8b..4842f62e53b5 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -725,6 +725,43 @@ TEST_F(ManifestFixerTest, InsertCompileSdkVersions) { attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename"); ASSERT_THAT(attr, NotNull()); EXPECT_THAT(attr->value, StrEq("P")); + + attr = manifest->root->FindAttribute("", "platformBuildVersionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("28")); + + attr = manifest->root->FindAttribute("", "platformBuildVersionName"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("P")); +} + +TEST_F(ManifestFixerTest, OverrideCompileSdkVersions) { + std::string input = R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" + compileSdkVersion="27" compileSdkVersionCodename="O" + platformBuildVersionCode="27" platformBuildVersionName="O"/>)"; + ManifestFixerOptions options; + options.compile_sdk_version = {"28"}; + options.compile_sdk_version_codename = {"P"}; + + std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options); + ASSERT_THAT(manifest, NotNull()); + + xml::Attribute* attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersion"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("28")); + + attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("P")); + + attr = manifest->root->FindAttribute("", "platformBuildVersionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("28")); + + attr = manifest->root->FindAttribute("", "platformBuildVersionName"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("P")); } TEST_F(ManifestFixerTest, UnexpectedElementsInManifest) { @@ -750,59 +787,6 @@ TEST_F(ManifestFixerTest, UnexpectedElementsInManifest) { ASSERT_THAT(manifest, IsNull()); } -TEST_F(ManifestFixerTest, InsertPlatformBuildVersions) { - // Test for insertion when versionCode and versionName are included in the manifest - { - std::string input = R"( - <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" - android:versionCode="27" android:versionName="O"/>)"; - std::unique_ptr<xml::XmlResource> manifest = Verify(input); - ASSERT_THAT(manifest, NotNull()); - - xml::Attribute* attr = manifest->root->FindAttribute("", "platformBuildVersionCode"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("27")); - attr = manifest->root->FindAttribute("", "platformBuildVersionName"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("O")); - } - - // Test for insertion when versionCode and versionName defaults are specified - { - std::string input = R"( - <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"/>)"; - ManifestFixerOptions options; - options.version_code_default = {"27"}; - options.version_name_default = {"O"}; - std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options); - ASSERT_THAT(manifest, NotNull()); - - xml::Attribute* attr = manifest->root->FindAttribute("", "platformBuildVersionCode"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("27")); - attr = manifest->root->FindAttribute("", "platformBuildVersionName"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("O")); - } - - // Test that the platform build version attributes are not changed if they are currently present - { - std::string input = R"( - <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" - android:versionCode="28" android:versionName="P" - platformBuildVersionCode="27" platformBuildVersionName="O"/>)"; - std::unique_ptr<xml::XmlResource> manifest = Verify(input); - ASSERT_THAT(manifest, NotNull()); - - xml::Attribute* attr = manifest->root->FindAttribute("", "platformBuildVersionCode"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("27")); - attr = manifest->root->FindAttribute("", "platformBuildVersionName"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("O")); - } -} - TEST_F(ManifestFixerTest, UsesLibraryMustHaveNonEmptyName) { std::string input = R"( <manifest xmlns:android="http://schemas.android.com/apk/res/android" diff --git a/tools/preload/Android.bp b/tools/preload/Android.bp new file mode 100644 index 000000000000..809ee474969a --- /dev/null +++ b/tools/preload/Android.bp @@ -0,0 +1,17 @@ +java_library_host { + name: "preload", + srcs: [ + "Compile.java", + "LoadedClass.java", + "MemoryUsage.java", + "Operation.java", + "Policy.java", + "PrintCsv.java", + "PrintHtmlDiff.java", + "PrintPsTree.java", + "Proc.java", + "Record.java", + "Root.java", + "WritePreloadedClassFile.java", + ], +} diff --git a/tools/preload/Android.mk b/tools/preload/Android.mk deleted file mode 100644 index 14a4547cccbf..000000000000 --- a/tools/preload/Android.mk +++ /dev/null @@ -1,23 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - Compile.java \ - LoadedClass.java \ - MemoryUsage.java \ - Operation.java \ - Policy.java \ - PrintCsv.java \ - PrintHtmlDiff.java \ - PrintPsTree.java \ - Proc.java \ - Record.java \ - Root.java \ - WritePreloadedClassFile.java - -LOCAL_MODULE:= preload - -include $(BUILD_HOST_JAVA_LIBRARY) - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tools/preload/loadclass/Android.bp b/tools/preload/loadclass/Android.bp new file mode 100644 index 000000000000..6f12015dae2b --- /dev/null +++ b/tools/preload/loadclass/Android.bp @@ -0,0 +1,4 @@ +java_test { + name: "loadclass", + srcs: ["**/*.java"], +} diff --git a/tools/preload/loadclass/Android.mk b/tools/preload/loadclass/Android.mk deleted file mode 100644 index 65828be617df..000000000000 --- a/tools/preload/loadclass/Android.mk +++ /dev/null @@ -1,9 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_MODULE_TAGS := tests - -LOCAL_MODULE := loadclass - -include $(BUILD_JAVA_LIBRARY) diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 2cc1d8313225..5e5a59566ce5 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1149,9 +1149,6 @@ public class WifiManager { * Return a list of all the networks configured for the current foreground * user. * - * Requires the same permissions as {@link #getScanResults}. - * If such access is not allowed, this API will always return an empty list. - * * Not all fields of WifiConfiguration are returned. Only the following * fields are filled in: * <ul> @@ -1176,8 +1173,12 @@ public class WifiManager { * when auto-connecting to wifi. * <b>Compatibility Note:</b> For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will return an empty list, - * except to callers with Carrier privilege which will receive a restricted list only - * containing configurations which they created. + * except for: + * <ul> + * <li>Device Owner (DO) & Profile Owner (PO) apps will have access to the full list. + * <li>Callers with Carrier privilege will receive a restricted list only containing + * configurations which they created. + * </ul> */ @Deprecated @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE}) @@ -4721,7 +4722,6 @@ public class WifiManager { * * @hide */ - @SystemApi private static class EasyConnectCallbackProxy extends IDppCallback.Stub { private final Executor mExecutor; private final EasyConnectStatusCallback mEasyConnectStatusCallback; diff --git a/wifi/java/android/net/wifi/aware/ParcelablePeerHandle.java b/wifi/java/android/net/wifi/aware/ParcelablePeerHandle.java new file mode 100644 index 000000000000..4ddf872f6cd2 --- /dev/null +++ b/wifi/java/android/net/wifi/aware/ParcelablePeerHandle.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.aware; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A Parcelable {@link PeerHandle}. Can be constructed from a {@code PeerHandle} and then passed + * to any of the APIs which take a {@code PeerHandle} as inputs. + */ +public final class ParcelablePeerHandle extends PeerHandle implements Parcelable { + /** + * Construct a parcelable version of {@link PeerHandle}. + * + * @param peerHandle The {@link PeerHandle} to be made parcelable. + */ + public ParcelablePeerHandle(PeerHandle peerHandle) { + super(peerHandle.peerId); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(peerId); + } + + public static final Creator<ParcelablePeerHandle> CREATOR = + new Creator<ParcelablePeerHandle>() { + @Override + public ParcelablePeerHandle[] newArray(int size) { + return new ParcelablePeerHandle[size]; + } + + @Override + public ParcelablePeerHandle createFromParcel(Parcel in) { + int peerHandle = in.readInt(); + return new ParcelablePeerHandle(new PeerHandle(peerHandle)); + } + }; +} diff --git a/wifi/java/android/net/wifi/aware/PeerHandle.java b/wifi/java/android/net/wifi/aware/PeerHandle.java index 1603d00fd88a..422e177ed7ad 100644 --- a/wifi/java/android/net/wifi/aware/PeerHandle.java +++ b/wifi/java/android/net/wifi/aware/PeerHandle.java @@ -16,9 +16,6 @@ package android.net.wifi.aware; -import android.os.Parcel; -import android.os.Parcelable; - /** * Opaque object used to represent a Wi-Fi Aware peer. Obtained from discovery sessions in * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} or @@ -35,8 +32,9 @@ import android.os.Parcelable; * configuration's service-specific information field, * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])}, or match filter, * {@link PublishConfig.Builder#setMatchFilter(java.util.List)}. + * <p>A parcelable handle object is available with {@link ParcelablePeerHandle}. */ -public final class PeerHandle implements Parcelable { +public class PeerHandle { /** @hide */ public PeerHandle(int peerId) { this.peerId = peerId; @@ -62,29 +60,4 @@ public final class PeerHandle implements Parcelable { public int hashCode() { return peerId; } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(peerId); - } - - public static final Creator<PeerHandle> CREATOR = new Creator<PeerHandle>() { - @Override - public PeerHandle[] newArray(int size) { - return new PeerHandle[size]; - } - - @Override - public PeerHandle createFromParcel(Parcel in) { - int peerHandle = in.readInt(); - - return new PeerHandle(peerHandle); - } - }; - } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 6da6d4adeb62..3cc96bf2c795 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -1614,23 +1614,31 @@ public class WifiAwareManagerTest { assertEquals(cap.hashCode(), rereadCap.hashCode()); } - // PeerHandle tests + // ParcelablePeerHandle tests + /** + * Verify parceling of ParcelablePeerHandle and interoperability with PeerHandle. + */ @Test - public void testPeerHandleParcel() { + public void testParcelablePeerHandleParcel() { final PeerHandle peerHandle = new PeerHandle(5); + final ParcelablePeerHandle parcelablePeerHandle = new ParcelablePeerHandle(peerHandle); Parcel parcelW = Parcel.obtain(); - peerHandle.writeToParcel(parcelW, 0); + parcelablePeerHandle.writeToParcel(parcelW, 0); byte[] bytes = parcelW.marshall(); parcelW.recycle(); Parcel parcelR = Parcel.obtain(); parcelR.unmarshall(bytes, 0, bytes.length); parcelR.setDataPosition(0); - PeerHandle rereadPeerHandle = PeerHandle.CREATOR.createFromParcel(parcelR); + ParcelablePeerHandle rereadParcelablePeerHandle = + ParcelablePeerHandle.CREATOR.createFromParcel(parcelR); + + assertEquals(peerHandle, rereadParcelablePeerHandle); + assertEquals(peerHandle.hashCode(), rereadParcelablePeerHandle.hashCode()); + assertEquals(parcelablePeerHandle, rereadParcelablePeerHandle); + assertEquals(parcelablePeerHandle.hashCode(), rereadParcelablePeerHandle.hashCode()); - assertEquals(peerHandle, rereadPeerHandle); - assertEquals(peerHandle.hashCode(), rereadPeerHandle.hashCode()); } } |