diff options
367 files changed, 7110 insertions, 5976 deletions
diff --git a/Android.bp b/Android.bp index c3025e8561e2..b4110fa906cf 100644 --- a/Android.bp +++ b/Android.bp @@ -920,6 +920,7 @@ filegroup { "core/java/com/android/internal/util/RingBufferIndices.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", + "core/java/com/android/internal/util/TrafficStatsConstants.java", "core/java/com/android/internal/util/WakeupMessage.java", "core/java/android/net/shared/*.java", ] diff --git a/api/current.txt b/api/current.txt index 867279a5388b..f9db63d68bd6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -224,6 +224,7 @@ package android { field public static final int __removed3 = 16844187; // 0x101059b field public static final int __removed4 = 16844188; // 0x101059c field public static final int __removed5 = 16844189; // 0x101059d + field public static final int __removed6 = 16844182; // 0x1010596 field public static final int absListViewStyle = 16842858; // 0x101006a field public static final int accessibilityEventTypes = 16843648; // 0x1010380 field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 @@ -286,7 +287,6 @@ package android { field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowEmbedded = 16843765; // 0x10103f5 - field public static final int allowExternalStorageSandbox = 16844201; // 0x10105a9 field public static final int allowParallelSyncs = 16843570; // 0x1010332 field public static final int allowSingleTap = 16843353; // 0x1010259 field public static final int allowTaskReparenting = 16843268; // 0x1010204 @@ -750,7 +750,6 @@ package android { field public static final int immersive = 16843456; // 0x10102c0 field public static final int importantForAccessibility = 16843690; // 0x10103aa field public static final int importantForAutofill = 16844120; // 0x1010558 - field public static final int importantForContentCapture = 16844182; // 0x1010596 field public static final int inAnimation = 16843127; // 0x1010177 field public static final int includeFontPadding = 16843103; // 0x101015f field public static final int includeInGlobalSearch = 16843374; // 0x101026e @@ -1125,6 +1124,7 @@ package android { field public static final int reqKeyboardType = 16843304; // 0x1010228 field public static final int reqNavigation = 16843306; // 0x101022a field public static final int reqTouchScreen = 16843303; // 0x1010227 + field public static final int requestLegacyExternalStorage = 16844201; // 0x10105a9 field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 @@ -6235,14 +6235,14 @@ package android.app { } public class TaskInfo { - field public android.content.ComponentName baseActivity; - field public android.content.Intent baseIntent; + field @Nullable public android.content.ComponentName baseActivity; + field @NonNull public android.content.Intent baseIntent; field public boolean isRunning; field public int numActivities; - field public android.content.ComponentName origActivity; - field public android.app.ActivityManager.TaskDescription taskDescription; + field @Nullable public android.content.ComponentName origActivity; + field @Nullable public android.app.ActivityManager.TaskDescription taskDescription; field public int taskId; - field public android.content.ComponentName topActivity; + field @Nullable public android.content.ComponentName topActivity; } public class TaskStackBuilder { @@ -9001,7 +9001,7 @@ package android.bluetooth.le { method @Nullable public byte[] getManufacturerSpecificData(int); method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData(); method @Nullable public byte[] getServiceData(android.os.ParcelUuid); - method @Nullable public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids(); + method @NonNull public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids(); method public java.util.List<android.os.ParcelUuid> getServiceUuids(); method public int getTxPowerLevel(); } @@ -11859,6 +11859,7 @@ package android.content.pm { field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PermissionInfo> CREATOR; field public static final int FLAG_COSTS_MONEY = 1; // 0x1 field public static final int FLAG_HARD_RESTRICTED = 4; // 0x4 + field public static final int FLAG_IMMUTABLY_RESTRICTED = 16; // 0x10 field public static final int FLAG_INSTALLED = 1073741824; // 0x40000000 field public static final int FLAG_SOFT_RESTRICTED = 8; // 0x8 field public static final int PROTECTION_DANGEROUS = 1; // 0x1 @@ -23251,6 +23252,7 @@ package android.media { method public int generateAudioSessionId(); method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations(); method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations(); + method public int getAllowedCapturePolicy(); method public android.media.AudioDeviceInfo[] getDevices(int); method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException; method public int getMode(); @@ -23423,6 +23425,10 @@ package android.media { } public final class AudioPlaybackCaptureConfiguration { + method @NonNull public int[] getExcludeUids(); + method @NonNull public int[] getExcludeUsages(); + method @NonNull public int[] getMatchingUids(); + method @NonNull public int[] getMatchingUsages(); method @NonNull public android.media.projection.MediaProjection getMediaProjection(); } @@ -25133,25 +25139,6 @@ package android.media { field public static final int TYPE_STRING = 4; // 0x4 } - public final class MediaItem2 implements android.os.Parcelable { - method public int describeContents(); - method public long getEndPosition(); - method @Nullable public android.media.MediaMetadata getMetadata(); - method public long getStartPosition(); - method public void setMetadata(@Nullable android.media.MediaMetadata); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.MediaItem2> CREATOR; - field public static final long POSITION_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL - } - - public static final class MediaItem2.Builder { - ctor public MediaItem2.Builder(); - method @NonNull public android.media.MediaItem2 build(); - method @NonNull public android.media.MediaItem2.Builder setEndPosition(long); - method @NonNull public android.media.MediaItem2.Builder setMetadata(@Nullable android.media.MediaMetadata); - method @NonNull public android.media.MediaItem2.Builder setStartPosition(long); - } - public final class MediaMetadata implements android.os.Parcelable { method public boolean containsKey(String); method public int describeContents(); @@ -26172,8 +26159,8 @@ package android.media { ctor public Session2Command(@NonNull String, @Nullable android.os.Bundle); method public int describeContents(); method public int getCommandCode(); - method @Nullable public String getCustomCommand(); - method @Nullable public android.os.Bundle getExtras(); + method @Nullable public String getCustomAction(); + method @Nullable public android.os.Bundle getCustomExtras(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0 field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2Command> CREATOR; @@ -38620,6 +38607,7 @@ package android.provider { field public static final String ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS"; field public static final String ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS"; field public static final String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS"; + field public static final String ACTION_APP_BATTERY_SETTINGS = "android.settings.APP_BATTERY_SETTINGS"; field public static final String ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS = "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS"; field public static final String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS"; field public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS"; @@ -39091,7 +39079,7 @@ package android.provider { field @Deprecated public static final String BEARER = "bearer"; field public static final String CARRIER_ENABLED = "carrier_enabled"; field public static final String CARRIER_ID = "carrier_id"; - field public static final android.net.Uri CONTENT_URI; + field @NonNull public static final android.net.Uri CONTENT_URI; field public static final String CURRENT = "current"; field public static final String DEFAULT_SORT_ORDER = "name ASC"; field @Deprecated public static final String MCC = "mcc"; @@ -39110,7 +39098,7 @@ package android.provider { field public static final String PROXY = "proxy"; field public static final String ROAMING_PROTOCOL = "roaming_protocol"; field public static final String SERVER = "server"; - field public static final android.net.Uri SIM_APN_URI; + field @NonNull public static final android.net.Uri SIM_APN_URI; field public static final String SUBSCRIPTION_ID = "sub_id"; field public static final String TYPE = "type"; field public static final String USER = "user"; @@ -39157,7 +39145,7 @@ package android.provider { field public static final String CONTENT_ID = "cid"; field public static final String CONTENT_LOCATION = "cl"; field public static final String CONTENT_TYPE = "ct"; - field public static final android.net.Uri CONTENT_URI; + field @NonNull public static final android.net.Uri CONTENT_URI; field public static final String CT_START = "ctt_s"; field public static final String CT_TYPE = "ctt_t"; field public static final String FILENAME = "fn"; @@ -41771,7 +41759,7 @@ package android.service.voice { method public int getDisabledShowContext(); method public static boolean isActiveService(android.content.Context, android.content.ComponentName); method public android.os.IBinder onBind(android.content.Intent); - method @Nullable public java.util.Set<java.lang.String> onGetSupportedVoiceActions(@NonNull java.util.Set<java.lang.String>); + method @NonNull public java.util.Set<java.lang.String> onGetSupportedVoiceActions(@NonNull java.util.Set<java.lang.String>); method public void onLaunchVoiceAssistFromKeyguard(); method public void onReady(); method public void onShutdown(); @@ -47813,6 +47801,10 @@ package android.util { method public boolean equals(android.util.DisplayMetrics); method public void setTo(android.util.DisplayMetrics); method public void setToDefaults(); + field public static final int DENSITY_140 = 140; // 0x8c + field public static final int DENSITY_180 = 180; // 0xb4 + field public static final int DENSITY_200 = 200; // 0xc8 + field public static final int DENSITY_220 = 220; // 0xdc field public static final int DENSITY_260 = 260; // 0x104 field public static final int DENSITY_280 = 280; // 0x118 field public static final int DENSITY_300 = 300; // 0x12c @@ -50196,7 +50188,6 @@ package android.view { method @android.view.ViewDebug.CapturedViewProperty @IdRes public int getId(); method @android.view.ViewDebug.ExportedProperty(category="accessibility", mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, to="noHideDescendants")}) public int getImportantForAccessibility(); method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, to="yesExcludeDescendants"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, to="noExcludeDescendants")}) public int getImportantForAutofill(); - method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, to="yesExcludeDescendants"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, to="noExcludeDescendants")}) public int getImportantForContentCapture(); method public boolean getKeepScreenOn(); method public android.view.KeyEvent.DispatcherState getKeyDispatcherState(); method @android.view.ViewDebug.ExportedProperty(category="accessibility") @IdRes public int getLabelFor(); @@ -50337,7 +50328,6 @@ package android.view { method @android.view.ViewDebug.ExportedProperty public boolean isHovered(); method public boolean isImportantForAccessibility(); method public final boolean isImportantForAutofill(); - method public final boolean isImportantForContentCapture(); method public boolean isInEditMode(); method public boolean isInLayout(); method @android.view.ViewDebug.ExportedProperty public boolean isInTouchMode(); @@ -50412,7 +50402,6 @@ package android.view { method @CallSuper public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public void onProvideAutofillStructure(android.view.ViewStructure, int); method public void onProvideAutofillVirtualStructure(android.view.ViewStructure, int); - method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int); method public void onProvideStructure(android.view.ViewStructure); method public void onProvideVirtualStructure(android.view.ViewStructure); method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int); @@ -50538,7 +50527,6 @@ package android.view { method public void setId(@IdRes int); method public void setImportantForAccessibility(int); method public void setImportantForAutofill(int); - method public void setImportantForContentCapture(int); method public void setKeepScreenOn(boolean); method public void setKeyboardNavigationCluster(boolean); method public void setLabelFor(@IdRes int); @@ -50717,11 +50705,6 @@ package android.view { field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8 field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1 field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4 - field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0; // 0x0 - field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 2; // 0x2 - field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 8; // 0x8 - field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 1; // 0x1 - field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 4; // 0x4 field public static final int INVISIBLE = 4; // 0x4 field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000 field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2 @@ -51527,6 +51510,7 @@ package android.view { method public android.transition.Transition getSharedElementReturnTransition(); method public boolean getSharedElementsUseOverlay(); method @ColorInt public abstract int getStatusBarColor(); + method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects(); method public long getTransitionBackgroundFadeDuration(); method public android.transition.TransitionManager getTransitionManager(); method public abstract int getVolumeControlStream(); @@ -51605,6 +51589,7 @@ package android.view { method public void setSoftInputMode(int); method public abstract void setStatusBarColor(@ColorInt int); method public void setSustainedPerformanceMode(boolean); + method public void setSystemGestureExclusionRects(@NonNull java.util.List<android.graphics.Rect>); method public abstract void setTitle(CharSequence); method @Deprecated public abstract void setTitleColor(@ColorInt int); method public void setTransitionBackgroundFadeDuration(long); @@ -52829,7 +52814,7 @@ package android.view.contentcapture { method @Nullable public java.util.Set<android.view.contentcapture.ContentCaptureCondition> getContentCaptureConditions(); method @Nullable public android.content.ComponentName getServiceComponentName(); method public boolean isContentCaptureEnabled(); - method public void removeUserData(@NonNull android.view.contentcapture.UserDataRemovalRequest); + method public void removeData(@NonNull android.view.contentcapture.DataRemovalRequest); method public void setContentCaptureEnabled(boolean); } @@ -52840,6 +52825,7 @@ package android.view.contentcapture { method @Nullable public final android.view.contentcapture.ContentCaptureContext getContentCaptureContext(); method @NonNull public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId(); method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long); + method @NonNull public final android.view.ViewStructure newViewStructure(@NonNull android.view.View); method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long); method public final void notifyViewAppeared(@NonNull android.view.ViewStructure); method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId); @@ -52854,24 +52840,24 @@ package android.view.contentcapture { field @NonNull public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureSessionId> CREATOR; } - public final class UserDataRemovalRequest implements android.os.Parcelable { + public final class DataRemovalRequest implements android.os.Parcelable { method public int describeContents(); - method @NonNull public java.util.List<android.view.contentcapture.UserDataRemovalRequest.LocusIdRequest> getLocusIdRequests(); + method @NonNull public java.util.List<android.view.contentcapture.DataRemovalRequest.LocusIdRequest> getLocusIdRequests(); method @NonNull public String getPackageName(); method public boolean isForEverything(); method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.view.contentcapture.UserDataRemovalRequest> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.view.contentcapture.DataRemovalRequest> CREATOR; field public static final int FLAG_IS_PREFIX = 1; // 0x1 } - public static final class UserDataRemovalRequest.Builder { - ctor public UserDataRemovalRequest.Builder(); - method @NonNull public android.view.contentcapture.UserDataRemovalRequest.Builder addLocusId(@NonNull android.content.LocusId, int); - method @NonNull public android.view.contentcapture.UserDataRemovalRequest build(); - method @NonNull public android.view.contentcapture.UserDataRemovalRequest.Builder forEverything(); + public static final class DataRemovalRequest.Builder { + ctor public DataRemovalRequest.Builder(); + method @NonNull public android.view.contentcapture.DataRemovalRequest.Builder addLocusId(@NonNull android.content.LocusId, int); + method @NonNull public android.view.contentcapture.DataRemovalRequest build(); + method @NonNull public android.view.contentcapture.DataRemovalRequest.Builder forEverything(); } - public final class UserDataRemovalRequest.LocusIdRequest { + public final class DataRemovalRequest.LocusIdRequest { method @NonNull public int getFlags(); method @NonNull public android.content.LocusId getLocusId(); } @@ -53421,6 +53407,7 @@ package android.view.textclassifier { method public int describeContents(); method @Nullable public String getCallingPackageName(); method @NonNull public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation(); + method @NonNull public android.os.Bundle getExtras(); method @Nullable public java.util.List<java.lang.String> getHints(); method @IntRange(from=0xffffffff) public int getMaxSuggestions(); method @NonNull public android.view.textclassifier.TextClassifier.EntityConfig getTypeConfig(); @@ -53433,6 +53420,7 @@ package android.view.textclassifier { public static final class ConversationActions.Request.Builder { ctor public ConversationActions.Request.Builder(@NonNull java.util.List<android.view.textclassifier.ConversationActions.Message>); method @NonNull public android.view.textclassifier.ConversationActions.Request build(); + method @NonNull public android.view.textclassifier.ConversationActions.Request.Builder setExtras(@Nullable android.os.Bundle); method @NonNull public android.view.textclassifier.ConversationActions.Request.Builder setHints(@Nullable java.util.List<java.lang.String>); method @NonNull public android.view.textclassifier.ConversationActions.Request.Builder setMaxSuggestions(@IntRange(from=0xffffffff) int); method @NonNull public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(@Nullable android.view.textclassifier.TextClassifier.EntityConfig); @@ -53629,29 +53617,23 @@ package android.view.textclassifier { method @NonNull public android.view.textclassifier.TextClassifier.EntityConfig.Builder setIncludedTypes(@Nullable java.util.Collection<java.lang.String>); } - public final class TextClassifierEvent implements android.os.Parcelable { + public abstract class TextClassifierEvent implements android.os.Parcelable { method public int describeContents(); method @NonNull public int[] getActionIndices(); method @NonNull public String[] getEntityTypes(); method public int getEventCategory(); method @Nullable public android.view.textclassifier.TextClassificationContext getEventContext(); method public int getEventIndex(); - method public long getEventTime(); method public int getEventType(); method @NonNull public android.os.Bundle getExtras(); - method @Nullable public String getLanguage(); - method public int getRelativeSuggestedWordEndIndex(); - method public int getRelativeSuggestedWordStartIndex(); - method public int getRelativeWordEndIndex(); - method public int getRelativeWordStartIndex(); + method @Nullable public String getModelName(); method @Nullable public String getResultId(); - method public float getScore(); + method @NonNull public float[] getScores(); method public void writeToParcel(android.os.Parcel, int); field public static final int CATEGORY_CONVERSATION_ACTIONS = 3; // 0x3 field public static final int CATEGORY_LANGUAGE_DETECTION = 4; // 0x4 field public static final int CATEGORY_LINKIFY = 2; // 0x2 field public static final int CATEGORY_SELECTION = 1; // 0x1 - field public static final int CATEGORY_UNDEFINED = 0; // 0x0 field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent> CREATOR; field public static final int TYPE_ACTIONS_GENERATED = 20; // 0x14 field public static final int TYPE_ACTIONS_SHOWN = 6; // 0x6 @@ -53673,25 +53655,63 @@ package android.view.textclassifier { field public static final int TYPE_SMART_ACTION = 13; // 0xd field public static final int TYPE_SMART_SELECTION_MULTI = 4; // 0x4 field public static final int TYPE_SMART_SELECTION_SINGLE = 3; // 0x3 - field public static final int TYPE_UNDEFINED = 0; // 0x0 - } - - public static final class TextClassifierEvent.Builder { - ctor public TextClassifierEvent.Builder(int, int); - method @NonNull public android.view.textclassifier.TextClassifierEvent build(); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setActionIndices(@NonNull int...); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEntityTypes(@NonNull java.lang.String...); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventContext(@Nullable android.view.textclassifier.TextClassificationContext); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventIndex(int); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventTime(long); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setExtras(@NonNull android.os.Bundle); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setLanguage(@Nullable String); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeSuggestedWordEndIndex(int); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeSuggestedWordStartIndex(int); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordEndIndex(int); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordStartIndex(int); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setResultId(@Nullable String); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setScore(float); + } + + public abstract static class TextClassifierEvent.Builder<T extends android.view.textclassifier.TextClassifierEvent.Builder<T>> { + method @NonNull public T setActionIndices(@NonNull int...); + method @NonNull public T setEntityTypes(@NonNull java.lang.String...); + method @NonNull public T setEventContext(@Nullable android.view.textclassifier.TextClassificationContext); + method @NonNull public T setEventIndex(int); + method @NonNull public T setExtras(@NonNull android.os.Bundle); + method @NonNull public T setModelName(@Nullable String); + method @NonNull public T setResultId(@Nullable String); + method @NonNull public T setScores(@NonNull float...); + } + + public static final class TextClassifierEvent.ConversationActionsEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable { + field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.ConversationActionsEvent> CREATOR; + } + + public static final class TextClassifierEvent.ConversationActionsEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.ConversationActionsEvent.Builder> { + ctor public TextClassifierEvent.ConversationActionsEvent.Builder(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.ConversationActionsEvent build(); + } + + public static final class TextClassifierEvent.LanguageDetectionEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable { + method @Nullable public android.icu.util.ULocale getLocale(); + field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent> CREATOR; + } + + public static final class TextClassifierEvent.LanguageDetectionEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent.Builder> { + ctor public TextClassifierEvent.LanguageDetectionEvent.Builder(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent build(); + method @NonNull public android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent.Builder setLocale(@Nullable android.icu.util.ULocale); + } + + public static final class TextClassifierEvent.TextLinkifyEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable { + field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.TextLinkifyEvent> CREATOR; + } + + public static final class TextClassifierEvent.TextLinkifyEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.TextLinkifyEvent.Builder> { + ctor public TextClassifierEvent.TextLinkifyEvent.Builder(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextLinkifyEvent build(); + } + + public static final class TextClassifierEvent.TextSelectionEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable { + method public int getRelativeSuggestedWordEndIndex(); + method public int getRelativeSuggestedWordStartIndex(); + method public int getRelativeWordEndIndex(); + method public int getRelativeWordStartIndex(); + field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.TextSelectionEvent> CREATOR; + } + + public static final class TextClassifierEvent.TextSelectionEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder> { + ctor public TextClassifierEvent.TextSelectionEvent.Builder(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent build(); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeSuggestedWordEndIndex(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeSuggestedWordStartIndex(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeWordEndIndex(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeWordStartIndex(int); } public final class TextLanguage implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 2d8b09293abb..b60e850249ef 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -604,7 +604,7 @@ package android.app { public final class Vr2dDisplayProperties implements android.os.Parcelable { ctor public Vr2dDisplayProperties(int, int, int); method public int describeContents(); - method public void dump(java.io.PrintWriter, String); + method public void dump(@NonNull java.io.PrintWriter, @NonNull String); method public int getAddedFlags(); method public int getDpi(); method public int getHeight(); @@ -615,26 +615,26 @@ package android.app { field public static final int FLAG_VIRTUAL_DISPLAY_ENABLED = 1; // 0x1 } - public static class Vr2dDisplayProperties.Builder { + public static final class Vr2dDisplayProperties.Builder { ctor public Vr2dDisplayProperties.Builder(); - method public android.app.Vr2dDisplayProperties.Builder addFlags(int); - method public android.app.Vr2dDisplayProperties build(); - method public android.app.Vr2dDisplayProperties.Builder removeFlags(int); - method public android.app.Vr2dDisplayProperties.Builder setDimensions(int, int, int); - method public android.app.Vr2dDisplayProperties.Builder setEnabled(boolean); + method @NonNull public android.app.Vr2dDisplayProperties.Builder addFlags(int); + method @NonNull public android.app.Vr2dDisplayProperties build(); + method @NonNull public android.app.Vr2dDisplayProperties.Builder removeFlags(int); + method @NonNull public android.app.Vr2dDisplayProperties.Builder setDimensions(int, int, int); + method @NonNull public android.app.Vr2dDisplayProperties.Builder setEnabled(boolean); } public class VrManager { method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public int getVr2dDisplayId(); method @RequiresPermission(anyOf={android.Manifest.permission.RESTRICTED_VR_ACCESS, "android.permission.ACCESS_VR_STATE"}) public boolean isPersistentVrModeEnabled(); method @RequiresPermission(anyOf={android.Manifest.permission.RESTRICTED_VR_ACCESS, "android.permission.ACCESS_VR_STATE"}) public boolean isVrModeEnabled(); - method @RequiresPermission(anyOf={android.Manifest.permission.RESTRICTED_VR_ACCESS, "android.permission.ACCESS_VR_STATE"}) public void registerVrStateCallback(@NonNull java.util.concurrent.Executor, android.app.VrStateCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.RESTRICTED_VR_ACCESS, "android.permission.ACCESS_VR_STATE"}) public void registerVrStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.app.VrStateCallback); method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public void setAndBindVrCompositor(android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public void setPersistentVrModeEnabled(boolean); method @RequiresPermission("android.permission.ACCESS_VR_MANAGER") public void setStandbyEnabled(boolean); - method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public void setVr2dDisplayProperties(android.app.Vr2dDisplayProperties); - method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public void setVrInputMethod(android.content.ComponentName); - method @RequiresPermission(anyOf={android.Manifest.permission.RESTRICTED_VR_ACCESS, "android.permission.ACCESS_VR_STATE"}) public void unregisterVrStateCallback(android.app.VrStateCallback); + method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public void setVr2dDisplayProperties(@NonNull android.app.Vr2dDisplayProperties); + method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public void setVrInputMethod(@Nullable android.content.ComponentName); + method @RequiresPermission(anyOf={android.Manifest.permission.RESTRICTED_VR_ACCESS, "android.permission.ACCESS_VR_STATE"}) public void unregisterVrStateCallback(@NonNull android.app.VrStateCallback); } public abstract class VrStateCallback { @@ -1092,12 +1092,14 @@ package android.app.prediction { } public static final class AppTarget.Builder { - ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId); + ctor @Deprecated public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId); + ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull String, @NonNull android.os.UserHandle); + ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull android.content.pm.ShortcutInfo); method @NonNull public android.app.prediction.AppTarget build(); method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String); method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int); - method @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); - method @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); } public final class AppTargetEvent implements android.os.Parcelable { @@ -2002,15 +2004,15 @@ package android.hardware.hdmi { public final class HdmiControlManager { method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener); method @Nullable public android.hardware.hdmi.HdmiClient getClient(int); - method @Nullable public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevicesList(); + method @NonNull public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevices(); method public int getPhysicalAddress(); method @Nullable public android.hardware.hdmi.HdmiPlaybackClient getPlaybackClient(); method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient(); method @Nullable public android.hardware.hdmi.HdmiTvClient getTvClient(); - method public boolean isRemoteDeviceConnected(@NonNull android.hardware.hdmi.HdmiDeviceInfo); - method public void powerOffRemoteDevice(@NonNull android.hardware.hdmi.HdmiDeviceInfo); + method public boolean isDeviceConnected(@NonNull android.hardware.hdmi.HdmiDeviceInfo); + method public void powerOffDevice(@NonNull android.hardware.hdmi.HdmiDeviceInfo); method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void removeHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener); - method public void requestRemoteDeviceToBecomeActiveSource(@NonNull android.hardware.hdmi.HdmiDeviceInfo); + method public void setActiveSource(@NonNull android.hardware.hdmi.HdmiDeviceInfo); method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean); field public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE"; field public static final int AVR_VOLUME_MUTED = 101; // 0x65 @@ -3672,7 +3674,6 @@ package android.media.audiopolicy { method public void setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener); method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setAudioPolicyVolumeCallback(@NonNull android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback); method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsAudioFocusPolicy(boolean); - method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean); method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setLooper(@NonNull android.os.Looper) throws java.lang.IllegalArgumentException; } @@ -4227,9 +4228,12 @@ package android.net { method public static void setThreadStatsTagApp(); method public static void setThreadStatsTagBackup(); method public static void setThreadStatsTagRestore(); - field public static final int TAG_SYSTEM_DHCP = -192; // 0xffffff40 - field public static final int TAG_SYSTEM_DHCP_SERVER = -186; // 0xffffff46 - field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42 + field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = -113; // 0xffffff8f + field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = -128; // 0xffffff80 + field public static final int TAG_NETWORK_STACK_RANGE_END = -257; // 0xfffffeff + field public static final int TAG_NETWORK_STACK_RANGE_START = -768; // 0xfffffd00 + field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_END = -241; // 0xffffff0f + field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00 } public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { @@ -5823,7 +5827,6 @@ package android.provider { public final class DeviceConfig { method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener); - method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertyChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertyChangedListener); method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean); method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float); method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int); @@ -5831,7 +5834,6 @@ package android.provider { method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getProperty(@NonNull String, @NonNull String); method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String, @NonNull String, @Nullable String); method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener); - method public static void removeOnPropertyChangedListener(@NonNull android.provider.DeviceConfig.OnPropertyChangedListener); method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String); method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean); field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager"; @@ -5864,10 +5866,6 @@ package android.provider { method public void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties); } - public static interface DeviceConfig.OnPropertyChangedListener { - method public void onPropertyChanged(@NonNull String, @NonNull String, @Nullable String); - } - public static class DeviceConfig.Properties { method public boolean getBoolean(@NonNull String, boolean); method public float getFloat(@NonNull String, float); @@ -6275,7 +6273,6 @@ package android.service.attention { public abstract class AttentionService extends android.app.Service { ctor public AttentionService(); - method public final void disableSelf(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onCancelAttentionCheck(@NonNull android.service.attention.AttentionService.AttentionCallback); method public abstract void onCheckAttention(@NonNull android.service.attention.AttentionService.AttentionCallback); @@ -6396,9 +6393,9 @@ package android.service.contentcapture { method public void onConnected(); method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId); + method public void onDataRemovalRequest(@NonNull android.view.contentcapture.DataRemovalRequest); method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDisconnected(); - method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest); method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set<android.view.contentcapture.ContentCaptureCondition>); method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>); field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService"; @@ -6498,7 +6495,7 @@ package android.service.euicc { ctor public EuiccService(); method @CallSuper public android.os.IBinder onBind(android.content.Intent); method public abstract int onDeleteSubscription(int, String); - method public abstract android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle); + method public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle); method @Deprecated public int onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean); method public abstract int onEraseSubscriptions(int); method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean); diff --git a/api/system-removed.txt b/api/system-removed.txt index 162f212a787e..8f7112266457 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -60,6 +60,18 @@ package android.content { } +package android.hardware.hdmi { + + public final class HdmiControlManager { + method @Deprecated public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevicesList(); + method @Deprecated public boolean isRemoteDeviceConnected(@NonNull android.hardware.hdmi.HdmiDeviceInfo); + method @Deprecated public void powerOffRemoteDevice(@NonNull android.hardware.hdmi.HdmiDeviceInfo); + method @Deprecated public void powerOnRemoteDevice(android.hardware.hdmi.HdmiDeviceInfo); + method @Deprecated public void requestRemoteDeviceToBecomeActiveSource(@NonNull android.hardware.hdmi.HdmiDeviceInfo); + } + +} + package android.location { public class LocationManager { @@ -113,6 +125,19 @@ package android.os { } +package android.provider { + + public final class DeviceConfig { + method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertyChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertyChangedListener); + method public static void removeOnPropertyChangedListener(@NonNull android.provider.DeviceConfig.OnPropertyChangedListener); + } + + public static interface DeviceConfig.OnPropertyChangedListener { + method public void onPropertyChanged(@NonNull String, @NonNull String, @Nullable String); + } + +} + package android.service.notification { public abstract class NotificationListenerService extends android.app.Service { diff --git a/api/test-current.txt b/api/test-current.txt index 6fb4a92d7816..f76b3830c461 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -498,12 +498,13 @@ package android.app.prediction { } public static final class AppTarget.Builder { - ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId); + ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull String, @NonNull android.os.UserHandle); + ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull android.content.pm.ShortcutInfo); method @NonNull public android.app.prediction.AppTarget build(); method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String); method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int); - method @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); - method @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); } public final class AppTargetEvent implements android.os.Parcelable { @@ -1074,6 +1075,19 @@ package android.location { package android.media { + public final class AudioFocusInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.media.AudioAttributes getAttributes(); + method @NonNull public String getClientId(); + method public int getClientUid(); + method public int getFlags(); + method public int getGainRequest(); + method public int getLossReceived(); + method @NonNull public String getPackageName(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioFocusInfo> CREATOR; + } + public final class AudioFocusRequest { method @Nullable public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener(); } @@ -1085,6 +1099,16 @@ package android.media { method public static boolean isEncodingLinearPcm(int); } + public class AudioManager { + method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); + method public boolean hasRegisteredDynamicPolicy(); + method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); + method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); + method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); + method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy); + field public static final int SUCCESS = 0; // 0x0 + } + public static final class AudioRecord.MetricsConstants { field public static final String ATTRIBUTES = "android.media.audiorecord.attributes"; field public static final String CHANNEL_MASK = "android.media.audiorecord.channelMask"; @@ -1108,22 +1132,6 @@ package android.media { field public static final String SAMPLE_RATE = "android.media.audiotrack.sampleRate"; } - public final class BufferingParams implements android.os.Parcelable { - method public int describeContents(); - method public int getInitialMarkMs(); - method public int getResumePlaybackMarkMs(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR; - } - - public static class BufferingParams.Builder { - ctor public BufferingParams.Builder(); - ctor public BufferingParams.Builder(android.media.BufferingParams); - method public android.media.BufferingParams build(); - method public android.media.BufferingParams.Builder setInitialMarkMs(int); - method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int); - } - public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint { ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(int, int, int, int, @NonNull android.util.Size); ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(@NonNull android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint, @NonNull android.util.Size); @@ -1178,6 +1186,93 @@ package android.media.audiofx { } +package android.media.audiopolicy { + + public class AudioMix { + method public int getMixState(); + field public static final int MIX_STATE_DISABLED = -1; // 0xffffffff + field public static final int MIX_STATE_IDLE = 0; // 0x0 + field public static final int MIX_STATE_MIXING = 1; // 0x1 + field public static final int ROUTE_FLAG_LOOP_BACK = 2; // 0x2 + field public static final int ROUTE_FLAG_RENDER = 1; // 0x1 + } + + public static class AudioMix.Builder { + ctor public AudioMix.Builder(android.media.audiopolicy.AudioMixingRule) throws java.lang.IllegalArgumentException; + method public android.media.audiopolicy.AudioMix build() throws java.lang.IllegalArgumentException; + method public android.media.audiopolicy.AudioMix.Builder setDevice(@NonNull android.media.AudioDeviceInfo) throws java.lang.IllegalArgumentException; + method public android.media.audiopolicy.AudioMix.Builder setFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException; + method public android.media.audiopolicy.AudioMix.Builder setRouteFlags(int) throws java.lang.IllegalArgumentException; + } + + public class AudioMixingRule { + field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2 + field public static final int RULE_MATCH_ATTRIBUTE_USAGE = 1; // 0x1 + field public static final int RULE_MATCH_UID = 4; // 0x4 + } + + public static class AudioMixingRule.Builder { + ctor public AudioMixingRule.Builder(); + method public android.media.audiopolicy.AudioMixingRule.Builder addMixRule(int, Object) throws java.lang.IllegalArgumentException; + method public android.media.audiopolicy.AudioMixingRule.Builder addRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException; + method @NonNull public android.media.audiopolicy.AudioMixingRule.Builder allowPrivilegedPlaybackCapture(boolean); + method public android.media.audiopolicy.AudioMixingRule build(); + method public android.media.audiopolicy.AudioMixingRule.Builder excludeMixRule(int, Object) throws java.lang.IllegalArgumentException; + method public android.media.audiopolicy.AudioMixingRule.Builder excludeRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException; + } + + public class AudioPolicy { + method public int attachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>); + method public android.media.AudioRecord createAudioRecordSink(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; + method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; + method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>); + method public int getFocusDuckingBehavior(); + method public int getStatus(); + method public boolean removeUidDeviceAffinity(int); + method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; + method public void setRegistration(String); + method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>); + method public String toLogFriendlyString(); + field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0 + field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0 + field public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1; // 0x1 + field public static final int POLICY_STATUS_REGISTERED = 2; // 0x2 + field public static final int POLICY_STATUS_UNREGISTERED = 1; // 0x1 + } + + public abstract static class AudioPolicy.AudioPolicyFocusListener { + ctor public AudioPolicy.AudioPolicyFocusListener(); + method public void onAudioFocusAbandon(android.media.AudioFocusInfo); + method public void onAudioFocusGrant(android.media.AudioFocusInfo, int); + method public void onAudioFocusLoss(android.media.AudioFocusInfo, boolean); + method public void onAudioFocusRequest(android.media.AudioFocusInfo, int); + } + + public abstract static class AudioPolicy.AudioPolicyStatusListener { + ctor public AudioPolicy.AudioPolicyStatusListener(); + method public void onMixStateUpdate(android.media.audiopolicy.AudioMix); + method public void onStatusChange(); + } + + public abstract static class AudioPolicy.AudioPolicyVolumeCallback { + ctor public AudioPolicy.AudioPolicyVolumeCallback(); + method public void onVolumeAdjustment(int); + } + + public static class AudioPolicy.Builder { + ctor public AudioPolicy.Builder(android.content.Context); + method @NonNull public android.media.audiopolicy.AudioPolicy.Builder addMix(@NonNull android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; + method @NonNull public android.media.audiopolicy.AudioPolicy build(); + method public void setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener); + method public void setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener); + method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setAudioPolicyVolumeCallback(@NonNull android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback); + method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsAudioFocusPolicy(boolean); + method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean); + method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setLooper(@NonNull android.os.Looper) throws java.lang.IllegalArgumentException; + } + +} + package android.metrics { public class LogMaker { @@ -1353,9 +1448,6 @@ package android.net { method public static long getLoopbackRxPackets(); method public static long getLoopbackTxBytes(); method public static long getLoopbackTxPackets(); - field public static final int TAG_SYSTEM_DHCP = -192; // 0xffffff40 - field public static final int TAG_SYSTEM_DHCP_SERVER = -186; // 0xffffff46 - field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42 } } @@ -2106,7 +2198,6 @@ package android.provider { public final class DeviceConfig { method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener); - 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 boolean getBoolean(@NonNull String, @NonNull String, boolean); method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static float getFloat(@NonNull String, @NonNull String, float); method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static int getInt(@NonNull String, @NonNull String, int); @@ -2114,7 +2205,6 @@ package android.provider { method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(@NonNull String, @NonNull String); method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getString(@NonNull String, @NonNull String, @Nullable String); method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener); - method public static void removeOnPropertyChangedListener(@NonNull android.provider.DeviceConfig.OnPropertyChangedListener); method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String); method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean); field public static final String NAMESPACE_AUTOFILL = "autofill"; @@ -2128,10 +2218,6 @@ package android.provider { method public void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties); } - public static interface DeviceConfig.OnPropertyChangedListener { - method public void onPropertyChanged(@NonNull String, @NonNull String, @Nullable String); - } - public static class DeviceConfig.Properties { method public boolean getBoolean(@NonNull String, boolean); method public float getFloat(@NonNull String, float); @@ -2445,9 +2531,9 @@ package android.service.contentcapture { method public void onConnected(); method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId); + method public void onDataRemovalRequest(@NonNull android.view.contentcapture.DataRemovalRequest); method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDisconnected(); - method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest); method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set<android.view.contentcapture.ContentCaptureCondition>); method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>); field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService"; diff --git a/api/test-removed.txt b/api/test-removed.txt index d802177e249b..83a5708a2eb3 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -1 +1,14 @@ // Signature format: 2.0 +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 public static void removeOnPropertyChangedListener(@NonNull android.provider.DeviceConfig.OnPropertyChangedListener); + } + + public static interface DeviceConfig.OnPropertyChangedListener { + method public void onPropertyChanged(@NonNull String, @NonNull String, @Nullable String); + } + +} + diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp index 47617e045c12..bb8d92737563 100644 --- a/cmds/idmap2/idmap2/Create.cpp +++ b/cmds/idmap2/idmap2/Create.cpp @@ -16,6 +16,7 @@ #include <sys/stat.h> // umask #include <sys/types.h> // umask + #include <fstream> #include <memory> #include <ostream> diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index 677c6fa155dd..b7ae9d090cee 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -31,15 +31,14 @@ #include "androidfw/ResourceUtils.h" #include "androidfw/StringPiece.h" #include "androidfw/Util.h" -#include "utils/String16.h" -#include "utils/String8.h" - #include "idmap2/CommandLineOptions.h" #include "idmap2/Idmap.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" #include "idmap2/Xml.h" #include "idmap2/ZipFile.h" +#include "utils/String16.h" +#include "utils/String8.h" using android::ApkAssets; using android::ApkAssetsCookie; diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp index d8867fe8f497..87949085cf1d 100644 --- a/cmds/idmap2/idmap2/Main.cpp +++ b/cmds/idmap2/idmap2/Main.cpp @@ -23,12 +23,11 @@ #include <string> #include <vector> +#include "Commands.h" #include "idmap2/CommandLineOptions.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -#include "Commands.h" - using android::idmap2::CommandLineOptions; using android::idmap2::Result; using android::idmap2::Unit; diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index 55b1003c38af..fa9a77aa69c6 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -15,6 +15,7 @@ */ #include <dirent.h> + #include <fstream> #include <memory> #include <ostream> @@ -24,8 +25,8 @@ #include <utility> #include <vector> +#include "Commands.h" #include "android-base/properties.h" - #include "idmap2/CommandLineOptions.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" @@ -35,8 +36,6 @@ #include "idmap2/Xml.h" #include "idmap2/ZipFile.h" -#include "Commands.h" - using android::idmap2::CommandLineOptions; using android::idmap2::Error; using android::idmap2::Idmap; @@ -211,7 +210,9 @@ Result<Unit> Scan(const std::vector<std::string>& args) { const auto create_ok = Create(create_args); if (!create_ok) { - return Error(create_ok.GetError(), "failed to create idmap"); + LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path + << "\": " << create_ok.GetError().GetMessage(); + continue; } } diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index 4f653796ce3f..8ee79f61520a 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "idmap2d/Idmap2Service.h" + #include <sys/stat.h> // umask #include <sys/types.h> // umask #include <unistd.h> @@ -28,15 +30,12 @@ #include "android-base/macros.h" #include "android-base/stringprintf.h" #include "binder/IPCThreadState.h" -#include "utils/String8.h" - #include "idmap2/BinaryStreamVisitor.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" #include "idmap2/Policies.h" #include "idmap2/SysTrace.h" - -#include "idmap2d/Idmap2Service.h" +#include "utils/String8.h" using android::IPCThreadState; using android::binder::Status; diff --git a/cmds/idmap2/idmap2d/Main.cpp b/cmds/idmap2/idmap2d/Main.cpp index 4393dcc130ec..2707049fa677 100644 --- a/cmds/idmap2/idmap2d/Main.cpp +++ b/cmds/idmap2/idmap2d/Main.cpp @@ -21,13 +21,11 @@ #include <binder/ProcessState.h> #include <cstdlib> // EXIT_{FAILURE,SUCCESS} - #include <iostream> #include <sstream> -#include "android-base/macros.h" - #include "Idmap2Service.h" +#include "android-base/macros.h" using android::BinderService; using android::IPCThreadState; diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h index 5cc0664b2bed..ebbb5ffc989d 100644 --- a/cmds/idmap2/include/idmap2/Idmap.h +++ b/cmds/idmap2/include/idmap2/Idmap.h @@ -52,11 +52,9 @@ #include <vector> #include "android-base/macros.h" - #include "androidfw/ApkAssets.h" #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" - #include "idmap2/Policies.h" namespace android::idmap2 { diff --git a/cmds/idmap2/include/idmap2/Policies.h b/cmds/idmap2/include/idmap2/Policies.h index cd76b84ccc0a..90c698cc6e49 100644 --- a/cmds/idmap2/include/idmap2/Policies.h +++ b/cmds/idmap2/include/idmap2/Policies.h @@ -17,11 +17,10 @@ #include <string> #include <vector> +#include "Result.h" #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" -#include "Result.h" - #ifndef IDMAP2_INCLUDE_IDMAP2_POLICIES_H_ #define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_ diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h index c388f4b94251..5111bb2eaab2 100644 --- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h @@ -21,7 +21,6 @@ #include <memory> #include "androidfw/AssetManager2.h" - #include "idmap2/Idmap.h" namespace android { diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h index 7e33b3b06fc3..2e543d4fabdd 100644 --- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h @@ -22,7 +22,6 @@ #include <string> #include "androidfw/AssetManager2.h" - #include "idmap2/Idmap.h" namespace android { diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h index 1d81c486d504..8797a788dd1d 100644 --- a/cmds/idmap2/include/idmap2/ResourceUtils.h +++ b/cmds/idmap2/include/idmap2/ResourceUtils.h @@ -21,7 +21,6 @@ #include <string> #include "androidfw/AssetManager2.h" - #include "idmap2/Idmap.h" #include "idmap2/Result.h" #include "idmap2/ZipFile.h" diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp index 96513283ac51..dee2d219cbe1 100644 --- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp +++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ +#include "idmap2/BinaryStreamVisitor.h" + #include <algorithm> #include <cstring> #include <string> #include "android-base/macros.h" -#include "idmap2/BinaryStreamVisitor.h" - namespace android::idmap2 { void BinaryStreamVisitor::Write16(uint16_t value) { diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp index d5fd2ce38b11..5b0ae92df887 100644 --- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp +++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "idmap2/CommandLineOptions.h" + #include <algorithm> #include <iomanip> #include <iostream> @@ -24,8 +26,6 @@ #include <vector> #include "android-base/macros.h" - -#include "idmap2/CommandLineOptions.h" #include "idmap2/Result.h" namespace android::idmap2 { diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp index a9b68cd6d5d5..3e8e32989a09 100644 --- a/cmds/idmap2/libidmap2/FileUtils.cpp +++ b/cmds/idmap2/libidmap2/FileUtils.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "idmap2/FileUtils.h" + #include <dirent.h> #include <sys/types.h> #include <unistd.h> @@ -33,8 +35,6 @@ #include "android-base/stringprintf.h" #include "private/android_filesystem_config.h" -#include "idmap2/FileUtils.h" - namespace android::idmap2::utils { std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse, diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 49470b4f4e95..aec1a6fc2bae 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "idmap2/Idmap.h" + #include <algorithm> #include <iostream> #include <iterator> @@ -28,14 +30,12 @@ #include "android-base/macros.h" #include "android-base/stringprintf.h" #include "androidfw/AssetManager2.h" -#include "utils/String16.h" -#include "utils/String8.h" - -#include "idmap2/Idmap.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" #include "idmap2/ZipFile.h" +#include "utils/String16.h" +#include "utils/String8.h" namespace android::idmap2 { diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp index 7c4555633fd2..0a0cecf13932 100644 --- a/cmds/idmap2/libidmap2/Policies.cpp +++ b/cmds/idmap2/libidmap2/Policies.cpp @@ -14,15 +14,15 @@ * limitations under the License. */ +#include "idmap2/Policies.h" + #include <iterator> #include <map> #include <string> #include <vector> #include "androidfw/ResourceTypes.h" - #include "idmap2/Idmap.h" -#include "idmap2/Policies.h" #include "idmap2/Result.h" namespace android::idmap2 { diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp index fc9677994fc0..fbf2c777be9a 100644 --- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp @@ -14,13 +14,13 @@ * limitations under the License. */ +#include "idmap2/PrettyPrintVisitor.h" + #include <string> #include "android-base/macros.h" #include "android-base/stringprintf.h" #include "androidfw/ApkAssets.h" - -#include "idmap2/PrettyPrintVisitor.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp index 1149c905a178..dd14fd47aea8 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ +#include "idmap2/RawPrintVisitor.h" + #include <cstdarg> #include <string> #include "android-base/macros.h" #include "android-base/stringprintf.h" #include "androidfw/ApkAssets.h" - -#include "idmap2/RawPrintVisitor.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp index a24836da7f3a..71ba3f0f1ac2 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -14,13 +14,13 @@ * limitations under the License. */ +#include "idmap2/ResourceUtils.h" + #include <memory> #include <string> #include "androidfw/StringPiece.h" #include "androidfw/Util.h" - -#include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/Xml.h" #include "idmap2/ZipFile.h" diff --git a/cmds/idmap2/libidmap2/Result.cpp b/cmds/idmap2/libidmap2/Result.cpp index 471dab2e0411..1eac25f1b955 100644 --- a/cmds/idmap2/libidmap2/Result.cpp +++ b/cmds/idmap2/libidmap2/Result.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ +#include "idmap2/Result.h" + #include <cstdarg> #include "android-base/stringprintf.h" -#include "idmap2/Result.h" - namespace android::idmap2 { // NOLINTNEXTLINE(cert-dcl50-cpp) diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp index 0075a922d676..264586829c47 100644 --- a/cmds/idmap2/libidmap2/Xml.cpp +++ b/cmds/idmap2/libidmap2/Xml.cpp @@ -14,13 +14,13 @@ * limitations under the License. */ +#include "idmap2/Xml.h" + #include <map> #include <memory> #include <string> #include <utility> -#include "idmap2/Xml.h" - namespace android::idmap2 { std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) { diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp index 0f0732466256..812fd6eacbf9 100644 --- a/cmds/idmap2/libidmap2/ZipFile.cpp +++ b/cmds/idmap2/libidmap2/ZipFile.cpp @@ -14,11 +14,12 @@ * limitations under the License. */ +#include "idmap2/ZipFile.h" + #include <memory> #include <string> #include "idmap2/Result.h" -#include "idmap2/ZipFile.h" namespace android::idmap2 { diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index 9a5b6331cb20..9cdc86ca181a 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -19,17 +19,14 @@ #include <string> #include <utility> -#include "gmock/gmock.h" -#include "gtest/gtest.h" - +#include "TestHelpers.h" #include "androidfw/ApkAssets.h" #include "androidfw/Idmap.h" - +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "idmap2/BinaryStreamVisitor.h" #include "idmap2/Idmap.h" -#include "TestHelpers.h" - using ::testing::NotNull; namespace android::idmap2 { diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp index d567af64b16a..6e83fc9abdb1 100644 --- a/cmds/idmap2/tests/CommandLineOptionsTests.cpp +++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp @@ -25,19 +25,16 @@ #include <string> #include <vector> -#include "gmock/gmock.h" -#include "gtest/gtest.h" - +#include "TestHelpers.h" #include "android-base/file.h" #include "androidfw/ApkAssets.h" #include "androidfw/Idmap.h" #include "androidfw/LoadedArsc.h" - +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "idmap2/CommandLineOptions.h" #include "idmap2/Idmap.h" -#include "TestHelpers.h" - namespace android::idmap2 { TEST(CommandLineOptionsTests, Flag) { diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp index 34a0097b0316..f4a306e41e32 100644 --- a/cmds/idmap2/tests/FileUtilsTests.cpp +++ b/cmds/idmap2/tests/FileUtilsTests.cpp @@ -15,19 +15,17 @@ */ #include <dirent.h> + #include <set> #include <string> -#include "gmock/gmock.h" -#include "gtest/gtest.h" - +#include "TestHelpers.h" #include "android-base/macros.h" #include "android-base/stringprintf.h" -#include "private/android_filesystem_config.h" - +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "idmap2/FileUtils.h" - -#include "TestHelpers.h" +#include "private/android_filesystem_config.h" using ::testing::NotNull; diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp index 91bc4ddb397f..c18744ccb2d5 100644 --- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp +++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp @@ -34,16 +34,13 @@ #include <string> #include <vector> +#include "TestHelpers.h" +#include "androidfw/PosixUtils.h" #include "gmock/gmock.h" #include "gtest/gtest.h" - -#include "androidfw/PosixUtils.h" -#include "private/android_filesystem_config.h" - #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" - -#include "TestHelpers.h" +#include "private/android_filesystem_config.h" using ::android::util::ExecuteBinary; using ::testing::NotNull; @@ -264,6 +261,24 @@ TEST_F(Idmap2BinaryTests, Scan) { ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; ASSERT_EQ(result->stdout, ""); + + // the signature idmap failing to generate should not cause scanning to fail + // clang-format off + result = ExecuteBinary({"idmap2", + "scan", + "--input-directory", GetTestDataPath(), + "--recursive", + "--target-package-name", "test.target", + "--target-apk-path", GetTargetApkPath(), + "--output-directory", GetTempDirPath(), + "--override-policy", "public"}); + // clang-format on + ASSERT_THAT(result, NotNull()); + ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; + ASSERT_EQ(result->stdout, expected.str()); + unlink(idmap_static_no_name_path.c_str()); + unlink(idmap_static_2_path.c_str()); + unlink(idmap_static_1_path.c_str()); } TEST_F(Idmap2BinaryTests, Lookup) { diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 621f50337aa3..90fe9a79bd1f 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -15,7 +15,6 @@ */ #include <cstdio> // fclose - #include <fstream> #include <memory> #include <sstream> @@ -23,18 +22,15 @@ #include <utility> #include <vector> -#include "gmock/gmock.h" -#include "gtest/gtest.h" - +#include "TestHelpers.h" #include "android-base/macros.h" #include "androidfw/ApkAssets.h" - +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "idmap2/BinaryStreamVisitor.h" #include "idmap2/CommandLineOptions.h" #include "idmap2/Idmap.h" -#include "TestHelpers.h" - using ::testing::IsNull; using ::testing::NotNull; diff --git a/cmds/idmap2/tests/Main.cpp b/cmds/idmap2/tests/Main.cpp index 2b13fed8d60d..3e753e974465 100644 --- a/cmds/idmap2/tests/Main.cpp +++ b/cmds/idmap2/tests/Main.cpp @@ -16,12 +16,10 @@ #include <string> +#include "TestHelpers.h" #include "android-base/file.h" - #include "gtest/gtest.h" -#include "TestHelpers.h" - namespace android::idmap2 { std::string GetTestDataPath() { diff --git a/cmds/idmap2/tests/PoliciesTests.cpp b/cmds/idmap2/tests/PoliciesTests.cpp index a76da533cdcb..e30da76ddd98 100644 --- a/cmds/idmap2/tests/PoliciesTests.cpp +++ b/cmds/idmap2/tests/PoliciesTests.cpp @@ -16,9 +16,8 @@ #include <string> -#include "gtest/gtest.h" - #include "TestHelpers.h" +#include "gtest/gtest.h" #include "idmap2/Policies.h" using android::idmap2::PolicyBitmask; diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp index 27a3880f67b6..c41250457678 100644 --- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp @@ -18,18 +18,15 @@ #include <sstream> #include <string> -#include "gmock/gmock.h" -#include "gtest/gtest.h" - +#include "TestHelpers.h" #include "androidfw/ApkAssets.h" #include "androidfw/Idmap.h" - +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "idmap2/Idmap.h" #include "idmap2/Policies.h" #include "idmap2/PrettyPrintVisitor.h" -#include "TestHelpers.h" - using ::testing::NotNull; using android::ApkAssets; diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index 7372148f0f0e..64518fdf8dee 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -19,14 +19,12 @@ #include <sstream> #include <string> +#include "TestHelpers.h" #include "gmock/gmock.h" #include "gtest/gtest.h" - #include "idmap2/Idmap.h" #include "idmap2/RawPrintVisitor.h" -#include "TestHelpers.h" - using ::testing::NotNull; namespace android::idmap2 { diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp index ad78685646b4..9ed807ccd8f9 100644 --- a/cmds/idmap2/tests/ResourceUtilsTests.cpp +++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp @@ -17,15 +17,13 @@ #include <memory> #include <string> +#include "TestHelpers.h" +#include "androidfw/ApkAssets.h" #include "gmock/gmock.h" #include "gtest/gtest.h" - -#include "androidfw/ApkAssets.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" -#include "TestHelpers.h" - using ::testing::NotNull; namespace android::idmap2 { diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp index 5f4daed521c0..cbced0ae32fb 100644 --- a/cmds/idmap2/tests/ResultTests.cpp +++ b/cmds/idmap2/tests/ResultTests.cpp @@ -20,7 +20,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" - #include "idmap2/Result.h" namespace android::idmap2 { diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h index 45525a5b7657..adea3293534d 100644 --- a/cmds/idmap2/tests/TestHelpers.h +++ b/cmds/idmap2/tests/TestHelpers.h @@ -19,6 +19,9 @@ #include <string> +#include "gmock/gmock.h" +#include "gtest/gtest.h" + namespace android::idmap2 { const unsigned char idmap_raw_data[] = { diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp index fe79d8f2c5a9..df63211a9209 100644 --- a/cmds/idmap2/tests/XmlTests.cpp +++ b/cmds/idmap2/tests/XmlTests.cpp @@ -16,13 +16,11 @@ #include <cstdio> // fclose -#include "idmap2/Xml.h" -#include "idmap2/ZipFile.h" - +#include "TestHelpers.h" #include "gmock/gmock.h" #include "gtest/gtest.h" - -#include "TestHelpers.h" +#include "idmap2/Xml.h" +#include "idmap2/ZipFile.h" using ::testing::IsNull; using ::testing::NotNull; diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp index 79be43ce0e42..3fca43621945 100644 --- a/cmds/idmap2/tests/ZipFileTests.cpp +++ b/cmds/idmap2/tests/ZipFileTests.cpp @@ -17,13 +17,11 @@ #include <cstdio> // fclose #include <string> -#include "idmap2/Result.h" -#include "idmap2/ZipFile.h" - +#include "TestHelpers.h" #include "gmock/gmock.h" #include "gtest/gtest.h" - -#include "TestHelpers.h" +#include "idmap2/Result.h" +#include "idmap2/ZipFile.h" using ::testing::IsNull; using ::testing::NotNull; diff --git a/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml index 9e6a4536cb51..5df0bea555b1 100644 --- a/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml +++ b/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml @@ -19,5 +19,7 @@ <application android:hasCode="false"/> <overlay android:targetPackage="test.target" - android:targetName="TestResources"/> + android:targetName="TestResources" + android:isStatic="true" + android:priority="10"/> </manifest> diff --git a/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk b/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk Binary files differindex b2c490dcbb90..51e19de082ed 100644 --- a/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk +++ b/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 55d3fba8845d..d28108ceeb73 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -268,7 +268,8 @@ message Atom { TouchGestureClassified touch_gesture_classified = 177; HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true]; StyleUIChanged style_ui_changed = 179; - PrivacyIndicatorsInteracted privacy_indicators_interacted = 180; + PrivacyIndicatorsInteracted privacy_indicators_interacted = + 180 [(log_from_module) = "permissioncontroller"]; AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181; NetworkStackReported network_stack_reported = 182 [(log_from_module) = "network_stack"]; AppMovedStorageReported app_moved_storage_reported = 183; @@ -340,6 +341,7 @@ message Atom { SystemIonHeapSize system_ion_heap_size = 10056; AppsOnExternalStorageInfo apps_on_external_storage_info = 10057; FaceSettings face_settings = 10058; + CoolingDevice cooling_device = 10059; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -410,17 +412,25 @@ message KeyValuePairsAtom { * frameworks/base/services/core/java/com/android/server/stats/StatsCompanionService.java */ message ThermalThrottlingStateChanged { + // The type of temperature being reported (CPU, GPU, SKIN, etc) optional android.os.TemperatureTypeEnum sensor_type = 1; + // Throttling state, this field is DEPRECATED enum State { UNKNOWN = 0; - START = 1; - STOP = 2; + START = 1; // START indicated that throttling was triggered. + STOP = 2; // STOP indicates that throttling was cleared. } - optional State state = 2; + // Temperature in deci degrees celsius optional float temperature = 3; + + // Severity of throttling + optional android.os.ThrottlingSeverityEnum severity = 4; + + // Thermistor name + optional string sensor_name = 5; } /** @@ -3957,8 +3967,7 @@ message BatteryLevel { * Pulls the temperature of various parts of the device. * The units are tenths of a degree Celsius. Eg: 30.3C is reported as 303. * - * Pulled from: - * frameworks/base/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp + * Pulled from StatsCompanionService.java */ message Temperature { // The type of temperature being reported. Eg. CPU, GPU, SKIN, BATTERY, BCL_. @@ -3970,6 +3979,9 @@ message Temperature { // Temperature in tenths of a degree C. // For BCL, it is decimillivolt, decimilliamps, and percentage * 10. optional int32 temperature_deci_celsius = 3; + + // Relative severity of the throttling, see enum definition. + optional android.os.ThrottlingSeverityEnum severity = 4; } /** @@ -5947,3 +5959,19 @@ message FaceSettings { // Whether or not a diverse set of poses are required during enrollment. optional bool unlock_diversity_required = 6; } + +/** + * Logs cooling devices maintained by the kernel. + * + * Pulled from StatsCompanionService.java + */ +message CoolingDevice { + // The type of cooling device being reported. Eg. CPU, GPU... + optional android.os.CoolingTypeEnum device_location = 1; + // The name of the cooling device source. Eg. CPU0 + optional string device_name = 2; + // Current throttle state of the cooling device. The value can any unsigned + // integer between 0 and max_state defined in its driver. 0 means device is + // not in throttling, higher value means deeper throttling. + optional int32 state = 3; +} diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 51839c4077bc..ca73059d8b8e 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -159,6 +159,9 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // temperature {android::util::TEMPERATURE, {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}}, + // cooling_device + {android::util::COOLING_DEVICE, + {.puller = new StatsCompanionServicePuller(android::util::COOLING_DEVICE)}}, // binder_calls {android::util::BINDER_CALLS, {.additiveFields = {4, 5, 6, 8, 12}, diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 5f972c9895b4..6e935e1e56c4 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -127,7 +127,6 @@ import android.view.autofill.AutofillPopupWindow; import android.view.autofill.IAutofillWindowPresenter; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; -import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; @@ -724,7 +723,7 @@ public class Activity extends ContextThemeWrapper Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, WindowControllerCallback, - AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient { + AutofillManager.AutofillClient { private static final String TAG = "Activity"; private static final boolean DEBUG_LIFECYCLE = false; @@ -1124,12 +1123,6 @@ public class Activity extends ContextThemeWrapper return this; } - /** @hide */ - @Override - public final ContentCaptureClient getContentCaptureClient() { - return this; - } - /** * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives * lifecycle callbacks for only this Activity. @@ -6509,12 +6502,6 @@ public class Activity extends ContextThemeWrapper return getComponentName(); } - /** @hide */ - @Override - public final ComponentName contentCaptureClientGetComponentName() { - return getComponentName(); - } - /** * Retrieve a {@link SharedPreferences} object for accessing preferences * that are private to this activity. This simply calls the underlying diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 143361f721d1..5e5611bcf058 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -16,6 +16,7 @@ package android.app; +import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE; import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY; import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; @@ -27,6 +28,8 @@ import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS; import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX; import static android.view.Display.INVALID_DISPLAY; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; @@ -193,6 +196,7 @@ import java.util.Map; import java.util.Objects; import java.util.TimeZone; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; final class RemoteServiceException extends AndroidRuntimeException { public RemoteServiceException(String msg) { @@ -225,6 +229,11 @@ public final class ActivityThread extends ClientTransactionHandler { public static final boolean DEBUG_ORDER = false; private static final long MIN_TIME_BETWEEN_GCS = 5*1000; /** + * If the activity doesn't become idle in time, the timeout will ensure to apply the pending top + * process state. + */ + private static final long PENDING_TOP_PROCESS_STATE_TIMEOUT = 1000; + /** * The delay to release the provider when it has no more references. It reduces the number of * transactions for acquiring and releasing provider if the client accesses the provider * frequently in a short time. @@ -242,6 +251,11 @@ public final class ActivityThread extends ClientTransactionHandler { // Whether to invoke an activity callback after delivering new configuration. private static final boolean REPORT_TO_ACTIVITY = true; + /** Use foreground GC policy (less pause time) and higher JIT weight. */ + private static final int VM_PROCESS_STATE_JANK_PERCEPTIBLE = 0; + /** Use background GC policy and default JIT threshold. */ + private static final int VM_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1; + /** * Denotes an invalid sequence number corresponding to a process state change. */ @@ -296,6 +310,11 @@ public final class ActivityThread extends ClientTransactionHandler { // Number of activities that are currently visible on-screen. @UnsupportedAppUsage int mNumVisibleActivities = 0; + private final AtomicInteger mNumLaunchingActivities = new AtomicInteger(); + @GuardedBy("mAppThread") + private int mLastProcessState = PROCESS_STATE_UNKNOWN; + @GuardedBy("mAppThread") + private int mPendingProcessState = PROCESS_STATE_UNKNOWN; ArrayList<WeakReference<AssistStructure>> mLastAssistStructures = new ArrayList<>(); private int mLastSessionId; @UnsupportedAppUsage @@ -873,17 +892,6 @@ public final class ActivityThread extends ClientTransactionHandler { private class ApplicationThread extends IApplicationThread.Stub { private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s"; - private int mLastProcessState = -1; - - private void updatePendingConfiguration(Configuration config) { - synchronized (mResourcesManager) { - if (mPendingConfiguration == null || - mPendingConfiguration.isOtherSeqNewer(config)) { - mPendingConfiguration = config; - } - } - } - public final void scheduleSleeping(IBinder token, boolean sleeping) { sendMessage(H.SLEEPING, token, sleeping ? 1 : 0); } @@ -1560,27 +1568,6 @@ public final class ActivityThread extends ClientTransactionHandler { updateProcessState(state, true); } - public void updateProcessState(int processState, boolean fromIpc) { - synchronized (this) { - if (mLastProcessState != processState) { - mLastProcessState = processState; - // Update Dalvik state based on ActivityManager.PROCESS_STATE_* constants. - final int DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE = 0; - final int DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1; - int dalvikProcessState = DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE; - // TODO: Tune this since things like gmail sync are important background but not jank perceptible. - if (processState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { - dalvikProcessState = DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE; - } - VMRuntime.getRuntime().updateProcessState(dalvikProcessState); - if (false) { - Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState - + (fromIpc ? " (from ipc": "")); - } - } - } - } - /** * Updates {@link #mNetworkBlockSeq}. This is used by ActivityManagerService to inform * the main thread that it needs to wait for the network rules to get updated before @@ -1661,16 +1648,6 @@ public final class ActivityThread extends ClientTransactionHandler { } } - @Override - public void updatePendingConfiguration(Configuration config) { - mAppThread.updatePendingConfiguration(config); - } - - @Override - public void updateProcessState(int processState, boolean fromIpc) { - mAppThread.updateProcessState(processState, fromIpc); - } - class H extends Handler { public static final int BIND_APPLICATION = 110; @UnsupportedAppUsage @@ -1995,6 +1972,7 @@ public final class ActivityThread extends ClientTransactionHandler { if (stopProfiling) { mProfiler.stopProfiling(); } + applyPendingProcessState(); return false; } } @@ -2939,6 +2917,68 @@ public final class ActivityThread extends ClientTransactionHandler { return mActivities.get(token); } + @Override + public void updatePendingConfiguration(Configuration config) { + synchronized (mResourcesManager) { + if (mPendingConfiguration == null || mPendingConfiguration.isOtherSeqNewer(config)) { + mPendingConfiguration = config; + } + } + } + + @Override + public void updateProcessState(int processState, boolean fromIpc) { + synchronized (mAppThread) { + if (mLastProcessState == processState) { + return; + } + mLastProcessState = processState; + // Defer the top state for VM to avoid aggressive JIT compilation affecting activity + // launch time. + if (processState == ActivityManager.PROCESS_STATE_TOP + && mNumLaunchingActivities.get() > 0) { + mPendingProcessState = processState; + mH.postDelayed(this::applyPendingProcessState, PENDING_TOP_PROCESS_STATE_TIMEOUT); + } else { + mPendingProcessState = PROCESS_STATE_UNKNOWN; + updateVmProcessState(processState); + } + if (localLOGV) { + Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState + + (fromIpc ? " (from ipc" : "")); + } + } + } + + /** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */ + private void updateVmProcessState(int processState) { + // TODO: Tune this since things like gmail sync are important background but not jank + // perceptible. + final int state = processState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND + ? VM_PROCESS_STATE_JANK_PERCEPTIBLE + : VM_PROCESS_STATE_JANK_IMPERCEPTIBLE; + VMRuntime.getRuntime().updateProcessState(state); + } + + private void applyPendingProcessState() { + synchronized (mAppThread) { + if (mPendingProcessState == PROCESS_STATE_UNKNOWN) { + return; + } + final int pendingState = mPendingProcessState; + mPendingProcessState = PROCESS_STATE_UNKNOWN; + // Only apply the pending state if the last state doesn't change. + if (pendingState == mLastProcessState) { + updateVmProcessState(pendingState); + } + } + } + + @Override + public void countLaunchingActivities(int num) { + mNumLaunchingActivities.getAndAdd(num); + } + @UnsupportedAppUsage public final void sendActivityResult( IBinder token, String id, int requestCode, @@ -4509,7 +4549,7 @@ public final class ActivityThread extends ClientTransactionHandler { if (!show && !r.stopped) { performStopActivityInner(r, null /* stopInfo */, show, false /* saveState */, false /* finalStateRequest */, "handleWindowVisibility"); - } else if (show && r.stopped) { + } else if (show && r.getLifecycleState() == ON_STOP) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); @@ -4570,7 +4610,7 @@ public final class ActivityThread extends ClientTransactionHandler { private void onCoreSettingsChange() { if (updateDebugViewAttributeState()) { // request all activities to relaunch for the changes to take place - relaunchAllActivities(); + relaunchAllActivities(false /* preserveWindows */); } } @@ -4587,10 +4627,13 @@ public final class ActivityThread extends ClientTransactionHandler { return previousState != View.sDebugViewAttributes; } - private void relaunchAllActivities() { + private void relaunchAllActivities(boolean preserveWindows) { for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) { - final Activity activity = entry.getValue().activity; - if (!activity.mFinished) { + final ActivityClientRecord r = entry.getValue(); + if (!r.activity.mFinished) { + if (preserveWindows && r.window != null) { + r.mPreserveWindow = true; + } scheduleRelaunchActivity(entry.getKey()); } } @@ -5423,7 +5466,8 @@ public final class ActivityThread extends ClientTransactionHandler { } } - void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) { + @VisibleForTesting(visibility = PACKAGE) + public void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) { // Updates triggered by package installation go through a package update // receiver. Here we try to capture ApplicationInfo changes that are // caused by other sources, such as overlays. That means we want to be as conservative @@ -5469,7 +5513,8 @@ public final class ActivityThread extends ClientTransactionHandler { newConfig.assetsSeq = (mConfiguration != null ? mConfiguration.assetsSeq : 0) + 1; handleConfigurationChanged(newConfig, null); - relaunchAllActivities(); + // Preserve windows to avoid black flickers when overlays change. + relaunchAllActivities(true /* preserveWindows */); } static void freeTextLayoutCachesIfNeeded(int configDiff) { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 6f9224481eba..7243624b855a 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -823,9 +823,11 @@ public class AppOpsManager { public static final int OP_LEGACY_STORAGE = 87; /** @hide Accessing accessibility features */ public static final int OP_ACCESS_ACCESSIBILITY = 88; + /** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */ + public static final int OP_READ_DEVICE_IDENTIFIERS = 89; /** @hide */ @UnsupportedAppUsage - public static final int _NUM_OP = 89; + public static final int _NUM_OP = 90; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1099,6 +1101,8 @@ public class AppOpsManager { /** @hide Interact with accessibility. */ @SystemApi public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; + /** @hide Read device identifiers */ + public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers"; // Warning: If an permission is added here it also has to be added to // com.android.packageinstaller.permission.utils.EventLogger @@ -1259,6 +1263,7 @@ public class AppOpsManager { OP_WRITE_MEDIA_IMAGES, // WRITE_MEDIA_IMAGES OP_LEGACY_STORAGE, // LEGACY_STORAGE OP_ACCESS_ACCESSIBILITY, // ACCESS_ACCESSIBILITY + OP_READ_DEVICE_IDENTIFIERS, // READ_DEVICE_IDENTIFIERS }; /** @@ -1354,6 +1359,7 @@ public class AppOpsManager { OPSTR_WRITE_MEDIA_IMAGES, OPSTR_LEGACY_STORAGE, OPSTR_ACCESS_ACCESSIBILITY, + OPSTR_READ_DEVICE_IDENTIFIERS, }; /** @@ -1450,6 +1456,7 @@ public class AppOpsManager { "WRITE_MEDIA_IMAGES", "LEGACY_STORAGE", "ACCESS_ACCESSIBILITY", + "READ_DEVICE_IDENTIFIERS", }; /** @@ -1547,6 +1554,7 @@ public class AppOpsManager { null, // no permission for OP_WRITE_MEDIA_IMAGES null, // no permission for OP_LEGACY_STORAGE null, // no permission for OP_ACCESS_ACCESSIBILITY + null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS }; /** @@ -1644,6 +1652,7 @@ public class AppOpsManager { null, // WRITE_MEDIA_IMAGES null, // LEGACY_STORAGE null, // ACCESS_ACCESSIBILITY + null, // READ_DEVICE_IDENTIFIERS }; /** @@ -1740,6 +1749,7 @@ public class AppOpsManager { false, // WRITE_MEDIA_IMAGES false, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY + false, // READ_DEVICE_IDENTIFIERS }; /** @@ -1752,21 +1762,21 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // VIBRATE AppOpsManager.MODE_ALLOWED, // READ_CONTACTS AppOpsManager.MODE_ALLOWED, // WRITE_CONTACTS - AppOpsManager.MODE_DEFAULT, // READ_CALL_LOG - AppOpsManager.MODE_DEFAULT, // WRITE_CALL_LOG + AppOpsManager.MODE_ALLOWED, // READ_CALL_LOG + AppOpsManager.MODE_ALLOWED, // WRITE_CALL_LOG AppOpsManager.MODE_ALLOWED, // READ_CALENDAR AppOpsManager.MODE_ALLOWED, // WRITE_CALENDAR AppOpsManager.MODE_ALLOWED, // WIFI_SCAN AppOpsManager.MODE_ALLOWED, // POST_NOTIFICATION AppOpsManager.MODE_ALLOWED, // NEIGHBORING_CELLS AppOpsManager.MODE_ALLOWED, // CALL_PHONE - AppOpsManager.MODE_DEFAULT, // READ_SMS + AppOpsManager.MODE_ALLOWED, // READ_SMS AppOpsManager.MODE_IGNORED, // WRITE_SMS - AppOpsManager.MODE_DEFAULT, // RECEIVE_SMS + AppOpsManager.MODE_ALLOWED, // RECEIVE_SMS AppOpsManager.MODE_ALLOWED, // RECEIVE_EMERGENCY_BROADCAST - AppOpsManager.MODE_DEFAULT, // RECEIVE_MMS - AppOpsManager.MODE_DEFAULT, // RECEIVE_WAP_PUSH - AppOpsManager.MODE_DEFAULT, // SEND_SMS + AppOpsManager.MODE_ALLOWED, // RECEIVE_MMS + AppOpsManager.MODE_ALLOWED, // RECEIVE_WAP_PUSH + AppOpsManager.MODE_ALLOWED, // SEND_SMS AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS @@ -1800,10 +1810,10 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // READ_PHONE_STATE AppOpsManager.MODE_ALLOWED, // ADD_VOICEMAIL AppOpsManager.MODE_ALLOWED, // USE_SIP - AppOpsManager.MODE_DEFAULT, // PROCESS_OUTGOING_CALLS + AppOpsManager.MODE_ALLOWED, // PROCESS_OUTGOING_CALLS AppOpsManager.MODE_ALLOWED, // USE_FINGERPRINT AppOpsManager.MODE_ALLOWED, // BODY_SENSORS - AppOpsManager.MODE_DEFAULT, // READ_CELL_BROADCASTS + AppOpsManager.MODE_ALLOWED, // READ_CELL_BROADCASTS AppOpsManager.MODE_ERRORED, // MOCK_LOCATION AppOpsManager.MODE_ALLOWED, // READ_EXTERNAL_STORAGE AppOpsManager.MODE_ALLOWED, // WRITE_EXTERNAL_STORAGE @@ -1835,6 +1845,7 @@ public class AppOpsManager { AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY + AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS }; /** @@ -1934,6 +1945,7 @@ public class AppOpsManager { false, // WRITE_MEDIA_IMAGES false, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY + false, // READ_DEVICE_IDENTIFIERS }; /** diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index 9dc8b45a71dc..d308adc52e15 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -78,6 +78,8 @@ public abstract class ClientTransactionHandler { /** Set current process state. */ public abstract void updateProcessState(int processState, boolean fromIpc); + /** Count how many activities are launching. */ + public abstract void countLaunchingActivities(int num); // Execute phase related logic and handlers. Methods here execute actual lifecycle transactions // and deliver callbacks. diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index 14c58e744dac..def1f457fb4a 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -27,5 +27,15 @@ } ] } + ], + "postsubmit": [ + { + "file_patterns": ["(/|^)ActivityThreadClientTest.java"], + "name": "FrameworksMockingCoreTests" + }, + { + "file_patterns": ["(/|^)ActivityThreadTest.java"], + "name": "FrameworksCoreTests" + } ] } diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index dd00e5a74382..de64db9def64 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -16,6 +16,8 @@ package android.app; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Intent; @@ -58,23 +60,27 @@ public class TaskInfo { * The base intent of the task (generally the intent that launched the task). This intent can * be used to relaunch the task (if it is no longer running) or brought to the front if it is. */ + @NonNull public Intent baseIntent; /** * The component of the first activity in the task, can be considered the "application" of this * task. */ + @Nullable public ComponentName baseActivity; /** * The component of the top activity in the task, currently showing to the user. */ + @Nullable public ComponentName topActivity; /** * The component of the target activity if this task was started from an activity alias. * Otherwise, this is null. */ + @Nullable public ComponentName origActivity; /** @@ -82,6 +88,7 @@ public class TaskInfo { * alias). * @hide */ + @Nullable public ComponentName realActivity; /** @@ -106,6 +113,7 @@ public class TaskInfo { * The recent activity values for the highest activity in the stack to have set the values. * {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}. */ + @Nullable public ActivityManager.TaskDescription taskDescription; /** @@ -126,6 +134,7 @@ public class TaskInfo { * The current configuration of the task. * @hide */ + @NonNull @UnsupportedAppUsage public final Configuration configuration = new Configuration(); diff --git a/core/java/android/app/Vr2dDisplayProperties.java b/core/java/android/app/Vr2dDisplayProperties.java index bcb8592183e9..fc200bf05253 100644 --- a/core/java/android/app/Vr2dDisplayProperties.java +++ b/core/java/android/app/Vr2dDisplayProperties.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -136,7 +137,7 @@ public final class Vr2dDisplayProperties implements Parcelable { /** * Prints out dump info. */ - public void dump(PrintWriter pw, String prefix) { + public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { pw.println(prefix + toString()); } @@ -188,7 +189,7 @@ public final class Vr2dDisplayProperties implements Parcelable { /** * Convenience class for creating Vr2dDisplayProperties. */ - public static class Builder { + public static final class Builder { private int mAddedFlags = 0; private int mRemovedFlags = 0; @@ -203,6 +204,7 @@ public final class Vr2dDisplayProperties implements Parcelable { /** * Sets the dimensions to use for the virtual display. */ + @NonNull public Builder setDimensions(int width, int height, int dpi) { mWidth = width; mHeight = height; @@ -213,6 +215,7 @@ public final class Vr2dDisplayProperties implements Parcelable { /** * Toggles the virtual display functionality for 2D activities in VR. */ + @NonNull public Builder setEnabled(boolean enabled) { if (enabled) { addFlags(FLAG_VIRTUAL_DISPLAY_ENABLED); @@ -225,6 +228,7 @@ public final class Vr2dDisplayProperties implements Parcelable { /** * Adds property flags. */ + @NonNull public Builder addFlags(@Vr2dDisplayFlag int flags) { mAddedFlags |= flags; mRemovedFlags &= ~flags; @@ -234,6 +238,7 @@ public final class Vr2dDisplayProperties implements Parcelable { /** * Removes property flags. */ + @NonNull public Builder removeFlags(@Vr2dDisplayFlag int flags) { mRemovedFlags |= flags; mAddedFlags &= ~flags; @@ -243,6 +248,7 @@ public final class Vr2dDisplayProperties implements Parcelable { /** * Builds the Vr2dDisplayProperty instance. */ + @NonNull public Vr2dDisplayProperties build() { return new Vr2dDisplayProperties(mWidth, mHeight, mDpi, mAddedFlags, mRemovedFlags); } diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java index 5f1a94c835c3..c74f8c389c20 100644 --- a/core/java/android/app/VrManager.java +++ b/core/java/android/app/VrManager.java @@ -2,6 +2,7 @@ package android.app; import android.annotation.CallbackExecutor; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -71,7 +72,7 @@ public class VrManager { android.Manifest.permission.ACCESS_VR_STATE }) public void registerVrStateCallback(@NonNull @CallbackExecutor Executor executor, - VrStateCallback callback) { + @NonNull VrStateCallback callback) { if (callback == null || mCallbackMap.containsKey(callback)) { return; } @@ -99,7 +100,7 @@ public class VrManager { android.Manifest.permission.RESTRICTED_VR_ACCESS, android.Manifest.permission.ACCESS_VR_STATE }) - public void unregisterVrStateCallback(VrStateCallback callback) { + public void unregisterVrStateCallback(@NonNull VrStateCallback callback) { CallbackEntry entry = mCallbackMap.remove(callback); if (entry != null) { try { @@ -175,7 +176,7 @@ public class VrManager { */ @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public void setVr2dDisplayProperties( - Vr2dDisplayProperties vr2dDisplayProp) { + @NonNull Vr2dDisplayProperties vr2dDisplayProp) { try { mService.setVr2dDisplayProperties(vr2dDisplayProp); } catch (RemoteException e) { @@ -220,7 +221,7 @@ public class VrManager { * @param componentName not used */ @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) - public void setVrInputMethod(ComponentName componentName) { + public void setVrInputMethod(@Nullable ComponentName componentName) { } /** diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java index 3f2f2090bf33..cd635d635ce1 100644 --- a/core/java/android/app/prediction/AppPredictor.java +++ b/core/java/android/app/prediction/AppPredictor.java @@ -271,7 +271,9 @@ public final class AppPredictor { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } - destroy(); + if (!mIsClosed.get()) { + destroy(); + } } finally { super.finalize(); } diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java index ed45b2f45383..61e4569c1228 100644 --- a/core/java/android/app/prediction/AppTarget.java +++ b/core/java/android/app/prediction/AppTarget.java @@ -204,24 +204,49 @@ public final class AppTarget implements Parcelable { private int mRank; /** - * @param id A unique id for this launchable target. + * @deprecated Use the other Builder constructors. * @hide */ + @Deprecated @SystemApi - @TestApi public Builder(@NonNull AppTargetId id) { mId = id; } /** - * Sets the target to be an app. - * - * @param packageName PackageName of the app + * @param id A unique id for this launchable target. + * @param packageName PackageName of the target. * @param user The UserHandle of the user which this target belongs to. - * - * @throws IllegalArgumentException is the target is already set + * @hide + */ + @SystemApi + @TestApi + public Builder(@NonNull AppTargetId id, @NonNull String packageName, + @NonNull UserHandle user) { + mId = Preconditions.checkNotNull(id); + mPackageName = Preconditions.checkNotNull(packageName); + mUser = Preconditions.checkNotNull(user); + } + + /** + * @param id A unique id for this launchable target. + * @param info The ShortcutInfo that represents this launchable target. + * @hide + */ + @SystemApi + @TestApi + public Builder(@NonNull AppTargetId id, @NonNull ShortcutInfo info) { + mId = Preconditions.checkNotNull(id); + mShortcutInfo = Preconditions.checkNotNull(info); + mPackageName = info.getPackage(); + mUser = info.getUserHandle(); + } + + /** + * @deprecated Use the appropriate constructor. */ @NonNull + @Deprecated public Builder setTarget(@NonNull String packageName, @NonNull UserHandle user) { if (mPackageName != null) { throw new IllegalArgumentException("Target is already set"); @@ -232,11 +257,10 @@ public final class AppTarget implements Parcelable { } /** - * Sets the target to be a ShortcutInfo. - * - * @throws IllegalArgumentException is the target is already set + * @deprecated Use the appropriate constructor. */ @NonNull + @Deprecated public Builder setTarget(@NonNull ShortcutInfo info) { setTarget(info.getPackage(), info.getUserHandle()); mShortcutInfo = Preconditions.checkNotNull(info); @@ -244,7 +268,7 @@ public final class AppTarget implements Parcelable { } /** - * Sets the className for the target + * Sets the className for the target. */ @NonNull public Builder setClassName(@NonNull String className) { @@ -253,7 +277,7 @@ public final class AppTarget implements Parcelable { } /** - * Sets the rank of the for the target. + * Sets the rank of the target. */ @NonNull public Builder setRank(@IntRange(from = 0) int rank) { @@ -274,7 +298,7 @@ public final class AppTarget implements Parcelable { @NonNull public AppTarget build() { if (mPackageName == null) { - throw new IllegalStateException("No target set"); + throw new IllegalStateException("No target is set"); } return new AppTarget(mId, mPackageName, mUser, mShortcutInfo, mClassName, mRank); } diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java index 027e152ee95f..394a0d64b3e4 100644 --- a/core/java/android/app/role/RoleControllerManager.java +++ b/core/java/android/app/role/RoleControllerManager.java @@ -52,7 +52,10 @@ public class RoleControllerManager { private static final String LOG_TAG = RoleControllerManager.class.getSimpleName(); + private static volatile ComponentName sRemoteServiceComponentName; + private static final Object sRemoteServicesLock = new Object(); + /** * Global remote services (per user) used by all {@link RoleControllerManager managers}. */ @@ -62,18 +65,36 @@ public class RoleControllerManager { @NonNull private final RemoteService mRemoteService; - public RoleControllerManager(@NonNull Context context, @NonNull Handler handler) { + /** + * Initialize the remote service component name once so that we can avoid acquiring the + * PackageManagerService lock in constructor. + * + * @see #createWithInitializedRemoteServiceComponentName(Handler, Context) + */ + public static void initializeRemoteServiceComponentName(@NonNull Context context) { + sRemoteServiceComponentName = getRemoteServiceComponentName(context); + } + + /** + * Create a {@link RoleControllerManager} instance with the initialized remote service component + * name so that we can avoid acquiring the PackageManagerService lock in constructor. + * + * @see #initializeRemoteServiceComponentName(Context) + */ + @NonNull + public static RoleControllerManager createWithInitializedRemoteServiceComponentName( + @NonNull Handler handler, @NonNull Context context) { + return new RoleControllerManager(sRemoteServiceComponentName, handler, context); + } + + private RoleControllerManager(@NonNull ComponentName remoteServiceComponentName, + @NonNull Handler handler, @NonNull Context context) { synchronized (sRemoteServicesLock) { int userId = context.getUserId(); RemoteService remoteService = sRemoteServices.get(userId); if (remoteService == null) { - Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE); - PackageManager packageManager = context.getPackageManager(); - intent.setPackage(packageManager.getPermissionControllerPackageName()); - ResolveInfo resolveInfo = packageManager.resolveService(intent, 0); - remoteService = new RemoteService(context.getApplicationContext(), - resolveInfo.getComponentInfo().getComponentName(), handler, userId); + remoteServiceComponentName, handler, userId); sRemoteServices.put(userId, remoteService); } mRemoteService = remoteService; @@ -81,7 +102,16 @@ public class RoleControllerManager { } public RoleControllerManager(@NonNull Context context) { - this(context, context.getMainThreadHandler()); + this(getRemoteServiceComponentName(context), context.getMainThreadHandler(), context); + } + + @NonNull + private static ComponentName getRemoteServiceComponentName(@NonNull Context context) { + Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE); + PackageManager packageManager = context.getPackageManager(); + intent.setPackage(packageManager.getPermissionControllerPackageName()); + ResolveInfo resolveInfo = packageManager.resolveService(intent, 0); + return resolveInfo.getComponentInfo().getComponentName(); } /** diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index db22f8d1de87..cdf5d4912ad5 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -66,6 +66,7 @@ public class LaunchActivityItem extends ClientTransactionItem { @Override public void preExecute(ClientTransactionHandler client, IBinder token) { + client.countLaunchingActivities(1); client.updateProcessState(mProcState, false); client.updatePendingConfiguration(mCurConfig); } @@ -82,6 +83,12 @@ public class LaunchActivityItem extends ClientTransactionItem { Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } + @Override + public void postExecute(ClientTransactionHandler client, IBinder token, + PendingTransactionActions pendingActions) { + client.countLaunchingActivities(-1); + } + // ObjectPoolItem implementation diff --git a/core/java/android/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java index fa3d3b8dea0c..941e9e2ecce5 100644 --- a/core/java/android/attention/AttentionManagerInternal.java +++ b/core/java/android/attention/AttentionManagerInternal.java @@ -46,13 +46,6 @@ public abstract class AttentionManagerInternal { */ public abstract void cancelAttentionCheck(AttentionCallbackInternal callback); - /** - * Disables the dependants. - * - * Example: called if the service does not have sufficient permissions to perform the task. - */ - public abstract void disableSelf(); - /** Internal interface for attention callback. */ public abstract static class AttentionCallbackInternal { /** diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java index 2174255a3619..30868bfeac66 100644 --- a/core/java/android/bluetooth/le/ScanRecord.java +++ b/core/java/android/bluetooth/le/ScanRecord.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothUuid; @@ -97,7 +98,7 @@ public final class ScanRecord { * Returns a list of service solicitation UUIDs within the advertisement that are used to * identify the Bluetooth GATT services. */ - @Nullable + @NonNull public List<ParcelUuid> getServiceSolicitationUuids() { return mServiceSolicitationUuids; } @@ -297,9 +298,6 @@ public final class ScanRecord { if (serviceUuids.isEmpty()) { serviceUuids = null; } - if (serviceSolicitationUuids.isEmpty()) { - serviceSolicitationUuids = null; - } return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData, serviceData, advertiseFlag, txPowerLevel, localName, scanRecord); } catch (Exception e) { diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java index f59bc9891c86..8fb9501b3319 100644 --- a/core/java/android/content/AutofillOptions.java +++ b/core/java/android/content/AutofillOptions.java @@ -24,7 +24,7 @@ import android.os.Parcelable; import android.util.ArraySet; import android.util.Log; import android.view.autofill.AutofillManager; -import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; +import android.view.autofill.AutofillManager.AutofillClient; import java.io.PrintWriter; @@ -73,10 +73,10 @@ public final class AutofillOptions implements Parcelable { public boolean isAugmentedAutofillEnabled(@NonNull Context context) { if (!augmentedAutofillEnabled) return false; - final ContentCaptureClient contentCaptureClient = context.getContentCaptureClient(); - if (contentCaptureClient == null) return false; + final AutofillClient autofillClient = context.getAutofillClient(); + if (autofillClient == null) return false; - final ComponentName component = contentCaptureClient.contentCaptureClientGetComponentName(); + final ComponentName component = autofillClient.autofillClientGetComponentName(); return whitelistedActivitiesForAugmentedAutofill == null || whitelistedActivitiesForAugmentedAutofill.contains(component); } diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java index a41b5d3fb781..c201e4d2a129 100644 --- a/core/java/android/content/ContentProviderOperation.java +++ b/core/java/android/content/ContentProviderOperation.java @@ -17,7 +17,6 @@ package android.content; import android.annotation.UnsupportedAppUsage; -import android.content.ContentProvider; import android.database.Cursor; import android.net.Uri; import android.os.Parcel; @@ -59,6 +58,7 @@ public class ContentProviderOperation implements Parcelable { private final ContentValues mValuesBackReferences; private final Map<Integer, Integer> mSelectionArgsBackReferences; private final boolean mYieldAllowed; + private final boolean mFailureAllowed; private final static String TAG = "ContentProviderOperation"; @@ -76,6 +76,7 @@ public class ContentProviderOperation implements Parcelable { mSelectionArgsBackReferences = builder.mSelectionArgsBackReferences; mValuesBackReferences = builder.mValuesBackReferences; mYieldAllowed = builder.mYieldAllowed; + mFailureAllowed = builder.mFailureAllowed; } private ContentProviderOperation(Parcel source) { @@ -98,6 +99,7 @@ public class ContentProviderOperation implements Parcelable { } } mYieldAllowed = source.readInt() != 0; + mFailureAllowed = source.readInt() != 0; } /** @hide */ @@ -111,6 +113,7 @@ public class ContentProviderOperation implements Parcelable { mSelectionArgsBackReferences = cpo.mSelectionArgsBackReferences; mValuesBackReferences = cpo.mValuesBackReferences; mYieldAllowed = cpo.mYieldAllowed; + mFailureAllowed = cpo.mFailureAllowed; } public void writeToParcel(Parcel dest, int flags) { @@ -157,6 +160,7 @@ public class ContentProviderOperation implements Parcelable { dest.writeInt(0); } dest.writeInt(mYieldAllowed ? 1 : 0); + dest.writeInt(mFailureAllowed ? 1 : 0); } /** @@ -212,6 +216,11 @@ public class ContentProviderOperation implements Parcelable { return mYieldAllowed; } + /** {@hide} */ + public boolean isFailureAllowed() { + return mFailureAllowed; + } + /** @hide exposed for unit tests */ @UnsupportedAppUsage public int getType() { @@ -290,19 +299,35 @@ public class ContentProviderOperation implements Parcelable { */ public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs, int numBackRefs) throws OperationApplicationException { + if (mFailureAllowed) { + try { + return applyInternal(provider, backRefs, numBackRefs); + } catch (Exception e) { + return new ContentProviderResult(e.getMessage()); + } + } else { + return applyInternal(provider, backRefs, numBackRefs); + } + } + + private ContentProviderResult applyInternal(ContentProvider provider, + ContentProviderResult[] backRefs, int numBackRefs) + throws OperationApplicationException { ContentValues values = resolveValueBackReferences(backRefs, numBackRefs); String[] selectionArgs = resolveSelectionArgsBackReferences(backRefs, numBackRefs); if (mType == TYPE_INSERT) { - Uri newUri = provider.insert(mUri, values); - if (newUri == null) { - throw new OperationApplicationException("insert failed"); + final Uri newUri = provider.insert(mUri, values); + if (newUri != null) { + return new ContentProviderResult(newUri); + } else { + throw new OperationApplicationException( + "Insert into " + mUri + " returned no result"); } - return new ContentProviderResult(newUri); } - int numRows; + final int numRows; if (mType == TYPE_DELETE) { numRows = provider.delete(mUri, mSelection, selectionArgs); } else if (mType == TYPE_UPDATE) { @@ -328,7 +353,6 @@ public class ContentProviderOperation implements Parcelable { final String expectedValue = values.getAsString(projection[i]); if (!TextUtils.equals(cursorValue, expectedValue)) { // Throw exception when expected values don't match - Log.e(TAG, this.toString()); throw new OperationApplicationException("Found value " + cursorValue + " when expected " + expectedValue + " for column " + projection[i]); @@ -340,13 +364,12 @@ public class ContentProviderOperation implements Parcelable { cursor.close(); } } else { - Log.e(TAG, this.toString()); throw new IllegalStateException("bad type, " + mType); } if (mExpectedCount != null && mExpectedCount != numRows) { - Log.e(TAG, this.toString()); - throw new OperationApplicationException("wrong number of rows: " + numRows); + throw new OperationApplicationException( + "Expected " + mExpectedCount + " rows but actual " + numRows); } return new ContentProviderResult(numRows); @@ -491,6 +514,7 @@ public class ContentProviderOperation implements Parcelable { private ContentValues mValuesBackReferences; private Map<Integer, Integer> mSelectionArgsBackReferences; private boolean mYieldAllowed; + private boolean mFailureAllowed; /** Create a {@link Builder} of a given type. The uri must not be null. */ private Builder(int type, Uri uri) { @@ -683,5 +707,11 @@ public class ContentProviderOperation implements Parcelable { mYieldAllowed = yieldAllowed; return this; } + + /** {@hide} */ + public Builder withFailureAllowed(boolean failureAllowed) { + mFailureAllowed = failureAllowed; + return this; + } } } diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java index d90173ceb3cd..b3010116a115 100644 --- a/core/java/android/content/ContentProviderResult.java +++ b/core/java/android/content/ContentProviderResult.java @@ -16,10 +16,11 @@ package android.content; -import android.content.ContentProvider; import android.net.Uri; -import android.os.Parcelable; import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; /** * Contains the result of the application of a {@link ContentProviderOperation}. It is guaranteed @@ -28,26 +29,44 @@ import android.os.Parcel; public class ContentProviderResult implements Parcelable { public final Uri uri; public final Integer count; + /** {@hide} */ + public final String failure; public ContentProviderResult(Uri uri) { - if (uri == null) throw new IllegalArgumentException("uri must not be null"); - this.uri = uri; - this.count = null; + this(Preconditions.checkNotNull(uri), null, null); } public ContentProviderResult(int count) { + this(null, count, null); + } + + /** {@hide} */ + public ContentProviderResult(String failure) { + this(null, null, failure); + } + + /** {@hide} */ + public ContentProviderResult(Uri uri, Integer count, String failure) { + this.uri = uri; this.count = count; - this.uri = null; + this.failure = failure; } public ContentProviderResult(Parcel source) { - int type = source.readInt(); - if (type == 1) { - count = source.readInt(); + if (source.readInt() != 0) { + uri = Uri.CREATOR.createFromParcel(source); + } else { uri = null; + } + if (source.readInt() != 0) { + count = source.readInt(); } else { count = null; - uri = Uri.CREATOR.createFromParcel(source); + } + if (source.readInt() != 0) { + failure = source.readString(); + } else { + failure = null; } } @@ -55,37 +74,63 @@ public class ContentProviderResult implements Parcelable { public ContentProviderResult(ContentProviderResult cpr, int userId) { uri = ContentProvider.maybeAddUserId(cpr.uri, userId); count = cpr.count; + failure = cpr.failure; } + @Override public void writeToParcel(Parcel dest, int flags) { - if (uri == null) { + if (uri != null) { + dest.writeInt(1); + uri.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (count != null) { dest.writeInt(1); dest.writeInt(count); } else { - dest.writeInt(2); - uri.writeToParcel(dest, 0); + dest.writeInt(0); + } + if (failure != null) { + dest.writeInt(1); + dest.writeString(failure); + } else { + dest.writeInt(0); } } + @Override public int describeContents() { return 0; } public static final @android.annotation.NonNull Creator<ContentProviderResult> CREATOR = new Creator<ContentProviderResult>() { + @Override public ContentProviderResult createFromParcel(Parcel source) { return new ContentProviderResult(source); } + @Override public ContentProviderResult[] newArray(int size) { return new ContentProviderResult[size]; } }; + @Override public String toString() { + final StringBuilder sb = new StringBuilder("ContentProviderResult("); + if (uri != null) { + sb.append("uri=" + uri + " "); + } + if (count != null) { + sb.append("count=" + count + " "); + } if (uri != null) { - return "ContentProviderResult(uri=" + uri.toString() + ")"; + sb.append("failure=" + failure + " "); } - return "ContentProviderResult(count=" + count + ")"; + sb.deleteCharAt(sb.length() - 1); + sb.append(")"); + return sb.toString(); } } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 5a844aab02f0..0ba457e65c67 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -70,7 +70,6 @@ import android.view.View; import android.view.ViewDebug; import android.view.WindowManager; import android.view.autofill.AutofillManager.AutofillClient; -import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.view.textclassifier.TextClassificationManager; import java.io.File; @@ -5414,14 +5413,6 @@ public abstract class Context { /** * @hide */ - @Nullable - public ContentCaptureClient getContentCaptureClient() { - return null; - } - - /** - * @hide - */ public final boolean isAutofillCompatibilityEnabled() { final AutofillOptions options = getAutofillOptions(); return options != null && options.compatModeEnabled; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index deb181f4fd0a..9bc5f8055617 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -679,13 +679,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int PRIVATE_FLAG_IS_RESOURCE_OVERLAY = 1 << 28; /** - * Value for {@link #privateFlags}: If {@code true} this app allows - * shared/external storage media to be a sandboxed view that only contains - * files owned by the app. + * Value for {@link #privateFlags}: If {@code true} this app requests + * full external storage access. The request may not be honored due to + * policy or other reasons. * * @hide */ - public static final int PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX = 1 << 29; + public static final int PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE = 1 << 29; /** * Value for {@link #privateFlags}: whether this app is pre-installed on the @@ -723,7 +723,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { PRIVATE_FLAG_HAS_FRAGILE_USER_DATA, PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, - PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX, + PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE, PRIVATE_FLAG_ODM, }) @Retention(RetentionPolicy.SOURCE) @@ -1858,13 +1858,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } /** - * If {@code true} this app allows shared/external storage media to be a - * sandboxed view that only contains files owned by the app. + * If {@code true} this app requested to run in the legacy storage mode. * * @hide */ - public boolean isExternalStorageSandboxAllowed() { - return (privateFlags & PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX) != 0; + public boolean hasRequestedLegacyExternalStorage() { + return (privateFlags & PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0; } private boolean isAllowedToUseHiddenApis() { diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 6ce682843ed5..d2f0fb3cc768 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1505,13 +1505,14 @@ public class PackageInstaller { * allows the app to hold that permission and whitelisting a soft restricted * permission allows the app to hold the permission in its full, unrestricted form. * - * <p>The whitelisted permissions would be applied as the {@link - * PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist}. + * <p> Permissions can also be immutably restricted which means that the whitelist + * state of the permission can be determined only at install time and cannot be + * changed on updated or at a later point via the package manager APIs. * - * @param permissions The restricted permissions to whitelist. Pass - * {@link #RESTRICTED_PERMISSIONS_ALL} to whitelist all permissions and - * <code>null</code> to clear. If you want to whitelist some permissions - * (not all) the list must contain at least one permission. + * <p>The whitelisted non-immutably restricted permissions would be added to + * the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist} + * while the immutably restricted permissions would be added to the {@link + * PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM system whitelist} * * @see PackageManager#addWhitelistedRestrictedPermission(String, String, int) * @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int) diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index bdd80e325c4b..0ea5200d202a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3700,9 +3700,9 @@ public class PackageParser { } if (sa.getBoolean( - R.styleable.AndroidManifestApplication_allowExternalStorageSandbox, - owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) { - ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX; + R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, + owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q)) { + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE; } ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0); diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index f838900d43d7..14340fe788f7 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -341,6 +341,17 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_SOFT_RESTRICTED = 1<<3; /** + * Flag for {@link #flags}, corresponding to <code>immutablyRestricted</code> + * value of {@link android.R.attr#permissionFlags}. + * + * <p>This permission is restricted immutably which means that its + * restriction state may be specified only on the first install of + * the app and will stay in this initial whitelist state until + * the app is uninstalled. + */ + public static final int FLAG_IMMUTABLY_RESTRICTED = 1<<4; + + /** * Flag for {@link #flags}, indicating that this permission has been * installed into the system's globally defined permissions. */ diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index aff385dc23e1..d05ba799205c 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -428,17 +428,33 @@ public final class HdmiControlManager { } /** - * Get a snapshot of the real-time status of the remote devices. + * Get a snapshot of the real-time status of the devices on the CEC bus. * - * <p>This only applies to devices with multiple HDMI inputs. + * <p>This only applies to devices with switch functionality, which are devices with one + * or more than one HDMI inputs. * - * @return a list of {@link HdmiDeviceInfo} of the connected CEC devices. An empty - * list will be returned if there is none. + * @return a list of {@link HdmiDeviceInfo} of the connected CEC devices on the CEC bus. An + * empty list will be returned if there is none. * * @hide */ + @NonNull + @SystemApi + public List<HdmiDeviceInfo> getConnectedDevices() { + try { + return mService.getDeviceList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @removed + * @hide + * @deprecated Please use {@link #getConnectedDevices()} instead. + */ + @Deprecated @SystemApi - @Nullable public List<HdmiDeviceInfo> getConnectedDevicesList() { try { return mService.getDeviceList(); @@ -448,7 +464,8 @@ public final class HdmiControlManager { } /** - * Power off the target device by sending CEC commands. + * Power off the target device by sending CEC commands. Note that this device can't be the + * current device itself. * * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}. * @@ -457,6 +474,23 @@ public final class HdmiControlManager { * @hide */ @SystemApi + public void powerOffDevice(@NonNull HdmiDeviceInfo deviceInfo) { + Preconditions.checkNotNull(deviceInfo); + try { + mService.powerOffRemoteDevice( + deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @removed + * @hide + * @deprecated Please use {@link #powerOffDevice(deviceInfo)} instead. + */ + @Deprecated + @SystemApi public void powerOffRemoteDevice(@NonNull HdmiDeviceInfo deviceInfo) { Preconditions.checkNotNull(deviceInfo); try { @@ -468,7 +502,8 @@ public final class HdmiControlManager { } /** - * Power on the target device by sending CEC commands. + * Power on the target device by sending CEC commands. Note that this device can't be the + * current device itself. * * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}. * @@ -476,6 +511,23 @@ public final class HdmiControlManager { * * @hide */ + public void powerOnDevice(HdmiDeviceInfo deviceInfo) { + Preconditions.checkNotNull(deviceInfo); + try { + mService.powerOnRemoteDevice( + deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @removed + * @hide + * @deprecated Please use {@link #powerOnDevice(deviceInfo)} instead. + */ + @Deprecated + @SystemApi public void powerOnRemoteDevice(HdmiDeviceInfo deviceInfo) { Preconditions.checkNotNull(deviceInfo); try { @@ -487,15 +539,35 @@ public final class HdmiControlManager { } /** - * Request the target device to be the new Active Source by sending CEC commands. + * Request the target device to be the new Active Source by sending CEC commands. Note that + * this device can't be the current device itself. * * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}. * + * <p>If the target device responds to the command, the users should see the target device + * streaming on their TVs. + * * @param deviceInfo HdmiDeviceInfo of the target device * * @hide */ @SystemApi + public void setActiveSource(@NonNull HdmiDeviceInfo deviceInfo) { + Preconditions.checkNotNull(deviceInfo); + try { + mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @removed + * @hide + * @deprecated Please use {@link #setActiveSource(deviceInfo)} instead. + */ + @Deprecated + @SystemApi public void requestRemoteDeviceToBecomeActiveSource(@NonNull HdmiDeviceInfo deviceInfo) { Preconditions.checkNotNull(deviceInfo); try { @@ -556,7 +628,7 @@ public final class HdmiControlManager { } /** - * Check if the target remote device is connected to the current device. + * Check if the target device is connected to the current device. * * <p>The API also returns true if the current device is the target. * @@ -567,6 +639,27 @@ public final class HdmiControlManager { * @hide */ @SystemApi + public boolean isDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) { + Preconditions.checkNotNull(targetDevice); + mPhysicalAddress = getPhysicalAddress(); + if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { + return false; + } + int targetPhysicalAddress = targetDevice.getPhysicalAddress(); + if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { + return false; + } + return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, mPhysicalAddress) + != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE; + } + + /** + * @removed + * @hide + * @deprecated Please use {@link #isDeviceConnected(targetDevice)} instead. + */ + @Deprecated + @SystemApi public boolean isRemoteDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) { Preconditions.checkNotNull(targetDevice); mPhysicalAddress = getPhysicalAddress(); diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java index 0604f6a69d00..4b02085726f1 100644 --- a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java +++ b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java @@ -374,4 +374,15 @@ public final class MultiClientInputMethodServiceDelegate { public boolean isUidAllowedOnDisplay(int displayId, int uid) { return mImpl.isUidAllowedOnDisplay(displayId, uid); } + + /** + * Can be called by MSIME to activate/deactivate a client when it is gaining/losing focus + * respectively. + * + * @param clientId client ID to activate/deactivate. + * @param active {@code true} to activate a client. + */ + public void setActive(int clientId, boolean active) { + mImpl.setActive(clientId, active); + } } diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java index bbe3a7fe1b31..04db8d625806 100644 --- a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java +++ b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java @@ -190,4 +190,8 @@ final class MultiClientInputMethodServiceDelegateImpl { boolean isUidAllowedOnDisplay(int displayId, int uid) { return mPrivOps.isUidAllowedOnDisplay(displayId, uid); } + + void setActive(int clientId, boolean active) { + mPrivOps.setActive(clientId, active); + } } diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java index b6c4fe2de4f4..68826cbeb845 100644 --- a/core/java/android/net/DnsResolver.java +++ b/core/java/android/net/DnsResolver.java @@ -197,7 +197,7 @@ public final class DnsResolver { final FileDescriptor queryfd; try { queryfd = resNetworkSend((network != null - ? network.netId : NETID_UNSET), query, query.length, flags); + ? network.getNetIdForResolv() : NETID_UNSET), query, query.length, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; @@ -238,7 +238,7 @@ public final class DnsResolver { final FileDescriptor queryfd; try { queryfd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags); + ? network.getNetIdForResolv() : NETID_UNSET), domain, nsClass, nsType, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; @@ -346,7 +346,8 @@ public final class DnsResolver { if (queryIpv6) { try { v6fd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags); + ? network.getNetIdForResolv() : NETID_UNSET), + domain, CLASS_IN, TYPE_AAAA, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; @@ -365,7 +366,8 @@ public final class DnsResolver { if (queryIpv4) { try { v4fd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags); + ? network.getNetIdForResolv() : NETID_UNSET), + domain, CLASS_IN, TYPE_A, flags); } catch (ErrnoException e) { if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid. executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); @@ -423,7 +425,7 @@ public final class DnsResolver { final FileDescriptor queryfd; try { queryfd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, CLASS_IN, nsType, flags); + ? network.getNetIdForResolv() : NETID_UNSET), domain, CLASS_IN, nsType, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java index b8d7cf167ca8..a55d9d00414f 100644 --- a/core/java/android/net/SntpClient.java +++ b/core/java/android/net/SntpClient.java @@ -20,6 +20,8 @@ import android.annotation.UnsupportedAppUsage; import android.os.SystemClock; import android.util.Log; +import com.android.internal.util.TrafficStatsConstants; + import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; @@ -99,7 +101,8 @@ public class SntpClient { public boolean requestTime(InetAddress address, int port, int timeout, Network network) { DatagramSocket socket = null; - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NTP); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_NTP); try { socket = new DatagramSocket(); network.bindSocket(socket); diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 49c6f74b1a34..4332d8abbce3 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -90,6 +90,42 @@ public class TrafficStats { public static final int UID_TETHERING = -5; /** + * Tag values in this range are reserved for the network stack. The network stack is + * running as UID {@link android.os.Process.NETWORK_STACK_UID} when in the mainline + * module separate process, and as the system UID otherwise. + */ + /** @hide */ + @SystemApi + public static final int TAG_NETWORK_STACK_RANGE_START = 0xFFFFFD00; + /** @hide */ + @SystemApi + public static final int TAG_NETWORK_STACK_RANGE_END = 0xFFFFFEFF; + + /** + * Tags between 0xFFFFFF00 and 0xFFFFFFFF are reserved and used internally by system services + * like DownloadManager when performing traffic on behalf of an application. + */ + // Please note there is no enforcement of these constants, so do not rely on them to + // determine that the caller is a system caller. + /** @hide */ + @SystemApi + public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = 0xFFFFFF00; + /** @hide */ + @SystemApi + public static final int TAG_SYSTEM_IMPERSONATION_RANGE_END = 0xFFFFFF0F; + + /** + * Tag values between these ranges are reserved for the network stack to do traffic + * on behalf of applications. It is a subrange of the range above. + */ + /** @hide */ + @SystemApi + public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = 0xFFFFFF80; + /** @hide */ + @SystemApi + public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = 0xFFFFFF8F; + + /** * Default tag value for {@link DownloadManager} traffic. * * @hide @@ -127,26 +163,9 @@ public class TrafficStats { */ public static final int TAG_SYSTEM_APP = 0xFFFFFF05; + // TODO : remove this constant when Wifi code is updated /** @hide */ - @SystemApi - @TestApi - public static final int TAG_SYSTEM_DHCP = 0xFFFFFF40; - /** @hide */ - public static final int TAG_SYSTEM_NTP = 0xFFFFFF41; - /** @hide */ - @SystemApi - @TestApi public static final int TAG_SYSTEM_PROBE = 0xFFFFFF42; - /** @hide */ - public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFF43; - /** @hide */ - public static final int TAG_SYSTEM_GPS = 0xFFFFFF44; - /** @hide */ - public static final int TAG_SYSTEM_PAC = 0xFFFFFF45; - /** @hide */ - @SystemApi - @TestApi - public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFF46; private static INetworkStatsService sStatsService; diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index ed5c1b1e2277..dde1e6a7f5f7 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -1148,15 +1148,9 @@ public class Environment { final Context context = AppGlobals.getInitialApplication(); final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); - final boolean hasLegacy = appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, + return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, context.getApplicationInfo().uid, - context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED; - - // STOPSHIP: only use app-op once permission model has fully landed - final boolean requestedLegacy = !AppGlobals.getInitialApplication().getApplicationInfo() - .isExternalStorageSandboxAllowed(); - - return !(hasLegacy || requestedLegacy); + context.getOpPackageName()) != AppOpsManager.MODE_ALLOWED; } static File getDirectory(String variableName, String defaultPath) { diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 166de3fde741..6c498c736854 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -493,6 +493,7 @@ public final class DeviceConfig { * @param onPropertyChangedListener The listener to add. * @hide * @see #removeOnPropertyChangedListener(OnPropertyChangedListener) + * @removed */ @SystemApi @TestApi @@ -569,6 +570,7 @@ public final class DeviceConfig { * @param onPropertyChangedListener The listener to remove. * @hide * @see #addOnPropertyChangedListener(String, Executor, OnPropertyChangedListener) + * @removed */ @SystemApi @TestApi @@ -737,6 +739,7 @@ public final class DeviceConfig { * Override {@link #onPropertyChanged(String, String, String)} to handle callbacks for changes. * * @hide + * @removed */ @SystemApi @TestApi diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0491c732db81..488f41719454 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -955,18 +955,16 @@ public final class Settings { "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; /** - * Activity Action: Open the advanced power usage details page of an associated app. + * Activity Action: Open the battery details page of an associated app. * <p> * Input: Intent's data URI set with an application name, using the * "package" schema (like "package:com.my.app") * <p> * Output: Nothing. - * - * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_VIEW_ADVANCED_POWER_USAGE_DETAIL = - "android.settings.VIEW_ADVANCED_POWER_USAGE_DETAIL"; + public static final String ACTION_APP_BATTERY_SETTINGS = + "android.settings.APP_BATTERY_SETTINGS"; /** * Activity Action: Show screen for controlling background data @@ -1533,6 +1531,9 @@ public final class Settings { /** * Activity Action: Show More default apps settings. * <p> + * If a Settings activity handles this intent action, a "More defaults" entry will be shown in + * the Default apps settings, and clicking it will launch that activity. + * <p> * In some cases, a matching Activity may not exist, so ensure you safeguard against this. * <p> * Input: Nothing. @@ -5801,6 +5802,14 @@ public final class Settings { "autofill_field_classification"; /** + * Boolean indicating if the dark mode dialog shown on first toggle has been seen. + * + * @hide + */ + public static final String DARK_MODE_DIALOG_SEEN = + "dark_mode_dialog_seen"; + + /** * Defines value returned by {@link android.service.autofill.UserData#getMaxUserDataSize()}. * * @hide @@ -8139,7 +8148,14 @@ public final class Settings { public static final String FACE_UNLOCK_ATTENTION_REQUIRED = "face_unlock_attention_required"; - private static final Validator FACE_UNLOCK_ATTENTION_REQUIRED_VALIDATOR = BOOLEAN_VALIDATOR; + /** + * Whether or not face unlock requires a diverse set of poses during enrollment. This is a + * cached value, the source of truth is obtained through the HAL. + * @hide + */ + public static final String FACE_UNLOCK_DIVERSITY_REQUIRED = + "face_unlock_diversity_required"; + /** * Whether or not face unlock is allowed for apps (through BiometricPrompt). @@ -8797,7 +8813,6 @@ public final class Settings { AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, FACE_UNLOCK_KEYGUARD_ENABLED, FACE_UNLOCK_DISMISSES_KEYGUARD, - FACE_UNLOCK_ATTENTION_REQUIRED, FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, ASSIST_GESTURE_ENABLED, @@ -8842,6 +8857,7 @@ public final class Settings { SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_CALL_GESTURE_COUNT, SILENCE_TIMER_GESTURE_COUNT, + DARK_MODE_DIALOG_SEEN }; /** @@ -8964,8 +8980,6 @@ public final class Settings { VALIDATORS.put(FACE_UNLOCK_KEYGUARD_ENABLED, FACE_UNLOCK_KEYGUARD_ENABLED_VALIDATOR); VALIDATORS.put(FACE_UNLOCK_DISMISSES_KEYGUARD, FACE_UNLOCK_DISMISSES_KEYGUARD_VALIDATOR); - VALIDATORS.put(FACE_UNLOCK_ATTENTION_REQUIRED, - FACE_UNLOCK_ATTENTION_REQUIRED_VALIDATOR); VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR); VALIDATORS.put(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR); @@ -9024,6 +9038,7 @@ public final class Settings { VALIDATORS.put(SILENCE_CALL_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(ODI_CAPTIONS_ENABLED, ODI_CAPTIONS_ENABLED_VALIDATOR); + VALIDATORS.put(DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR); } /** diff --git a/core/java/android/service/attention/AttentionService.java b/core/java/android/service/attention/AttentionService.java index 6172ce501590..49ab5db74b87 100644 --- a/core/java/android/service/attention/AttentionService.java +++ b/core/java/android/service/attention/AttentionService.java @@ -21,13 +21,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.Service; -import android.attention.AttentionManagerInternal; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import com.android.internal.util.Preconditions; -import com.android.server.LocalServices; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -132,19 +130,6 @@ public abstract class AttentionService extends Service { } /** - * Disables the dependants. - * - * Example: called if the service does not have sufficient permissions to perform the task. - */ - public final void disableSelf() { - AttentionManagerInternal attentionManager = LocalServices.getService( - AttentionManagerInternal.class); - if (attentionManager != null) { - attentionManager.disableSelf(); - } - } - - /** * Checks the user attention and calls into the provided callback. * * @param callback the callback to return the result to diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index 5be73b92fbc0..02ce87324a4f 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -46,9 +46,9 @@ import android.view.contentcapture.ContentCaptureEvent; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureSession; import android.view.contentcapture.ContentCaptureSessionId; +import android.view.contentcapture.DataRemovalRequest; import android.view.contentcapture.IContentCaptureDirectManager; import android.view.contentcapture.MainContentCaptureSession; -import android.view.contentcapture.UserDataRemovalRequest; import com.android.internal.os.IResultReceiver; @@ -86,11 +86,28 @@ public abstract class ContentCaptureService extends Service { * <code><{@link * android.R.styleable#ContentCaptureService content-capture-service}></code> tag. * - * <p>This is a a sample XML file configuring a ContentCaptureService: - * <pre> <content-capture-service - * android:settingsActivity="foo.bar.SettingsActivity" - * . . . - * /></pre> + * <p>Here's an example of how to use it on {@code AndroidManifest.xml}: + * + * <pre> + * <service android:name=".MyContentCaptureService" + * android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE"> + * <intent-filter> + * <action android:name="android.service.contentcapture.ContentCaptureService" /> + * </intent-filter> + * + * <meta-data + * android:name="android.content_capture" + * android:resource="@xml/my_content_capture_service"/> + * </service> + * </pre> + * + * <p>And then on {@code res/xml/my_content_capture_service.xml}: + * + * <pre> + * <content-capture-service xmlns:android="http://schemas.android.com/apk/res/android" + * android:settingsActivity="my.package.MySettingsActivity"> + * </content-capture-service> + * </pre> */ public static final String SERVICE_META_DATA = "android.content_capture"; @@ -138,7 +155,7 @@ public abstract class ContentCaptureService extends Service { } @Override - public void onUserDataRemovalRequest(UserDataRemovalRequest request) { + public void onDataRemovalRequest(DataRemovalRequest request) { mHandler.sendMessage( obtainMessage(ContentCaptureService::handleOnUserDataRemovalRequest, ContentCaptureService.this, request)); @@ -288,12 +305,12 @@ public abstract class ContentCaptureService extends Service { } /** - * Notifies the service that the app requested to remove data associated with the user. + * Notifies the service that the app requested to remove content capture data. * - * @param request the user data requested to be removed + * @param request the content capture data requested to be removed */ - public void onUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) { - if (sVerbose) Log.v(TAG, "onUserDataRemovalRequest()"); + public void onDataRemovalRequest(@NonNull DataRemovalRequest request) { + if (sVerbose) Log.v(TAG, "onDataRemovalRequest()"); } /** @@ -449,8 +466,8 @@ public abstract class ContentCaptureService extends Service { onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId)); } - private void handleOnUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) { - onUserDataRemovalRequest(request); + private void handleOnUserDataRemovalRequest(@NonNull DataRemovalRequest request) { + onDataRemovalRequest(request); } private void handleOnActivityEvent(@NonNull ActivityEvent event) { diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl index 03e1b7857837..a7578af94004 100644 --- a/core/java/android/service/contentcapture/IContentCaptureService.aidl +++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl @@ -21,7 +21,7 @@ import android.os.IBinder; import android.service.contentcapture.ActivityEvent; import android.service.contentcapture.SnapshotData; import android.view.contentcapture.ContentCaptureContext; -import android.view.contentcapture.UserDataRemovalRequest; +import android.view.contentcapture.DataRemovalRequest; import com.android.internal.os.IResultReceiver; @@ -39,6 +39,6 @@ oneway interface IContentCaptureService { in IResultReceiver clientReceiver, int initialState); void onSessionFinished(int sessionId); void onActivitySnapshot(int sessionId, in SnapshotData snapshotData); - void onUserDataRemovalRequest(in UserDataRemovalRequest request); + void onDataRemovalRequest(in DataRemovalRequest request); void onActivityEvent(in ActivityEvent event); } diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java index 2288106d8351..d2f22bfac84a 100644 --- a/core/java/android/service/euicc/EuiccService.java +++ b/core/java/android/service/euicc/EuiccService.java @@ -418,12 +418,15 @@ public abstract class EuiccService extends Service { * bit map, and original the card Id. The result code may be one of the predefined * {@code RESULT_} constants or any implementation-specific code starting with * {@link #RESULT_FIRST_USER}. The resolvable error bit map can be either 0 or values - * defined in {@code RESOLVABLE_ERROR_}. + * defined in {@code RESOLVABLE_ERROR_}. A subclass should override this method. Otherwise, + * this method does nothing and returns null by default. * @see android.telephony.euicc.EuiccManager#downloadSubscription */ - public abstract DownloadSubscriptionResult onDownloadSubscription(int slotId, + public DownloadSubscriptionResult onDownloadSubscription(int slotId, @NonNull DownloadableSubscription subscription, boolean switchAfterDownload, - boolean forceDeactivateSim, @Nullable Bundle resolvedBundle); + boolean forceDeactivateSim, @Nullable Bundle resolvedBundle) { + return null; + } /** * Download the given subscription. @@ -439,14 +442,14 @@ public abstract class EuiccService extends Service { * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. * @see android.telephony.euicc.EuiccManager#downloadSubscription * - * @deprecated From Q, please use the above - * {@link #onDownloadSubscription(int, DownloadableSubscription, boolean, boolean, Bundle)}. + * @deprecated From Q, a subclass should use and override the above + * {@link #onDownloadSubscription(int, DownloadableSubscription, boolean, boolean, Bundle)}. The + * default return value for this one is Integer.MIN_VALUE. */ @Deprecated public @Result int onDownloadSubscription(int slotId, @NonNull DownloadableSubscription subscription, boolean switchAfterDownload, boolean forceDeactivateSim) { - throw new UnsupportedOperationException("onDownloadSubscription(int, " - + "DownloadableSubscription, boolean, boolean) is deprecated."); + return Integer.MIN_VALUE; } /** diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index e3e63e539591..0de17ca3b960 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -17,7 +17,6 @@ package android.service.voice; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.UnsupportedAppUsage; import android.app.Service; @@ -41,6 +40,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Set; @@ -211,11 +211,11 @@ public class VoiceInteractionService extends Service { * * @param voiceActions A set of checked voice actions. * @return Returns a subset of checked voice actions. Additional voice actions in the - * returned set will be ignored. Returns null or empty set if no actions are supported. + * returned set will be ignored. Returns empty set if no actions are supported. */ - @Nullable + @NonNull public Set<String> onGetSupportedVoiceActions(@NonNull Set<String> voiceActions) { - return null; + return Collections.emptySet(); } @Override @@ -272,7 +272,7 @@ public class VoiceInteractionService extends Service { try { Set<String> voiceActionsSet = new ArraySet<>(voiceActions); Set<String> resultSet = onGetSupportedVoiceActions(voiceActionsSet); - callback.onComplete(resultSet == null ? null : new ArrayList<>(resultSet)); + callback.onComplete(new ArrayList<>(resultSet)); } catch (RemoteException e) { } } diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 6f274477431f..5b5f3b843c50 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -123,7 +123,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall /** * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked - * from an Android automotive system Ui. + * from an Android automotive system UI. */ public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 1 << 7; diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index f2747cf426fa..1bcfc05224ca 100755 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -34,11 +34,32 @@ public class DisplayMetrics { public static final int DENSITY_LOW = 120; /** + * Intermediate density for screens that sit between {@link #DENSITY_LOW} (120dpi) and + * {@link #DENSITY_MEDIUM} (160dpi). This is not a density that applications should target, + * instead relying on the system to scale their {@link #DENSITY_MEDIUM} assets for them. + */ + public static final int DENSITY_140 = 140; + + /** * Standard quantized DPI for medium-density screens. */ public static final int DENSITY_MEDIUM = 160; /** + * Intermediate density for screens that sit between {@link #DENSITY_MEDIUM} (160dpi) and + * {@link #DENSITY_HIGH} (240dpi). This is not a density that applications should target, + * instead relying on the system to scale their {@link #DENSITY_HIGH} assets for them. + */ + public static final int DENSITY_180 = 180; + + /** + * Intermediate density for screens that sit between {@link #DENSITY_MEDIUM} (160dpi) and + * {@link #DENSITY_HIGH} (240dpi). This is not a density that applications should target, + * instead relying on the system to scale their {@link #DENSITY_HIGH} assets for them. + */ + public static final int DENSITY_200 = 200; + + /** * This is a secondary density, added for some common screen configurations. * It is recommended that applications not generally target this as a first * class density -- that is, don't supply specific graphics for this @@ -58,6 +79,13 @@ public class DisplayMetrics { public static final int DENSITY_TV = 213; /** + * Intermediate density for screens that sit between {@link #DENSITY_MEDIUM} (160dpi) and + * {@link #DENSITY_HIGH} (240dpi). This is not a density that applications should target, + * instead relying on the system to scale their {@link #DENSITY_HIGH} assets for them. + */ + public static final int DENSITY_220 = 220; + + /** * Standard quantized DPI for high-density screens. */ public static final int DENSITY_HIGH = 240; diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index feff9db1a63e..f2aaeaddef97 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -63,7 +63,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(FORCE_GLOBAL_ACTIONS_GRID_ENABLED, "false"); DEFAULT_FLAGS.put(GLOBAL_ACTIONS_PANEL_ENABLED, "true"); DEFAULT_FLAGS.put(PIXEL_WALLPAPER_CATEGORY_SWITCH, "false"); - DEFAULT_FLAGS.put("settings_wifi_details_saved_screen", "true"); DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false"); } diff --git a/core/java/android/view/GestureExclusionTracker.java b/core/java/android/view/GestureExclusionTracker.java index 8eccc04fa647..6fcdd714f827 100644 --- a/core/java/android/view/GestureExclusionTracker.java +++ b/core/java/android/view/GestureExclusionTracker.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; +import com.android.internal.util.Preconditions; + import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; @@ -31,6 +33,8 @@ import java.util.List; */ class GestureExclusionTracker { private boolean mGestureExclusionViewsChanged = false; + private boolean mRootGestureExclusionRectsChanged = false; + private List<Rect> mRootGestureExclusionRects = Collections.emptyList(); private List<GestureExclusionViewInfo> mGestureExclusionViewInfos = new ArrayList<>(); private List<Rect> mGestureExclusionRects = Collections.emptyList(); @@ -59,9 +63,9 @@ class GestureExclusionTracker { @Nullable public List<Rect> computeChangedRects() { - boolean changed = false; + boolean changed = mRootGestureExclusionRectsChanged; final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator(); - final List<Rect> rects = new ArrayList<>(); + final List<Rect> rects = new ArrayList<>(mRootGestureExclusionRects); while (i.hasNext()) { final GestureExclusionViewInfo info = i.next(); switch (info.update()) { @@ -79,6 +83,7 @@ class GestureExclusionTracker { } if (changed || mGestureExclusionViewsChanged) { mGestureExclusionViewsChanged = false; + mRootGestureExclusionRectsChanged = false; if (!mGestureExclusionRects.equals(rects)) { mGestureExclusionRects = rects; return rects; @@ -87,6 +92,17 @@ class GestureExclusionTracker { return null; } + public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) { + Preconditions.checkNotNull(rects, "rects must not be null"); + mRootGestureExclusionRects = rects; + mRootGestureExclusionRectsChanged = true; + } + + @NonNull + public List<Rect> getRootSystemGestureExclusionRects() { + return mRootGestureExclusionRects; + } + private static class GestureExclusionViewInfo { public static final int CHANGED = 0; public static final int UNCHANGED = 1; diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index 2ba1e016e03d..d415387808dd 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -85,6 +85,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { @Override public void onWindowFocusLost() { mHasWindowFocus = false; + getImm().unregisterImeConsumer(this); } /** diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index 7c69cfde2b9e..1d721516a979 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -551,7 +551,7 @@ public class ScaleGestureDetector { (mEventBeforeOrAboveStartingGestureEvent && (mCurrSpan < mPrevSpan)) || (!mEventBeforeOrAboveStartingGestureEvent && (mCurrSpan > mPrevSpan)); final float spanDiff = (Math.abs(1 - (mCurrSpan / mPrevSpan)) * SCALE_FACTOR); - return mPrevSpan <= 0 ? 1 : scaleUp ? (1 + spanDiff) : (1 - spanDiff); + return mPrevSpan <= mSpanSlop ? 1 : scaleUp ? (1 + spanDiff) : (1 - spanDiff); } return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 51bcbbdb7e7d..096c988c8cf1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1377,59 +1377,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public static final int AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x1; - /** @hide */ - @IntDef(prefix = { "IMPORTANT_FOR_CONTENT_CAPTURE_" }, value = { - IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, - IMPORTANT_FOR_CONTENT_CAPTURE_YES, - IMPORTANT_FOR_CONTENT_CAPTURE_NO, - IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, - IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ContentCaptureImportance {} - - /** - * Automatically determine whether a view is important for content capture. - * - * @see #isImportantForContentCapture() - * @see #setImportantForContentCapture(int) - */ - public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0x0; - - /** - * The view is important for content capture, and its children (if any) will be traversed. - * - * @see #isImportantForContentCapture() - * @see #setImportantForContentCapture(int) - */ - public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 0x1; - - /** - * The view is not important for content capture, but its children (if any) will be traversed. - * - * @see #isImportantForContentCapture() - * @see #setImportantForContentCapture(int) - */ - public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 0x2; - - /** - * The view is important for content capture, but its children (if any) will not be traversed. - * - * @see #isImportantForContentCapture() - * @see #setImportantForContentCapture(int) - */ - public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 0x4; - - /** - * The view is not important for content capture, and its children (if any) will not be - * traversed. - * - * @see #isImportantForContentCapture() - * @see #setImportantForContentCapture(int) - */ - public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 0x8; - - /** * This view is enabled. Interpretation varies by subclass. * Use with ENABLED_MASK when calling setFlags. @@ -3402,55 +3349,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /* End of masks for mPrivateFlags3 */ - /* - * Masks for mPrivateFlags4, as generated by dumpFlags(): - * - * |-------|-------|-------|-------| - * 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK - * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED - * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED - * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED - * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE - * 11 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK - * |-------|-------|-------|-------| - */ - - /** - * Mask for obtaining the bits which specify how to determine - * whether a view is important for autofill. - * - * <p>NOTE: the important for content capture values were the first flags added and are set in - * the rightmost position, so we don't need to shift them - */ - private static final int PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK = - IMPORTANT_FOR_CONTENT_CAPTURE_AUTO | IMPORTANT_FOR_CONTENT_CAPTURE_YES - | IMPORTANT_FOR_CONTENT_CAPTURE_NO - | IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS - | IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS; - - /* - * Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods - * should be called. - * - * The idea is to call notifyAppeared() after the view is layout and visible, then call - * notifyDisappeared() when it's gone (without known when it was removed from the parent). - */ - private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10; - private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20; - - /* - * Flags used to cache the value returned by isImportantForContentCapture while the view - * hierarchy is being traversed. - */ - private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED = 0x40; - private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE = 0x80; - - private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK = - PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED - | PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; - - /* End of masks for mPrivateFlags4 */ - /** @hide */ protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0; /** @hide */ @@ -4074,8 +3972,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 129147060) int mPrivateFlags3; - private int mPrivateFlags4; - /** * This view's request for the visibility of the status bar. * @hide @@ -5808,11 +5704,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setImportantForAutofill(a.getInt(attr, IMPORTANT_FOR_AUTOFILL_AUTO)); } break; - case R.styleable.View_importantForContentCapture: - if (a.peekValue(attr) != null) { - setImportantForContentCapture(a.getInt(attr, - IMPORTANT_FOR_CONTENT_CAPTURE_AUTO)); - } case R.styleable.View_defaultFocusHighlightEnabled: if (a.peekValue(attr) != null) { setDefaultFocusHighlightEnabled(a.getBoolean(attr, true)); @@ -8532,62 +8423,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, onProvideStructure(structure, VIEW_STRUCTURE_FOR_AUTOFILL, flags); } - /** - * Populates a {@link ViewStructure} for content capture. - * - * <p>This method is called after a view is that is eligible for content capture - * (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for - * the user, and the activity rendering the view is enabled for content capture) is laid out and - * is visible. - * - * <p>The populated structure is then passed to the service through - * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}. - * - * <p><b>Note: </b>views that manage a virtual structure under this view must populate just - * the node representing this view and return right away, then asynchronously report (not - * necessarily in the UI thread) when the children nodes appear, disappear or have their text - * changed by calling - * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}, - * {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and - * {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)} - * respectively. The structure for the a child must be created using - * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the - * {@code autofillId} for a child can be obtained either through - * {@code childStructure.getAutofillId()} or - * {@link ContentCaptureSession#newAutofillId(AutofillId, long)}. - * - * <p>When the virtual view hierarchy represents a web page, you should also: - * - * <ul> - * <li>Call {@link ContentCaptureManager#getContentCaptureConditions()} to infer content - * capture events should be generate for that URL. - * <li>Create a new {@link ContentCaptureSession} child for every HTML element that - * renders a new URL (like an {@code IFRAME}) and use that session to notify events from - * that subtree. - * </ul> - * - * <p><b>Note: </b>the following methods of the {@code structure} will be ignored: - * <ul> - * <li>{@link ViewStructure#setChildCount(int)} - * <li>{@link ViewStructure#addChildCount(int)} - * <li>{@link ViewStructure#getChildCount()} - * <li>{@link ViewStructure#newChild(int)} - * <li>{@link ViewStructure#asyncNewChild(int)} - * <li>{@link ViewStructure#asyncCommit()} - * <li>{@link ViewStructure#setWebDomain(String)} - * <li>{@link ViewStructure#newHtmlInfoBuilder(String)} - * <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)} - * <li>{@link ViewStructure#setDataIsSensitive(boolean)} - * <li>{@link ViewStructure#setAlpha(float)} - * <li>{@link ViewStructure#setElevation(float)} - * <li>{@link ViewStructure#setTransformation(Matrix)} - * - * </ul> - */ - public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) { - onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags); - } - /** @hide */ protected void onProvideStructure(@NonNull ViewStructure structure, @ViewStructureType int viewFor, int flags) { @@ -9225,265 +9060,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Gets the mode for determining whether this view is important for content capture. - * - * <p>See {@link #setImportantForContentCapture(int)} and - * {@link #isImportantForContentCapture()} for more info about this mode. - * - * @return {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO} by default, or value passed to - * {@link #setImportantForContentCapture(int)}. - * - * @attr ref android.R.styleable#View_importantForContentCapture - */ - @ViewDebug.ExportedProperty(mapping = { - @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to = "auto"), - @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES, to = "yes"), - @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO, to = "no"), - @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, - to = "yesExcludeDescendants"), - @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, - to = "noExcludeDescendants")}) - @InspectableProperty(enumMapping = { - @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, name = "auto"), - @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES, name = "yes"), - @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO, name = "no"), - @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, - name = "yesExcludeDescendants"), - @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, - name = "noExcludeDescendants"), - }) - public @ContentCaptureImportance int getImportantForContentCapture() { - // NOTE: the important for content capture values were the first flags added and are set in - // the rightmost position, so we don't need to shift them - return mPrivateFlags4 & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK; - } - - /** - * Sets the mode for determining whether this view is considered important for content capture. - * - * <p>The platform determines the importance for autofill automatically but you - * can use this method to customize the behavior. Typically, a view that provides text should - * be marked as {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}. - * - * @param mode {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}, - * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}, {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO}, - * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS}, - * or {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS}. - * - * @attr ref android.R.styleable#View_importantForContentCapture - */ - public void setImportantForContentCapture(@ContentCaptureImportance int mode) { - // Reset first - mPrivateFlags4 &= ~PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK; - // Then set again - // NOTE: the important for content capture values were the first flags added and are set in - // the rightmost position, so we don't need to shift them - mPrivateFlags4 |= (mode & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK); - } - - /** - * Hints the Android System whether this view is considered important for content capture, based - * on the value explicitly set by {@link #setImportantForContentCapture(int)} and heuristics - * when it's {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}. - * - * <p>See {@link ContentCaptureManager} for more info about content capture. - * - * @return whether the view is considered important for content capture. - * - * @see #setImportantForContentCapture(int) - * @see #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO - * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES - * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO - * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS - * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS - */ - public final boolean isImportantForContentCapture() { - boolean isImportant; - if ((mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED) != 0) { - isImportant = (mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE) != 0; - return isImportant; - } - - isImportant = calculateIsImportantForContentCapture(); - - mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; - if (isImportant) { - mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; - } - mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED; - return isImportant; - } - - /** - * Calculates whether the flag is important for content capture so it can be used by - * {@link #isImportantForContentCapture()} while the tree is traversed. - */ - private boolean calculateIsImportantForContentCapture() { - // Check parent mode to ensure we're important - ViewParent parent = mParent; - while (parent instanceof View) { - final int parentImportance = ((View) parent).getImportantForContentCapture(); - if (parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS - || parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS) { - if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) { - Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" + this + ") is not important for " - + "content capture because parent " + parent + "'s importance is " - + parentImportance); - } - return false; - } - parent = parent.getParent(); - } - - final int importance = getImportantForContentCapture(); - - // First, check the explicit states. - if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS - || importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES) { - return true; - } - if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS - || importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO) { - if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) { - Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" + this + ") is not important for content " - + "capture because its importance is " + importance); - } - return false; - } - - // Then use some heuristics to handle AUTO. - if (importance != IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) { - Log.w(CONTENT_CAPTURE_LOG_TAG, "invalid content capture importance (" + importance - + " on view " + this); - return false; - } - - // View group is important if at least one children also is - if (this instanceof ViewGroup) { - final ViewGroup group = (ViewGroup) this; - for (int i = 0; i < group.getChildCount(); i++) { - final View child = group.getChildAt(i); - if (child.isImportantForContentCapture()) { - return true; - } - } - } - - // If the app developer explicitly set hints or autofill hintsfor it, it's important. - if (getAutofillHints() != null) { - return true; - } - - // Otherwise, assume it's not important... - return false; - } - - /** - * Helper used to notify the {@link ContentCaptureManager} when the view is removed or - * added, based on whether it's laid out and visible, and without knowing if the parent removed - * it from the view hierarchy. - * - * <p>This method is called from many places (visibility changed, view laid out, view attached - * or detached to/from window, etc...) and hence must contain the logic to call the manager, as - * described below: - * - * <ol> - * <li>It should only be called when content capture is enabled for the view. - * <li>It must call viewAppeared() before viewDisappeared() - * <li>viewAppearead() can only be called when the view is visible and laidout - * <li>It should not call the same event twice. - * </ol> - */ - private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) { - AttachInfo ai = mAttachInfo; - // Skip it while the view is being laided out for the first time - if (ai != null && !ai.mReadyForContentCaptureUpdates) return; - - if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, - "notifyContentCapture(" + appeared + ") for " + getClass().getSimpleName()); - } - try { - notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(appeared); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } - } - - private void notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(boolean appeared) { - AttachInfo ai = mAttachInfo; - - // First check if context has client, so it saves a service lookup when it doesn't - if (mContext.getContentCaptureOptions() == null) return; - - // Then check if it's enabled in the context... - final ContentCaptureManager ccm = ai != null ? ai.getContentCaptureManager(mContext) - : mContext.getSystemService(ContentCaptureManager.class); - if (ccm == null || !ccm.isContentCaptureEnabled()) return; - - // ... and finally at the view level - // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled() - if (!isImportantForContentCapture()) return; - - ContentCaptureSession session = getContentCaptureSession(); - if (session == null) return; - - if (appeared) { - if (!isLaidOut() || getVisibility() != VISIBLE - || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) { - if (DEBUG_CONTENT_CAPTURE) { - Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid=" - + isLaidOut() + ", visibleToUser=" + isVisibleToUser() - + ", visible=" + (getVisibility() == VISIBLE) - + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) - + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); - } - return; - } - setNotifiedContentCaptureAppeared(); - - if (ai != null) { - ai.delayNotifyContentCaptureEvent(session, this, appeared); - } else { - if (DEBUG_CONTENT_CAPTURE) { - Log.w(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on appeared for " + this); - } - } - } else { - if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0 - || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) { - if (DEBUG_CONTENT_CAPTURE) { - Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid=" - + isLaidOut() + ", visibleToUser=" + isVisibleToUser() - + ", visible=" + (getVisibility() == VISIBLE) - + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) - + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); - } - return; - } - mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; - mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; - - if (ai != null) { - ai.delayNotifyContentCaptureEvent(session, this, appeared); - } else { - if (DEBUG_CONTENT_CAPTURE) { - Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this); - } - } - } - } - - private void setNotifiedContentCaptureAppeared() { - mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; - mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; - } - - /** * Sets the (optional) {@link ContentCaptureSession} associated with this view. * * <p>This method should be called when you need to associate a {@link ContentCaptureContext} to @@ -9739,68 +9315,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Dispatches the initial content capture events for a view structure. - * - * @hide - */ - public void dispatchInitialProvideContentCaptureStructure() { - AttachInfo ai = mAttachInfo; - if (ai == null) { - Log.w(CONTENT_CAPTURE_LOG_TAG, - "dispatchProvideContentCaptureStructure(): no AttachInfo for " + this); - return; - } - ContentCaptureManager ccm = ai.mContentCaptureManager; - if (ccm == null) { - Log.w(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): " - + "no ContentCaptureManager for " + this); - return; - } - - // We must set it before checkign if the view itself is important, because it might - // initially not be (for example, if it's empty), although that might change later (for - // example, if important views are added) - ai.mReadyForContentCaptureUpdates = true; - - if (!isImportantForContentCapture()) { - if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) { - Log.d(CONTENT_CAPTURE_LOG_TAG, - "dispatchProvideContentCaptureStructure(): decorView is not important"); - } - return; - } - - ai.mContentCaptureManager = ccm; - - ContentCaptureSession session = getContentCaptureSession(); - if (session == null) { - if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) { - Log.d(CONTENT_CAPTURE_LOG_TAG, - "dispatchProvideContentCaptureStructure(): no session for " + this); - } - return; - } - - session.internalNotifyViewTreeEvent(/* started= */ true); - try { - dispatchProvideContentCaptureStructure(); - } finally { - session.internalNotifyViewTreeEvent(/* started= */ false); - } - } - - /** @hide */ - void dispatchProvideContentCaptureStructure() { - ContentCaptureSession session = getContentCaptureSession(); - if (session != null) { - ViewStructure structure = session.newViewStructure(this); - onProvideContentCaptureStructure(structure, /* flags= */ 0); - setNotifiedContentCaptureAppeared(); - session.notifyViewAppeared(structure); - } - } - - /** * @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) * * Note: Called from the default {@link AccessibilityDelegate}. @@ -12134,14 +11648,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * When screen readers (one type of accessibility tool) decide what should be read to the - * user, they typically look for input focusable ({@link #isFocusable()}) parents of - * non-focusable text items, and read those focusable parents and their non-focusable children - * as a unit. In some situations, this behavior is desirable for views that should not take - * input focus. Setting an item to be screen reader focusable requests that the view be - * treated as a unit by screen readers without any effect on input focusability. The default - * value of {@code false} lets screen readers use other signals, like focusable, to determine - * how to group items. + * Sets whether this View should be a focusable element for screen readers + * and include non-focusable Views from its subtree when providing feedback. + * <p> + * Note: this is similar to using <a href="#attr_android:focusable">{@code android:focusable}, + * but does not impact input focus behavior. * * @param screenReaderFocusable Whether the view should be treated as a unit by screen reader * accessibility tools. @@ -13753,7 +13264,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void dispatchStartTemporaryDetach() { mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH; notifyEnterOrExitForAutoFillIfNeeded(false); - notifyAppearedOrDisappearedForContentCaptureIfNeeded(false); onStartTemporaryDetach(); } @@ -13780,7 +13290,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, notifyFocusChangeToInputMethodManager(true /* hasFocus */); } notifyEnterOrExitForAutoFillIfNeeded(true); - notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); } /** @@ -14372,8 +13881,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED); } } - - notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible); } /** @@ -18069,7 +17576,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } // Reset content capture caches - mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK; mCachedContentCaptureSession = null; if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) @@ -20079,7 +19585,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, needGlobalAttributesUpdate(false); notifyEnterOrExitForAutoFillIfNeeded(true); - notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) @@ -20129,7 +19634,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } notifyEnterOrExitForAutoFillIfNeeded(false); - notifyAppearedOrDisappearedForContentCaptureIfNeeded(false); } /** @@ -22442,8 +21946,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT; notifyEnterOrExitForAutoFillIfNeeded(true); } - - notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); } private boolean hasParentWantsFocus() { @@ -28650,23 +28152,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, View mTooltipHost; /** - * The initial structure has been reported so the view is ready to report updates. - */ - boolean mReadyForContentCaptureUpdates; - - /** - * Map(keyed by session) of content capture events that need to be notified after the view - * hierarchy is traversed: value is either the view itself for appearead events, or its - * autofill id for disappeared. - */ - SparseArray<ArrayList<Object>> mContentCaptureEvents; - - /** - * Cached reference to the {@link ContentCaptureManager}. - */ - ContentCaptureManager mContentCaptureManager; - - /** * Creates a new set of attachment information with the specified * events handler and thread. * @@ -28684,31 +28169,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mRootCallbacks = effectPlayer; mTreeObserver = new ViewTreeObserver(context); } - - private void delayNotifyContentCaptureEvent(@NonNull ContentCaptureSession session, - @NonNull View view, boolean appeared) { - if (mContentCaptureEvents == null) { - // Most of the time there will be just one session, so intial capacity is 1 - mContentCaptureEvents = new SparseArray<>(1); - } - int sessionId = session.getId(); - // TODO: life would be much easier if we provided a MultiMap implementation somwhere... - ArrayList<Object> events = mContentCaptureEvents.get(sessionId); - if (events == null) { - events = new ArrayList<>(); - mContentCaptureEvents.put(sessionId, events); - } - events.add(appeared ? view : view.getAutofillId()); - } - - @Nullable - ContentCaptureManager getContentCaptureManager(@NonNull Context context) { - if (mContentCaptureManager != null) { - return mContentCaptureManager; - } - mContentCaptureManager = context.getSystemService(ContentCaptureManager.class); - return mContentCaptureManager; - } } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 937bd1b34e61..d362024ed525 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3606,7 +3606,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return; } - final ChildListForAutoFillOrContentCapture children = getChildrenForAutofill(flags); + final ChildListForAutofill children = getChildrenForAutofill(flags); final int childrenCount = children.size(); structure.setChildCount(childrenCount); for (int i = 0; i < childrenCount; i++) { @@ -3617,30 +3617,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager children.recycle(); } - /** @hide */ - @Override - public void dispatchProvideContentCaptureStructure() { - super.dispatchProvideContentCaptureStructure(); - - if (!isLaidOut()) return; - - final ChildListForAutoFillOrContentCapture children = getChildrenForContentCapture(); - final int childrenCount = children.size(); - for (int i = 0; i < childrenCount; i++) { - final View child = children.get(i); - child.dispatchProvideContentCaptureStructure(); - } - children.recycle(); - } - /** * Gets the children for autofill. Children for autofill are the first * level descendants that are important for autofill. The returned * child list object is pooled and the caller must recycle it once done. * @hide */ - private @NonNull ChildListForAutoFillOrContentCapture getChildrenForAutofill( + private @NonNull ChildListForAutofill getChildrenForAutofill( @AutofillFlags int flags) { - final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture + final ChildListForAutofill children = ChildListForAutofill .obtain(); populateChildrenForAutofill(children, flags); return children; @@ -3668,34 +3652,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - private @NonNull ChildListForAutoFillOrContentCapture getChildrenForContentCapture() { - final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture - .obtain(); - populateChildrenForContentCapture(children); - return children; - } - - /** @hide */ - private void populateChildrenForContentCapture(ArrayList<View> list) { - final int childrenCount = mChildrenCount; - if (childrenCount <= 0) { - return; - } - final ArrayList<View> preorderedList = buildOrderedChildList(); - final boolean customOrder = preorderedList == null - && isChildrenDrawingOrderEnabled(); - for (int i = 0; i < childrenCount; i++) { - final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); - final View child = (preorderedList == null) - ? mChildren[childIndex] : preorderedList.get(childIndex); - if (child.isImportantForContentCapture()) { - list.add(child); - } else if (child instanceof ViewGroup) { - ((ViewGroup) child).populateChildrenForContentCapture(list); - } - } - } - private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children, int childIndex) { final View child; @@ -8678,16 +8634,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Pooled class that to hold the children for autifill. */ - private static class ChildListForAutoFillOrContentCapture extends ArrayList<View> { + private static class ChildListForAutofill extends ArrayList<View> { private static final int MAX_POOL_SIZE = 32; - private static final Pools.SimplePool<ChildListForAutoFillOrContentCapture> sPool = + private static final Pools.SimplePool<ChildListForAutofill> sPool = new Pools.SimplePool<>(MAX_POOL_SIZE); - public static ChildListForAutoFillOrContentCapture obtain() { - ChildListForAutoFillOrContentCapture list = sPool.acquire(); + public static ChildListForAutofill obtain() { + ChildListForAutofill list = sPool.acquire(); if (list == null) { - list = new ChildListForAutoFillOrContentCapture(); + list = new ChildListForAutofill(); } return list; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f3b7ad5e557c..7ad118e760d8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -105,11 +105,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; -import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; -import android.view.contentcapture.ContentCaptureManager; -import android.view.contentcapture.ContentCaptureSession; -import android.view.contentcapture.MainContentCaptureSession; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; @@ -224,21 +220,6 @@ public final class ViewRootImpl implements ViewParent, */ static final int MAX_TRACKBALL_DELAY = 250; - /** - * Initial value for {@link #mContentCaptureEnabled}. - */ - private static final int CONTENT_CAPTURE_ENABLED_NOT_CHECKED = 0; - - /** - * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code true}. - */ - private static final int CONTENT_CAPTURE_ENABLED_TRUE = 1; - - /** - * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code false}. - */ - private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2; - @UnsupportedAppUsage static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); @@ -435,10 +416,6 @@ public final class ViewRootImpl implements ViewParent, boolean mLayoutRequested; boolean mFirst; - @Nullable - int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED; - boolean mPerformContentCapture; - boolean mReportNextDraw; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; @@ -637,7 +614,6 @@ public final class ViewRootImpl implements ViewParent, mTransparentRegion = new Region(); mPreviousTransparentRegion = new Region(); mFirst = true; // true for the first time the view is added - mPerformContentCapture = true; // also true for the first time the view is added mAdded = false; mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context); @@ -2787,55 +2763,9 @@ public final class ViewRootImpl implements ViewParent, } } - if (mAttachInfo.mContentCaptureEvents != null) { - notifyContentCatpureEvents(); - } - mIsInTraversal = false; } - private void notifyContentCatpureEvents() { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents"); - try { - MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager - .getMainContentCaptureSession(); - for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) { - int sessionId = mAttachInfo.mContentCaptureEvents.keyAt(i); - mainSession.notifyViewTreeEvent(sessionId, /* started= */ true); - ArrayList<Object> events = mAttachInfo.mContentCaptureEvents - .valueAt(i); - for_each_event: for (int j = 0; j < events.size(); j++) { - Object event = events.get(j); - if (event instanceof AutofillId) { - mainSession.notifyViewDisappeared(sessionId, (AutofillId) event); - } else if (event instanceof View) { - View view = (View) event; - ContentCaptureSession session = view.getContentCaptureSession(); - if (session == null) { - Log.w(mTag, "no content capture session on view: " + view); - continue for_each_event; - } - int actualId = session.getId(); - if (actualId != sessionId) { - Log.w(mTag, "content capture session mismatch for view (" + view - + "): was " + sessionId + " before, it's " + actualId + " now"); - continue for_each_event; - } - ViewStructure structure = session.newViewStructure(view); - view.onProvideContentCaptureStructure(structure, /* flags= */ 0); - session.notifyViewAppeared(structure); - } else { - Log.w(mTag, "invalid content capture event: " + event); - } - } - mainSession.notifyViewTreeEvent(sessionId, /* started= */ false); - } - mAttachInfo.mContentCaptureEvents = null; - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } - } - private void notifySurfaceDestroyed() { mSurfaceHolder.ungetCallbacks(); SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); @@ -2966,13 +2896,6 @@ public final class ViewRootImpl implements ViewParent, } } mFirstInputStage.onWindowFocusChanged(hasWindowFocus); - - // NOTE: there's no view visibility (appeared / disapparead) events when the windows focus - // is lost, so we don't need to to force a flush - there might be other events such as - // text changes, but these should be flushed independently. - if (hasWindowFocus) { - handleContentCaptureFlush(); - } } private void fireAccessibilityFocusEventIfHasFocusedNode() { @@ -3539,86 +3462,6 @@ public final class ViewRootImpl implements ViewParent, pendingDrawFinished(); } } - if (mPerformContentCapture) { - performContentCaptureInitialReport(); - } - } - - /** - * Checks (and caches) if content capture is enabled for this context. - */ - private boolean isContentCaptureEnabled() { - switch (mContentCaptureEnabled) { - case CONTENT_CAPTURE_ENABLED_TRUE: - return true; - case CONTENT_CAPTURE_ENABLED_FALSE: - return false; - case CONTENT_CAPTURE_ENABLED_NOT_CHECKED: - final boolean reallyEnabled = isContentCaptureReallyEnabled(); - mContentCaptureEnabled = reallyEnabled ? CONTENT_CAPTURE_ENABLED_TRUE - : CONTENT_CAPTURE_ENABLED_FALSE; - return reallyEnabled; - default: - Log.w(TAG, "isContentCaptureEnabled(): invalid state " + mContentCaptureEnabled); - return false; - } - - } - - /** - * Checks (without caching) if content capture is enabled for this context. - */ - private boolean isContentCaptureReallyEnabled() { - // First check if context supports it, so it saves a service lookup when it doesn't - if (mContext.getContentCaptureOptions() == null) return false; - - final ContentCaptureManager ccm = mAttachInfo.getContentCaptureManager(mContext); - // Then check if it's enabled in the contex itself. - if (ccm == null || !ccm.isContentCaptureEnabled()) return false; - - return true; - } - - private void performContentCaptureInitialReport() { - mPerformContentCapture = false; // One-time offer! - final View rootView = mView; - if (DEBUG_CONTENT_CAPTURE) { - Log.v(mTag, "performContentCaptureInitialReport() on " + rootView); - } - if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for " - + getClass().getSimpleName()); - } - try { - if (!isContentCaptureEnabled()) return; - - // Content capture is a go! - rootView.dispatchInitialProvideContentCaptureStructure(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } - } - - private void handleContentCaptureFlush() { - if (DEBUG_CONTENT_CAPTURE) { - Log.v(mTag, "handleContentCaptureFlush()"); - } - if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for " - + getClass().getSimpleName()); - } - try { - if (!isContentCaptureEnabled()) return; - - final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager; - if (ccm == null) { - Log.w(TAG, "No ContentCapture on AttachInfo"); - return; - } - ccm.flush(ContentCaptureSession.FLUSH_REASON_VIEW_ROOT_ENTERED); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } } private boolean draw(boolean fullRedrawNeeded) { @@ -3986,6 +3829,24 @@ public final class ViewRootImpl implements ViewParent, } /** + * Set the root-level system gesture exclusion rects. These are added to those provided by + * the root's view hierarchy. + */ + public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) { + mGestureExclusionTracker.setRootSystemGestureExclusionRects(rects); + mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); + } + + /** + * Returns the root-level system gesture exclusion rects. These do not include those provided by + * the root's view hierarchy. + */ + @NonNull + public List<Rect> getRootSystemGestureExclusionRects() { + return mGestureExclusionTracker.getRootSystemGestureExclusionRects(); + } + + /** * Requests that the root render node is invalidated next time we perform a draw, such that * {@link WindowCallbacks#onPostDraw} gets called. */ diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index fc9d8c269538..b0ec6210d828 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -48,6 +48,7 @@ import android.transition.Transition; import android.transition.TransitionManager; import android.view.accessibility.AccessibilityEvent; +import java.util.Collections; import java.util.List; /** @@ -2397,6 +2398,53 @@ public abstract class Window { return false; } + /** + * Sets a list of areas within this window's coordinate space where the system should not + * intercept touch or other pointing device gestures. + * + * <p>This method should be used by apps that make use of + * {@link #takeSurface(SurfaceHolder.Callback2)} and do not have a view hierarchy available. + * Apps that do have a view hierarchy should use + * {@link View#setSystemGestureExclusionRects(List)} instead. This method does not modify or + * replace the gesture exclusion rects populated by individual views in this window's view + * hierarchy using {@link View#setSystemGestureExclusionRects(List)}.</p> + * + * <p>Use this to tell the system which specific sub-areas of a view need to receive gesture + * input in order to function correctly in the presence of global system gestures that may + * conflict. For example, if the system wishes to capture swipe-in-from-screen-edge gestures + * to provide system-level navigation functionality, a view such as a navigation drawer + * container can mark the left (or starting) edge of itself as requiring gesture capture + * priority using this API. The system may then choose to relax its own gesture recognition + * to allow the app to consume the user's gesture. It is not necessary for an app to register + * exclusion rects for broadly spanning regions such as the entirety of a + * <code>ScrollView</code> or for simple press and release click targets such as + * <code>Button</code>. Mark an exclusion rect when interacting with a view requires + * a precision touch gesture in a small area in either the X or Y dimension, such as + * an edge swipe or dragging a <code>SeekBar</code> thumb.</p> + * + * <p>Do not modify the provided list after this method is called.</p> + * + * @param rects A list of precision gesture regions that this window needs to function correctly + */ + @SuppressWarnings("unused") + public void setSystemGestureExclusionRects(@NonNull List<Rect> rects) { + throw new UnsupportedOperationException("window does not support gesture exclusion rects"); + } + + /** + * Retrieve the list of areas within this window's coordinate space where the system should not + * intercept touch or other pointing device gestures. This is the list as set by + * {@link #setSystemGestureExclusionRects(List)} or an empty list if + * {@link #setSystemGestureExclusionRects(List)} has not been called. It does not include + * exclusion rects set by this window's view hierarchy. + * + * @return a list of system gesture exclusion rects specific to this window + */ + @NonNull + public List<Rect> getSystemGestureExclusionRects() { + return Collections.emptyList(); + } + /** @hide */ public void setTheme(int resId) { } diff --git a/core/java/android/view/autofill/AutofillManagerInternal.java b/core/java/android/view/autofill/AutofillManagerInternal.java index 3de1a03d98e5..a07d46d77258 100644 --- a/core/java/android/view/autofill/AutofillManagerInternal.java +++ b/core/java/android/view/autofill/AutofillManagerInternal.java @@ -45,4 +45,12 @@ public abstract class AutofillManagerInternal { @Nullable public abstract AutofillOptions getAutofillOptions(@NonNull String packageName, long versionCode, @UserIdInt int userId); + + /** + * Checks whether the given {@code uid} owns the + * {@link android.service.autofill.augmented.AugmentedAutofillService} implementation associated + * with the given {@code userId}. + */ + public abstract boolean isAugmentedAutofillServiceForUser(@NonNull int callingUid, + @UserIdInt int userId); } diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java index 6f9d4d30909f..54ebf55a0063 100644 --- a/core/java/android/view/contentcapture/ContentCaptureCondition.java +++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java @@ -35,7 +35,8 @@ import java.lang.annotation.RetentionPolicy; public final class ContentCaptureCondition implements Parcelable { /** - * When set, package should use the {@link LocusId#getId()} as a regular expression. + * When set, package should use the {@link LocusId#getId()} as a regular expression (using the + * {@link java.util.regex.Pattern} format). */ public static final int FLAG_IS_REGEX = 0x2; diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 253935680cb8..26454c055932 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -191,8 +191,8 @@ import java.util.Set; * * <p>If your view provides its own virtual hierarchy (for example, if it's a browser that draws * the HTML using {@link Canvas} or native libraries in a different render process), then the view - * is also responsible to notify the session when the virtual elements appear and disappear - see - * {@link View#onProvideContentCaptureStructure(ViewStructure, int)} for more info. + * is also responsible to notify the session when the virtual elements appear and disappear - + * see {@link ContentCaptureSession#newViewStructure(View)} for more info. */ @SystemService(Context.CONTENT_CAPTURE_MANAGER_SERVICE) public final class ContentCaptureManager { @@ -343,15 +343,6 @@ public final class ContentCaptureManager { private MainContentCaptureSession mMainSession; /** @hide */ - public interface ContentCaptureClient { - /** - * Gets the component name of the client. - */ - @NonNull - ComponentName contentCaptureClientGetComponentName(); - } - - /** @hide */ public ContentCaptureManager(@NonNull Context context, @NonNull IContentCaptureManager service, @NonNull ContentCaptureOptions options) { mContext = Preconditions.checkNotNull(context, "context cannot be null"); @@ -578,16 +569,15 @@ public final class ContentCaptureManager { } /** - * Called by the app to request the content capture service to remove user-data associated with - * some context. + * Called by the app to remove content capture data associated with some context. * - * @param request object specifying what user data should be removed. + * @param request object specifying what data should be removed. */ - public void removeUserData(@NonNull UserDataRemovalRequest request) { + public void removeData(@NonNull DataRemovalRequest request) { Preconditions.checkNotNull(request); try { - mService.removeUserData(request); + mService.removeData(request); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 7761038f8af1..17a1fb405321 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -356,10 +356,6 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** * Notifies the Content Capture Service that a node has been added to the view structure. * - * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or - * automatically by the Android System for views that return {@code true} on - * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}. - * * @param node node that has been added. */ public final void notifyViewAppeared(@NonNull ViewStructure node) { @@ -378,9 +374,6 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** * Notifies the Content Capture Service that a node has been removed from the view structure. * - * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or - * automatically by the Android System for standard views. - * * @param id id of the node that has been removed. */ public final void notifyViewDisappeared(@NonNull AutofillId id) { @@ -441,7 +434,46 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** * Creates a {@link ViewStructure} for a "standard" view. * - * @hide + * <p>This method should be called after a visible view is laid out; the view then must populate + * the structure and pass it to {@link #notifyViewAppeared(ViewStructure)}. + * + * <b>Note: </b>views that manage a virtual structure under this view must populate just the + * node representing this view and return right away, then asynchronously report (not + * necessarily in the UI thread) when the children nodes appear, disappear or have their text + * changed by calling {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}, + * {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and + * {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)} respectively. + * The structure for the a child must be created using + * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the + * {@code autofillId} for a child can be obtained either through + * {@code childStructure.getAutofillId()} or + * {@link ContentCaptureSession#newAutofillId(AutofillId, long)}. + * + * <p>When the virtual view hierarchy represents a web page, you should also: + * + * <ul> + * <li>Call {@link ContentCaptureManager#getContentCaptureConditions()} to infer content capture + * events should be generate for that URL. + * <li>Create a new {@link ContentCaptureSession} child for every HTML element that renders a + * new URL (like an {@code IFRAME}) and use that session to notify events from that subtree. + * </ul> + * + * <p><b>Note: </b>the following methods of the {@code structure} will be ignored: + * <ul> + * <li>{@link ViewStructure#setChildCount(int)} + * <li>{@link ViewStructure#addChildCount(int)} + * <li>{@link ViewStructure#getChildCount()} + * <li>{@link ViewStructure#newChild(int)} + * <li>{@link ViewStructure#asyncNewChild(int)} + * <li>{@link ViewStructure#asyncCommit()} + * <li>{@link ViewStructure#setWebDomain(String)} + * <li>{@link ViewStructure#newHtmlInfoBuilder(String)} + * <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)} + * <li>{@link ViewStructure#setDataIsSensitive(boolean)} + * <li>{@link ViewStructure#setAlpha(float)} + * <li>{@link ViewStructure#setElevation(float)} + * <li>{@link ViewStructure#setTransformation(android.graphics.Matrix)} + * </ul> */ @NonNull public final ViewStructure newViewStructure(@NonNull View view) { diff --git a/core/java/android/view/contentcapture/UserDataRemovalRequest.aidl b/core/java/android/view/contentcapture/DataRemovalRequest.aidl index fbe47e08ea7c..c89d222d159f 100644 --- a/core/java/android/view/contentcapture/UserDataRemovalRequest.aidl +++ b/core/java/android/view/contentcapture/DataRemovalRequest.aidl @@ -16,4 +16,4 @@ package android.view.contentcapture; -parcelable UserDataRemovalRequest; +parcelable DataRemovalRequest; diff --git a/core/java/android/view/contentcapture/UserDataRemovalRequest.java b/core/java/android/view/contentcapture/DataRemovalRequest.java index 3e1e4abaa84c..3792846bea71 100644 --- a/core/java/android/view/contentcapture/UserDataRemovalRequest.java +++ b/core/java/android/view/contentcapture/DataRemovalRequest.java @@ -31,14 +31,12 @@ import java.util.ArrayList; import java.util.List; /** - * Class used by apps to request the Content Capture service to remove user-data associated with - * some context. + * Class used by apps to remove content capture data associated with {@link LocusId LocusIds}. */ -public final class UserDataRemovalRequest implements Parcelable { +public final class DataRemovalRequest implements Parcelable { /** - * When set, service should use the {@link LocusId#getId()} as prefix for the data to be - * removed. + * When set, the {@link LocusId#getId()} is the prefix for the data to be removed. */ public static final int FLAG_IS_PREFIX = 0x1; @@ -54,7 +52,7 @@ public final class UserDataRemovalRequest implements Parcelable { private final boolean mForEverything; private ArrayList<LocusIdRequest> mLocusIdRequests; - private UserDataRemovalRequest(@NonNull Builder builder) { + private DataRemovalRequest(@NonNull Builder builder) { mPackageName = ActivityThread.currentActivityThread().getApplication().getPackageName(); mForEverything = builder.mForEverything; if (builder.mLocusIds != null) { @@ -67,7 +65,7 @@ public final class UserDataRemovalRequest implements Parcelable { } } - private UserDataRemovalRequest(@NonNull Parcel parcel) { + private DataRemovalRequest(@NonNull Parcel parcel) { mPackageName = parcel.readString(); mForEverything = parcel.readBoolean(); if (!mForEverything) { @@ -89,7 +87,7 @@ public final class UserDataRemovalRequest implements Parcelable { } /** - * Checks if app is requesting to remove all user data associated with its package. + * Checks if app is requesting to remove content capture data associated with its package. */ public boolean isForEverything() { return mForEverything; @@ -104,7 +102,7 @@ public final class UserDataRemovalRequest implements Parcelable { } /** - * Builder for {@link UserDataRemovalRequest} objects. + * Builder for {@link DataRemovalRequest} objects. */ public static final class Builder { @@ -115,7 +113,7 @@ public final class UserDataRemovalRequest implements Parcelable { private boolean mDestroyed; /** - * Requests servive to remove all user data associated with the app's package. + * Requests to remove all content capture data associated with the app's package. * * @return this builder */ @@ -132,7 +130,7 @@ public final class UserDataRemovalRequest implements Parcelable { * Request service to remove data associated with a given {@link LocusId}. * * @param locusId the {@link LocusId} being requested to be removed. - * @param flags either {@link UserDataRemovalRequest#FLAG_IS_PREFIX} or {@code 0} + * @param flags either {@link DataRemovalRequest#FLAG_IS_PREFIX} or {@code 0} * * @return this builder */ @@ -154,17 +152,17 @@ public final class UserDataRemovalRequest implements Parcelable { } /** - * Builds the {@link UserDataRemovalRequest}. + * Builds the {@link DataRemovalRequest}. */ @NonNull - public UserDataRemovalRequest build() { + public DataRemovalRequest build() { throwIfDestroyed(); Preconditions.checkState(mForEverything || mLocusIds != null, "must call either #forEverything() or add one #addLocusId()"); mDestroyed = true; - return new UserDataRemovalRequest(this); + return new DataRemovalRequest(this); } private void throwIfDestroyed() { @@ -192,19 +190,19 @@ public final class UserDataRemovalRequest implements Parcelable { } } - public static final @android.annotation.NonNull Parcelable.Creator<UserDataRemovalRequest> CREATOR = - new Parcelable.Creator<UserDataRemovalRequest>() { + public static final @android.annotation.NonNull Parcelable.Creator<DataRemovalRequest> CREATOR = + new Parcelable.Creator<DataRemovalRequest>() { @Override @NonNull - public UserDataRemovalRequest createFromParcel(Parcel parcel) { - return new UserDataRemovalRequest(parcel); + public DataRemovalRequest createFromParcel(Parcel parcel) { + return new DataRemovalRequest(parcel); } @Override @NonNull - public UserDataRemovalRequest[] newArray(int size) { - return new UserDataRemovalRequest[size]; + public DataRemovalRequest[] newArray(int size) { + return new DataRemovalRequest[size]; } }; @@ -231,7 +229,7 @@ public final class UserDataRemovalRequest implements Parcelable { /** * Gets the flags associates with request. * - * @return either {@link UserDataRemovalRequest#FLAG_IS_PREFIX} or {@code 0}. + * @return either {@link DataRemovalRequest#FLAG_IS_PREFIX} or {@code 0}. */ @NonNull public @Flags int getFlags() { diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl index 7335073c59e0..ced941744387 100644 --- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl +++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl @@ -19,7 +19,7 @@ package android.view.contentcapture; import android.content.ComponentName; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureEvent; -import android.view.contentcapture.UserDataRemovalRequest; +import android.view.contentcapture.DataRemovalRequest; import android.os.IBinder; import com.android.internal.os.IResultReceiver; @@ -59,9 +59,9 @@ oneway interface IContentCaptureManager { void getServiceComponentName(in IResultReceiver result); /** - * Requests the removal of user data for the calling user. + * Requests the removal of content capture data for the calling user. */ - void removeUserData(in UserDataRemovalRequest request); + void removeData(in DataRemovalRequest request); /** * Returns whether the content capture feature is enabled for the calling user. diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 5e00425407ba..fd73856bf79b 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1951,6 +1951,23 @@ public final class InputMethodManager { } /** + * Unregister for IME state callbacks and applying visibility in + * {@link android.view.ImeInsetsSourceConsumer}. + * @hide + */ + public void unregisterImeConsumer(@NonNull ImeInsetsSourceConsumer imeInsetsConsumer) { + if (imeInsetsConsumer == null) { + throw new IllegalStateException("ImeInsetsSourceConsumer cannot be null."); + } + + synchronized (mH) { + if (mImeInsetsConsumer == imeInsetsConsumer) { + mImeInsetsConsumer = null; + } + } + } + + /** * Call showSoftInput with currently focused view. * @return {@code true} if IME can be shown. * @hide diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index b408129231e7..f2fa67d58839 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -316,16 +316,20 @@ public final class ConversationActions implements Parcelable { private final List<String> mHints; @Nullable private String mCallingPackageName; + @NonNull + private Bundle mExtras; private Request( @NonNull List<Message> conversation, @NonNull TextClassifier.EntityConfig typeConfig, int maxSuggestions, - @Nullable @Hint List<String> hints) { + @Nullable @Hint List<String> hints, + @NonNull Bundle extras) { mConversation = Preconditions.checkNotNull(conversation); mTypeConfig = Preconditions.checkNotNull(typeConfig); mMaxSuggestions = maxSuggestions; mHints = hints; + mExtras = extras; } private static Request readFromParcel(Parcel in) { @@ -336,12 +340,13 @@ public final class ConversationActions implements Parcelable { List<String> hints = new ArrayList<>(); in.readStringList(hints); String callingPackageName = in.readString(); - + Bundle extras = in.readBundle(); Request request = new Request( conversation, typeConfig, maxSuggestions, - hints); + hints, + extras); request.setCallingPackageName(callingPackageName); return request; } @@ -353,6 +358,7 @@ public final class ConversationActions implements Parcelable { parcel.writeInt(mMaxSuggestions); parcel.writeStringList(mHints); parcel.writeString(mCallingPackageName); + parcel.writeBundle(mExtras); } @Override @@ -421,6 +427,16 @@ public final class ConversationActions implements Parcelable { return mCallingPackageName; } + /** + * Returns the extended data related to this request. + * + * <p><b>NOTE: </b>Do not modify this bundle. + */ + @NonNull + public Bundle getExtras() { + return mExtras; + } + /** Builder object to construct the {@link Request} object. */ public static final class Builder { @NonNull @@ -431,6 +447,8 @@ public final class ConversationActions implements Parcelable { @Nullable @Hint private List<String> mHints; + @Nullable + private Bundle mExtras; /** * Constructs a builder. @@ -469,6 +487,13 @@ public final class ConversationActions implements Parcelable { return this; } + /** Sets a set of extended data to the request. */ + @NonNull + public Builder setExtras(@Nullable Bundle bundle) { + mExtras = bundle; + return this; + } + /** Builds the {@link Request} object. */ @NonNull public Request build() { @@ -480,7 +505,8 @@ public final class ConversationActions implements Parcelable { mMaxSuggestions, mHints == null ? Collections.emptyList() - : Collections.unmodifiableList(mHints)); + : Collections.unmodifiableList(mHints), + mExtras == null ? Bundle.EMPTY : mExtras); } } } diff --git a/core/java/android/view/textclassifier/ExtrasUtils.java b/core/java/android/view/textclassifier/ExtrasUtils.java index 7b236747bae6..11e0e2ca072c 100644 --- a/core/java/android/view/textclassifier/ExtrasUtils.java +++ b/core/java/android/view/textclassifier/ExtrasUtils.java @@ -36,6 +36,7 @@ import java.util.List; // TODO: Make this a TestApi for CTS testing. public final class ExtrasUtils { + // Keys for response objects. private static final String SERIALIZED_ENTITIES_DATA = "serialized-entities-data"; private static final String ENTITIES_EXTRAS = "entities-extras"; private static final String ACTION_INTENT = "action-intent"; @@ -48,6 +49,10 @@ public final class ExtrasUtils { private static final String TEXT_LANGUAGES = "text-languages"; private static final String ENTITIES = "entities"; + // Keys for request objects. + private static final String IS_SERIALIZED_ENTITY_DATA_ENABLED = + "is-serialized-entity-data-enabled"; + private ExtrasUtils() {} /** @@ -308,7 +313,23 @@ public final class ExtrasUtils { /** * Returns a list of entities contained in the {@code extra}. */ + @Nullable public static List<Bundle> getEntities(Bundle container) { return container.getParcelableArrayList(ENTITIES); } + + /** + * Whether the annotator should populate serialized entity data into the result object. + */ + public static boolean isSerializedEntityDataEnabled(TextLinks.Request request) { + return request.getExtras().getBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED); + } + + /** + * To indicate whether the annotator should populate serialized entity data in the result + * object. + */ + public static void putIsSerializedEntityDataEnabled(Bundle bundle, boolean isEnabled) { + bundle.putBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED, isEnabled); + } } diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java index 236f89bf921a..d3d61a7460c7 100644 --- a/core/java/android/view/textclassifier/TextClassifierEvent.java +++ b/core/java/android/view/textclassifier/TextClassifierEvent.java @@ -19,6 +19,7 @@ package android.view.textclassifier; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.icu.util.ULocale; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -30,54 +31,65 @@ import java.lang.annotation.RetentionPolicy; import java.util.Arrays; /** - * A text classifier event. + * This class represents events that are sent by components to the {@link TextClassifier} to report + * something of note that relates to a feature powered by the TextClassifier. The TextClassifier may + * log these events or use them to improve future responses to queries. + * <p> + * Each categories of the events have their own subclass. Events of each types has an associated + * set of related properties. You can find the specification of them in the subclasses. */ -// TODO: Comprehensive javadoc. -public final class TextClassifierEvent implements Parcelable { +public abstract class TextClassifierEvent implements Parcelable { - public static final @android.annotation.NonNull Creator<TextClassifierEvent> CREATOR = new Creator<TextClassifierEvent>() { - @Override - public TextClassifierEvent createFromParcel(Parcel in) { - return readFromParcel(in); - } - - @Override - public TextClassifierEvent[] newArray(int size) { - return new TextClassifierEvent[size]; - } - }; + private static final int PARCEL_TOKEN_TEXT_SELECTION_EVENT = 1; + private static final int PARCEL_TOKEN_TEXT_LINKIFY_EVENT = 2; + private static final int PARCEL_TOKEN_CONVERSATION_ACTION_EVENT = 3; + private static final int PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT = 4; /** @hide **/ @Retention(RetentionPolicy.SOURCE) - @IntDef({CATEGORY_UNDEFINED, CATEGORY_SELECTION, CATEGORY_LINKIFY, + @IntDef({CATEGORY_SELECTION, CATEGORY_LINKIFY, CATEGORY_CONVERSATION_ACTIONS, CATEGORY_LANGUAGE_DETECTION}) public @interface Category { // For custom event categories, use range 1000+. } - /** Undefined category */ - public static final int CATEGORY_UNDEFINED = 0; - /** Smart selection */ + + /** + * Smart selection + * + * @see TextSelectionEvent + */ public static final int CATEGORY_SELECTION = 1; - /** Linkify */ + /** + * Linkify + * + * @see TextLinkifyEvent + */ public static final int CATEGORY_LINKIFY = 2; - /** Conversation actions */ + /** + * Conversation actions + * + * @see ConversationActionsEvent + */ public static final int CATEGORY_CONVERSATION_ACTIONS = 3; - /** Language detection */ + /** + * Language detection + * + * @see LanguageDetectionEvent + */ public static final int CATEGORY_LANGUAGE_DETECTION = 4; /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef({TYPE_UNDEFINED, TYPE_SELECTION_STARTED, TYPE_SELECTION_MODIFIED, - TYPE_SMART_SELECTION_SINGLE, TYPE_SMART_SELECTION_MULTI, TYPE_AUTO_SELECTION, - TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION, - TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION, - TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL, - TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED}) + @IntDef({TYPE_SELECTION_STARTED, TYPE_SELECTION_MODIFIED, + TYPE_SMART_SELECTION_SINGLE, TYPE_SMART_SELECTION_MULTI, TYPE_AUTO_SELECTION, + TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION, + TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION, + TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL, + TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED}) public @interface Type { // For custom event types, use range 1,000,000+. } - /** User started a new selection. */ - public static final int TYPE_UNDEFINED = 0; + /** User started a new selection. */ public static final int TYPE_SELECTION_STARTED = 1; /** User modified an existing selection. */ @@ -119,63 +131,49 @@ public final class TextClassifierEvent implements Parcelable { /** TextClassifier generated some actions */ public static final int TYPE_ACTIONS_GENERATED = 20; - @Category private final int mEventCategory; - @Type private final int mEventType; - @Nullable private final String[] mEntityTypes; - @Nullable private final TextClassificationContext mEventContext; - @Nullable private final String mResultId; + @Category + private final int mEventCategory; + @Type + private final int mEventType; + @Nullable + private final String[] mEntityTypes; + @Nullable + private final TextClassificationContext mEventContext; + @Nullable + private final String mResultId; private final int mEventIndex; - private final long mEventTime; + private final float[] mScores; + @Nullable + private final String mModelName; + private final int[] mActionIndices; private final Bundle mExtras; - // Smart selection. - private final int mRelativeWordStartIndex; - private final int mRelativeWordEndIndex; - private final int mRelativeSuggestedWordStartIndex; - private final int mRelativeSuggestedWordEndIndex; - - // Smart action. - private final int[] mActionIndices; + private TextClassifierEvent(Builder builder) { + mEventCategory = builder.mEventCategory; + mEventType = builder.mEventType; + mEntityTypes = builder.mEntityTypes; + mEventContext = builder.mEventContext; + mResultId = builder.mResultId; + mEventIndex = builder.mEventIndex; + mScores = builder.mScores; + mModelName = builder.mModelName; + mActionIndices = builder.mActionIndices; + mExtras = builder.mExtras == null ? Bundle.EMPTY : builder.mExtras; + } - // Language detection. - @Nullable private final String mLanguage; - private final float mScore; - - @Nullable private final String mModelName; - - private TextClassifierEvent( - int eventCategory, - int eventType, - String[] entityTypes, - TextClassificationContext eventContext, - String resultId, - int eventIndex, - long eventTime, - Bundle extras, - int relativeWordStartIndex, - int relativeWordEndIndex, - int relativeSuggestedWordStartIndex, - int relativeSuggestedWordEndIndex, - int[] actionIndex, - String language, - float score, - String modelVersion) { - mEventCategory = eventCategory; - mEventType = eventType; - mEntityTypes = entityTypes; - mEventContext = eventContext; - mResultId = resultId; - mEventIndex = eventIndex; - mEventTime = eventTime; - mExtras = extras; - mRelativeWordStartIndex = relativeWordStartIndex; - mRelativeWordEndIndex = relativeWordEndIndex; - mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex; - mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex; - mActionIndices = actionIndex; - mLanguage = language; - mScore = score; - mModelName = modelVersion; + private TextClassifierEvent(Parcel in) { + mEventCategory = in.readInt(); + mEventType = in.readInt(); + mEntityTypes = in.readStringArray(); + mEventContext = in.readParcelable(null); + mResultId = in.readString(); + mEventIndex = in.readInt(); + int scoresLength = in.readInt(); + mScores = new float[scoresLength]; + in.readFloatArray(mScores); + mModelName = in.readString(); + mActionIndices = in.createIntArray(); + mExtras = in.readBundle(); } @Override @@ -183,44 +181,62 @@ public final class TextClassifierEvent implements Parcelable { return 0; } + @NonNull + public static final Creator<TextClassifierEvent> CREATOR = new Creator<TextClassifierEvent>() { + @Override + public TextClassifierEvent createFromParcel(Parcel in) { + int token = in.readInt(); + if (token == PARCEL_TOKEN_TEXT_SELECTION_EVENT) { + return new TextSelectionEvent(in); + } + if (token == PARCEL_TOKEN_TEXT_LINKIFY_EVENT) { + return new TextLinkifyEvent(in); + } + if (token == PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT) { + return new LanguageDetectionEvent(in); + } + if (token == PARCEL_TOKEN_CONVERSATION_ACTION_EVENT) { + return new ConversationActionsEvent(in); + } + throw new IllegalStateException("Unexpected input event type token in parcel."); + } + + @Override + public TextClassifierEvent[] newArray(int size) { + return new TextClassifierEvent[size]; + } + }; + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(getParcelToken()); dest.writeInt(mEventCategory); dest.writeInt(mEventType); dest.writeStringArray(mEntityTypes); dest.writeParcelable(mEventContext, flags); dest.writeString(mResultId); dest.writeInt(mEventIndex); - dest.writeLong(mEventTime); - dest.writeBundle(mExtras); - dest.writeInt(mRelativeWordStartIndex); - dest.writeInt(mRelativeWordEndIndex); - dest.writeInt(mRelativeSuggestedWordStartIndex); - dest.writeInt(mRelativeSuggestedWordEndIndex); - dest.writeIntArray(mActionIndices); - dest.writeString(mLanguage); - dest.writeFloat(mScore); + dest.writeInt(mScores.length); + dest.writeFloatArray(mScores); dest.writeString(mModelName); + dest.writeIntArray(mActionIndices); + dest.writeBundle(mExtras); } - private static TextClassifierEvent readFromParcel(Parcel in) { - return new TextClassifierEvent( - /* eventCategory= */ in.readInt(), - /* eventType= */ in.readInt(), - /* entityTypes=*/ in.readStringArray(), - /* eventContext= */ in.readParcelable(null), - /* resultId= */ in.readString(), - /* eventIndex= */ in.readInt(), - /* eventTime= */ in.readLong(), - /* extras= */ in.readBundle(), - /* relativeWordStartIndex= */ in.readInt(), - /* relativeWordEndIndex= */ in.readInt(), - /* relativeSuggestedWordStartIndex= */ in.readInt(), - /* relativeSuggestedWordEndIndex= */ in.readInt(), - /* actionIndices= */ in.createIntArray(), - /* language= */ in.readString(), - /* score= */ in.readFloat(), - /* modelVersion= */ in.readString()); + private int getParcelToken() { + if (this instanceof TextSelectionEvent) { + return PARCEL_TOKEN_TEXT_SELECTION_EVENT; + } + if (this instanceof TextLinkifyEvent) { + return PARCEL_TOKEN_TEXT_LINKIFY_EVENT; + } + if (this instanceof LanguageDetectionEvent) { + return PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT; + } + if (this instanceof ConversationActionsEvent) { + return PARCEL_TOKEN_CONVERSATION_ACTION_EVENT; + } + throw new IllegalArgumentException("Unexpected type: " + this.getClass().getSimpleName()); } /** @@ -241,6 +257,8 @@ public final class TextClassifierEvent implements Parcelable { /** * Returns an array of entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}. + * + * @see Builder#setEntityTypes(String...) for supported types. */ @NonNull public String[] getEntityTypes() { @@ -270,52 +288,20 @@ public final class TextClassifierEvent implements Parcelable { return mEventIndex; } - // TODO: Remove this API. /** - * Returns the time this event occurred. This is the number of milliseconds since - * January 1, 1970, 00:00:00 GMT. 0 indicates not set. - */ - public long getEventTime() { - return mEventTime; - } - - /** - * Returns a bundle containing non-structured extra information about this event. - * - * <p><b>NOTE: </b>Do not modify this bundle. + * Returns the scores of the suggestions. */ @NonNull - public Bundle getExtras() { - return mExtras; - } - - /** - * For smart selection. Returns the relative word index of the start of the selection. - */ - public int getRelativeWordStartIndex() { - return mRelativeWordStartIndex; - } - - /** - * For smart selection. Returns the relative word (exclusive) index of the end of the selection. - */ - public int getRelativeWordEndIndex() { - return mRelativeWordEndIndex; - } - - /** - * For smart selection. Returns the relative word index of the start of the smart selection. - */ - public int getRelativeSuggestedWordStartIndex() { - return mRelativeSuggestedWordStartIndex; + public float[] getScores() { + return mScores; } /** - * For smart selection. Returns the relative word (exclusive) index of the end of the - * smart selection. + * Returns the model name. */ - public int getRelativeSuggestedWordEndIndex() { - return mRelativeSuggestedWordEndIndex; + @Nullable + public String getModelName() { + return mModelName; } /** @@ -323,6 +309,8 @@ public final class TextClassifierEvent implements Parcelable { * Actions are usually returned by the text classifier in priority order with the most * preferred action at index 0. This list gives an indication of the position of the actions * that are being reported. + * + * @see Builder#setActionIndices(int...) */ @NonNull public int[] getActionIndices() { @@ -330,110 +318,158 @@ public final class TextClassifierEvent implements Parcelable { } /** - * For language detection. Returns the language tag for the detected locale. - * @see java.util.Locale#forLanguageTag(String). - */ - @Nullable - public String getLanguage() { - return mLanguage; - } - - /** - * Returns the score of the suggestion. + * Returns a bundle containing non-structured extra information about this event. + * + * <p><b>NOTE: </b>Do not modify this bundle. */ - public float getScore() { - return mScore; + @NonNull + public Bundle getExtras() { + return mExtras; } - /** - * Returns the model name. - * @hide - */ - @Nullable - public String getModelName() { - return mModelName; + @Override + public String toString() { + StringBuilder out = new StringBuilder(128); + out.append(this.getClass().getSimpleName()); + out.append("{"); + out.append("mEventCategory=").append(mEventCategory); + out.append(", mEventTypes=").append(Arrays.toString(mEntityTypes)); + out.append(", mEventContext=").append(mEventContext); + out.append(", mResultId=").append(mResultId); + out.append(", mEventIndex=").append(mEventIndex); + out.append(", mExtras=").append(mExtras); + out.append(", mScores=").append(Arrays.toString(mScores)); + out.append(", mModelName=").append(mModelName); + out.append(", mActionIndices=").append(Arrays.toString(mActionIndices)); + out.append("}"); + return out.toString(); } /** * Builder to build a text classifier event. + * + * @param <T> The subclass to be built. */ - public static final class Builder { + public abstract static class Builder<T extends Builder<T>> { private final int mEventCategory; private final int mEventType; private String[] mEntityTypes = new String[0]; - @Nullable private TextClassificationContext mEventContext; - @Nullable private String mResultId; + @Nullable + private TextClassificationContext mEventContext; + @Nullable + private String mResultId; private int mEventIndex; - private long mEventTime; - @Nullable private Bundle mExtras; - private int mRelativeWordStartIndex; - private int mRelativeWordEndIndex; - private int mRelativeSuggestedWordStartIndex; - private int mRelativeSuggestedWordEndIndex; - private int[] mActionIndices = new int[0]; - @Nullable private String mLanguage; - private float mScore; - + private float[] mScores = new float[0]; + @Nullable private String mModelName; + private int[] mActionIndices = new int[0]; + @Nullable + private Bundle mExtras; /** * Creates a builder for building {@link TextClassifierEvent}s. * * @param eventCategory The event category. e.g. {@link #CATEGORY_SELECTION} - * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED} + * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED} */ - public Builder(@Category int eventCategory, @Type int eventType) { + private Builder(@Category int eventCategory, @Type int eventType) { mEventCategory = eventCategory; mEventType = eventType; } /** * Sets the entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}. + * <p> + * Supported types: + * <p>See {@link TextClassifier.EntityType} + * <p>See {@link ConversationAction.ActionType} + * <p>See {@link ULocale#toLanguageTag()} */ @NonNull - public Builder setEntityTypes(@NonNull String... entityTypes) { + public T setEntityTypes(@NonNull String... entityTypes) { + Preconditions.checkNotNull(entityTypes); mEntityTypes = new String[entityTypes.length]; System.arraycopy(entityTypes, 0, mEntityTypes, 0, entityTypes.length); - return this; + return self(); } /** * Sets the event context. */ @NonNull - public Builder setEventContext(@Nullable TextClassificationContext eventContext) { + public T setEventContext(@Nullable TextClassificationContext eventContext) { mEventContext = eventContext; - return this; + return self(); } /** * Sets the id of the text classifier result related to this event. */ @NonNull - public Builder setResultId(@Nullable String resultId) { + public T setResultId(@Nullable String resultId) { mResultId = resultId; - return this; + return self(); } /** - * Sets the index of this events in the series of events it belongs to. + * Sets the index of this event in the series of events it belongs to. */ @NonNull - public Builder setEventIndex(int eventIndex) { + public T setEventIndex(int eventIndex) { mEventIndex = eventIndex; - return this; + return self(); + } + + /** + * Sets the scores of the suggestions. + */ + @NonNull + public T setScores(@NonNull float... scores) { + Preconditions.checkNotNull(scores); + mScores = new float[scores.length]; + System.arraycopy(scores, 0, mScores, 0, scores.length); + return self(); } - // TODO: Remove this API. /** - * Sets the time this event occurred. This is the number of milliseconds since - * January 1, 1970, 00:00:00 GMT. 0 indicates not set. + * Sets the model name string. */ @NonNull - public Builder setEventTime(long eventTime) { - mEventTime = eventTime; - return this; + public T setModelName(@Nullable String modelVersion) { + mModelName = modelVersion; + return self(); + } + + /** + * Sets the indices of the actions involved in this event. Actions are usually returned by + * the text classifier in priority order with the most preferred action at index 0. + * These indices give an indication of the position of the actions that are being reported. + * <p> + * E.g. + * <pre> + * // 3 smart actions are shown at index 0, 1, 2 respectively in response to a link click. + * new TextClassifierEvent.Builder(CATEGORY_LINKIFY, TYPE_ACTIONS_SHOWN) + * .setEventIndex(0, 1, 2) + * ... + * .build(); + * + * ... + * + * // Smart action at index 1 is activated. + * new TextClassifierEvent.Builder(CATEGORY_LINKIFY, TYPE_SMART_ACTION) + * .setEventIndex(1) + * ... + * .build(); + * </pre> + * + * @see TextClassification#getActions() + */ + @NonNull + public T setActionIndices(@NonNull int... actionIndices) { + mActionIndices = new int[actionIndices.length]; + System.arraycopy(actionIndices, 0, mActionIndices, 0, actionIndices.length); + return self(); } /** @@ -445,136 +481,545 @@ public final class TextClassifierEvent implements Parcelable { * objects in this bundle. */ @NonNull - public Builder setExtras(@NonNull Bundle extras) { + public T setExtras(@NonNull Bundle extras) { mExtras = Preconditions.checkNotNull(extras); - return this; + return self(); + } + + abstract T self(); + } + + /** + * This class represents events that are related to the smart text selection feature. + * <p> + * <pre> + * // User started a selection. e.g. "York" in text "New York City, NY". + * new TextSelectionEvent.Builder(TYPE_SELECTION_STARTED) + * .setEventContext(classificationContext) + * .setEventIndex(0) + * .build(); + * + * // System smart-selects a recognized entity. e.g. "New York City". + * new TextSelectionEvent.Builder(TYPE_SMART_SELECTION_MULTI) + * .setEventContext(classificationContext) + * .setResultId(textSelection.getId()) + * .setRelativeWordStartIndex(-1) // Goes back one word to "New" from "York". + * .setRelativeWordEndIndex(2) // Goes forward 2 words from "York" to start of ",". + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(1) + * .build(); + * + * // User resets the selection to the original selection. i.e. "York". + * new TextSelectionEvent.Builder(TYPE_SELECTION_RESET) + * .setEventContext(classificationContext) + * .setResultId(textSelection.getId()) + * .setRelativeSuggestedWordStartIndex(-1) // Repeated from above. + * .setRelativeSuggestedWordEndIndex(2) // Repeated from above. + * .setRelativeWordStartIndex(0) // Original selection is always at (0, 1]. + * .setRelativeWordEndIndex(1) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(2) + * .build(); + * + * // User modified the selection. e.g. "New". + * new TextSelectionEvent.Builder(TYPE_SELECTION_MODIFIED) + * .setEventContext(classificationContext) + * .setResultId(textSelection.getId()) + * .setRelativeSuggestedWordStartIndex(-1) // Repeated from above. + * .setRelativeSuggestedWordEndIndex(2) // Repeated from above. + * .setRelativeWordStartIndex(-1) // Goes backward one word from "York" to + * "New". + * .setRelativeWordEndIndex(0) // Goes backward one word to exclude "York". + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(3) + * .build(); + * + * // Smart (contextual) actions (at indices, 0, 1, 2) presented to the user. + * // e.g. "Map", "Ride share", "Explore". + * new TextSelectionEvent.Builder(TYPE_ACTIONS_SHOWN) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setActionIndices(0, 1, 2) + * .setEventIndex(4) + * .build(); + * + * // User chooses the "Copy" action. + * new TextSelectionEvent.Builder(TYPE_COPY_ACTION) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(5) + * .build(); + * + * // User chooses smart action at index 1. i.e. "Ride share". + * new TextSelectionEvent.Builder(TYPE_SMART_ACTION) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setActionIndices(1) + * .setEventIndex(5) + * .build(); + * + * // Selection dismissed. + * new TextSelectionEvent.Builder(TYPE_SELECTION_DESTROYED) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(6) + * .build(); + * </pre> + * <p> + */ + public static final class TextSelectionEvent extends TextClassifierEvent implements Parcelable { + + @NonNull + public static final Creator<TextSelectionEvent> CREATOR = + new Creator<TextSelectionEvent>() { + @Override + public TextSelectionEvent createFromParcel(Parcel in) { + in.readInt(); // skip token, we already know this is a TextSelectionEvent + return new TextSelectionEvent(in); + } + + @Override + public TextSelectionEvent[] newArray(int size) { + return new TextSelectionEvent[size]; + } + }; + + final int mRelativeWordStartIndex; + final int mRelativeWordEndIndex; + final int mRelativeSuggestedWordStartIndex; + final int mRelativeSuggestedWordEndIndex; + + private TextSelectionEvent(TextSelectionEvent.Builder builder) { + super(builder); + mRelativeWordStartIndex = builder.mRelativeWordStartIndex; + mRelativeWordEndIndex = builder.mRelativeWordEndIndex; + mRelativeSuggestedWordStartIndex = builder.mRelativeSuggestedWordStartIndex; + mRelativeSuggestedWordEndIndex = builder.mRelativeSuggestedWordEndIndex; + } + + private TextSelectionEvent(Parcel in) { + super(in); + mRelativeWordStartIndex = in.readInt(); + mRelativeWordEndIndex = in.readInt(); + mRelativeSuggestedWordStartIndex = in.readInt(); + mRelativeSuggestedWordEndIndex = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mRelativeWordStartIndex); + dest.writeInt(mRelativeWordEndIndex); + dest.writeInt(mRelativeSuggestedWordStartIndex); + dest.writeInt(mRelativeSuggestedWordEndIndex); } /** - * For smart selection. Sets the relative word index of the start of the selection. + * Returns the relative word index of the start of the selection. */ - @NonNull - public Builder setRelativeWordStartIndex(int relativeWordStartIndex) { - mRelativeWordStartIndex = relativeWordStartIndex; - return this; + public int getRelativeWordStartIndex() { + return mRelativeWordStartIndex; } /** - * For smart selection. Sets the relative word (exclusive) index of the end of the - * selection. + * Returns the relative word (exclusive) index of the end of the selection. */ - @NonNull - public Builder setRelativeWordEndIndex(int relativeWordEndIndex) { - mRelativeWordEndIndex = relativeWordEndIndex; - return this; + public int getRelativeWordEndIndex() { + return mRelativeWordEndIndex; } /** - * For smart selection. Sets the relative word index of the start of the smart selection. + * Returns the relative word index of the start of the smart selection. */ - @NonNull - public Builder setRelativeSuggestedWordStartIndex(int relativeSuggestedWordStartIndex) { - mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex; - return this; + public int getRelativeSuggestedWordStartIndex() { + return mRelativeSuggestedWordStartIndex; } /** - * For smart selection. Sets the relative word (exclusive) index of the end of the + * Returns the relative word (exclusive) index of the end of the * smart selection. */ - @NonNull - public Builder setRelativeSuggestedWordEndIndex(int relativeSuggestedWordEndIndex) { - mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex; - return this; + public int getRelativeSuggestedWordEndIndex() { + return mRelativeSuggestedWordEndIndex; } /** - * Sets the indices of the actions involved in this event. Actions are usually returned by - * the text classifier in priority order with the most preferred action at index 0. - * This index gives an indication of the position of the action that is being reported. + * Builder class for {@link TextSelectionEvent}. */ + public static final class Builder extends + TextClassifierEvent.Builder<TextSelectionEvent.Builder> { + int mRelativeWordStartIndex; + int mRelativeWordEndIndex; + int mRelativeSuggestedWordStartIndex; + int mRelativeSuggestedWordEndIndex; + + /** + * Creates a builder for building {@link TextSelectionEvent}s. + * + * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED} + */ + public Builder(@Type int eventType) { + super(CATEGORY_SELECTION, eventType); + } + + /** + * Sets the relative word index of the start of the selection. + */ + @NonNull + public Builder setRelativeWordStartIndex(int relativeWordStartIndex) { + mRelativeWordStartIndex = relativeWordStartIndex; + return this; + } + + /** + * Sets the relative word (exclusive) index of the end of the + * selection. + */ + @NonNull + public Builder setRelativeWordEndIndex(int relativeWordEndIndex) { + mRelativeWordEndIndex = relativeWordEndIndex; + return this; + } + + /** + * Sets the relative word index of the start of the smart + * selection. + */ + @NonNull + public Builder setRelativeSuggestedWordStartIndex(int relativeSuggestedWordStartIndex) { + mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex; + return this; + } + + /** + * Sets the relative word (exclusive) index of the end of the + * smart selection. + */ + @NonNull + public Builder setRelativeSuggestedWordEndIndex(int relativeSuggestedWordEndIndex) { + mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex; + return this; + } + + @Override + TextSelectionEvent.Builder self() { + return this; + } + + /** + * Builds and returns a {@link TextSelectionEvent}. + */ + @NonNull + public TextSelectionEvent build() { + return new TextSelectionEvent(this); + } + } + } + + /** + * This class represents events that are related to the smart linkify feature. + * <p> + * <pre> + * // User clicked on a link. + * new TextLinkifyEvent.Builder(TYPE_LINK_CLICKED) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(0) + * .build(); + * + * // Smart (contextual) actions presented to the user in response to a link click. + * new TextLinkifyEvent.Builder(TYPE_ACTIONS_SHOWN) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setActionIndices(range(textClassification.getActions().size())) + * .setEventIndex(1) + * .build(); + * + * // User chooses smart action at index 0. + * new TextLinkifyEvent.Builder(TYPE_SMART_ACTION) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setActionIndices(0) + * .setEventIndex(2) + * .build(); + * </pre> + */ + public static final class TextLinkifyEvent extends TextClassifierEvent implements Parcelable { + @NonNull - public Builder setActionIndices(@NonNull int... actionIndices) { - mActionIndices = new int[actionIndices.length]; - System.arraycopy(actionIndices, 0, mActionIndices, 0, actionIndices.length); - return this; + public static final Creator<TextLinkifyEvent> CREATOR = + new Creator<TextLinkifyEvent>() { + @Override + public TextLinkifyEvent createFromParcel(Parcel in) { + in.readInt(); // skip token, we already know this is a TextLinkifyEvent + return new TextLinkifyEvent(in); + } + + @Override + public TextLinkifyEvent[] newArray(int size) { + return new TextLinkifyEvent[size]; + } + }; + + private TextLinkifyEvent(Parcel in) { + super(in); + } + + private TextLinkifyEvent(TextLinkifyEvent.Builder builder) { + super(builder); } /** - * For language detection. Sets the language tag for the detected locale. - * @see java.util.Locale#forLanguageTag(String). + * Builder class for {@link TextLinkifyEvent}. */ + public static final class Builder + extends TextClassifierEvent.Builder<TextLinkifyEvent.Builder> { + /** + * Creates a builder for building {@link TextLinkifyEvent}s. + * + * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION} + */ + public Builder(@Type int eventType) { + super(TextClassifierEvent.CATEGORY_LINKIFY, eventType); + } + + @Override + Builder self() { + return this; + } + + /** + * Builds and returns a {@link TextLinkifyEvent}. + */ + @NonNull + public TextLinkifyEvent build() { + return new TextLinkifyEvent(this); + } + } + } + + /** + * This class represents events that are related to the language detection feature. + * <p> + * <pre> + * // Translate action shown for foreign text. + * new LanguageDetectionEvent.Builder(TYPE_ACTIONS_SHOWN) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(language) + * .setScore(score) + * .setActionIndices(textClassification.getActions().indexOf(translateAction)) + * .setEventIndex(0) + * .build(); + * + * // Translate action selected. + * new LanguageDetectionEvent.Builder(TYPE_SMART_ACTION) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(language) + * .setScore(score) + * .setActionIndices(textClassification.getActions().indexOf(translateAction)) + * .setEventIndex(1) + * .build(); + */ + public static final class LanguageDetectionEvent extends TextClassifierEvent + implements Parcelable { + @NonNull - public Builder setLanguage(@Nullable String language) { - mLanguage = language; - return this; + public static final Creator<LanguageDetectionEvent> CREATOR = + new Creator<LanguageDetectionEvent>() { + @Override + public LanguageDetectionEvent createFromParcel(Parcel in) { + // skip token, we already know this is a LanguageDetectionEvent. + in.readInt(); + return new LanguageDetectionEvent(in); + } + + @Override + public LanguageDetectionEvent[] newArray(int size) { + return new LanguageDetectionEvent[size]; + } + }; + + @Nullable + private final ULocale mLocale; + + private LanguageDetectionEvent(Parcel in) { + super(in); + final String languageTag = in.readString(); + mLocale = languageTag == null ? null : ULocale.forLanguageTag(languageTag); + } + + private LanguageDetectionEvent(LanguageDetectionEvent.Builder builder) { + super(builder); + mLocale = builder.mLocale; } /** - * Sets the score of the suggestion. + * Returns the detected locale. */ - @NonNull - public Builder setScore(float score) { - mScore = score; - return this; + @Nullable + public ULocale getLocale() { + return mLocale; } /** - * Sets the model name string. - * @hide + * Builder class for {@link LanguageDetectionEvent}. */ - public Builder setModelName(@Nullable String modelVersion) { - mModelName = modelVersion; - return this; + public static final class Builder + extends TextClassifierEvent.Builder<LanguageDetectionEvent.Builder> { + @Nullable + private ULocale mLocale; + + /** + * Creates a builder for building {@link TextSelectionEvent}s. + * + * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION} + */ + public Builder(@Type int eventType) { + super(TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION, eventType); + } + + /** + * Sets the detected locale. + */ + @NonNull + public Builder setLocale(@Nullable ULocale locale) { + mLocale = locale; + return this; + } + + @Override + Builder self() { + return this; + } + + /** + * Builds and returns a {@link LanguageDetectionEvent}. + */ + @NonNull + public LanguageDetectionEvent build() { + return new LanguageDetectionEvent(this); + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(mLocale == null ? null : mLocale.toLanguageTag()); + } + } + + /** + * This class represents events that are related to the conversation actions feature. + * <p> + * <pre> + * // Conversation (contextual) actions/replies generated. + * new ConversationActionsEvent.Builder(TYPE_ACTIONS_GENERATED) + * .setEventContext(classificationContext) + * .setResultId(conversationActions.getId()) + * .setEntityTypes(getTypes(conversationActions)) + * .setActionIndices(range(conversationActions.getActions().size())) + * .setEventIndex(0) + * .build(); + * + * // Conversation actions/replies presented to user. + * new ConversationActionsEvent.Builder(TYPE_ACTIONS_SHOWN) + * .setEventContext(classificationContext) + * .setResultId(conversationActions.getId()) + * .setEntityTypes(getTypes(conversationActions)) + * .setActionIndices(range(conversationActions.getActions().size())) + * .setEventIndex(1) + * .build(); + * + * // User clicked the "Reply" button to compose their custom reply. + * new ConversationActionsEvent.Builder(TYPE_MANUAL_REPLY) + * .setEventContext(classificationContext) + * .setResultId(conversationActions.getId()) + * .setEventIndex(2) + * .build(); + * + * // User selected a smart (contextual) action/reply. + * new ConversationActionsEvent.Builder(TYPE_SMART_ACTION) + * .setEventContext(classificationContext) + * .setResultId(conversationActions.getId()) + * .setEntityTypes(conversationActions.get(1).getType()) + * .setScore(conversationAction.get(1).getConfidenceScore()) + * .setActionIndices(1) + * .setEventIndex(2) + * .build(); + * </pre> + */ + public static final class ConversationActionsEvent extends TextClassifierEvent + implements Parcelable { + + @NonNull + public static final Creator<ConversationActionsEvent> CREATOR = + new Creator<ConversationActionsEvent>() { + @Override + public ConversationActionsEvent createFromParcel(Parcel in) { + // skip token, we already know this is a ConversationActionsEvent. + in.readInt(); + return new ConversationActionsEvent(in); + } + + @Override + public ConversationActionsEvent[] newArray(int size) { + return new ConversationActionsEvent[size]; + } + }; + + private ConversationActionsEvent(Parcel in) { + super(in); + } + + private ConversationActionsEvent(ConversationActionsEvent.Builder builder) { + super(builder); } /** - * Builds and returns a text classifier event. + * Builder class for {@link ConversationActionsEvent}. */ - @NonNull - public TextClassifierEvent build() { - mExtras = mExtras == null ? Bundle.EMPTY : mExtras; - return new TextClassifierEvent( - mEventCategory, - mEventType, - mEntityTypes, - mEventContext, - mResultId, - mEventIndex, - mEventTime, - mExtras, - mRelativeWordStartIndex, - mRelativeWordEndIndex, - mRelativeSuggestedWordStartIndex, - mRelativeSuggestedWordEndIndex, - mActionIndices, - mLanguage, - mScore, - mModelName); - } - // TODO: Add build(boolean validate). - } + public static final class Builder + extends TextClassifierEvent.Builder<ConversationActionsEvent.Builder> { + /** + * Creates a builder for building {@link TextSelectionEvent}s. + * + * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION} + */ + public Builder(@Type int eventType) { + super(TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType); + } - @Override - public String toString() { - StringBuilder out = new StringBuilder(128); - out.append("TextClassifierEvent{"); - out.append("mEventCategory=").append(mEventCategory); - out.append(", mEventTypes=").append(Arrays.toString(mEntityTypes)); - out.append(", mEventContext=").append(mEventContext); - out.append(", mResultId=").append(mResultId); - out.append(", mEventIndex=").append(mEventIndex); - out.append(", mEventTime=").append(mEventTime); - out.append(", mExtras=").append(mExtras); - out.append(", mRelativeWordStartIndex=").append(mRelativeWordStartIndex); - out.append(", mRelativeWordEndIndex=").append(mRelativeWordEndIndex); - out.append(", mRelativeSuggestedWordStartIndex=").append(mRelativeSuggestedWordStartIndex); - out.append(", mRelativeSuggestedWordEndIndex=").append(mRelativeSuggestedWordEndIndex); - out.append(", mActionIndices=").append(Arrays.toString(mActionIndices)); - out.append(", mLanguage=").append(mLanguage); - out.append(", mScore=").append(mScore); - out.append(", mModelName=").append(mModelName); - out.append("}"); - return out.toString(); + @Override + Builder self() { + return this; + } + + /** + * Builds and returns a {@link ConversationActionsEvent}. + */ + @NonNull + public ConversationActionsEvent build() { + return new ConversationActionsEvent(this); + } + } } } diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java index 6a122506d0ac..3e088b8565f2 100644 --- a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java +++ b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java @@ -65,9 +65,10 @@ public final class TextClassifierEventTronLogger { final LogMaker log = new LogMaker(category) .setSubtype(getLogType(event)) .addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId()) - .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event)) - .addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScore()); - + .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event)); + if (event.getScores().length >= 1) { + log.addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScores()[0]); + } String[] entityTypes = event.getEntityTypes(); // The old logger does not support a field of list type, and thus workaround by store them // in three separate fields. This is not an issue with the new logger. diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 323bf597ab55..3297523b0da9 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -307,6 +307,8 @@ public final class TextClassifierImpl implements TextClassifier { final String detectLanguageTags = detectLanguageTagsFromText(request.getText()); final AnnotatorModel annotatorImpl = getAnnotatorImpl(request.getDefaultLocales()); + final boolean isSerializedEntityDataEnabled = + ExtrasUtils.isSerializedEntityDataEnabled(request); final AnnotatorModel.AnnotatedSpan[] annotations = annotatorImpl.annotate( textString, @@ -314,7 +316,10 @@ public final class TextClassifierImpl implements TextClassifier { refTime.toInstant().toEpochMilli(), refTime.getZone().getId(), localesString, - detectLanguageTags)); + detectLanguageTags, + entitiesToIdentify, + AnnotatorModel.AnnotationUsecase.SMART.getValue(), + isSerializedEntityDataEnabled)); for (AnnotatorModel.AnnotatedSpan span : annotations) { final AnnotatorModel.ClassificationResult[] results = span.getClassification(); @@ -326,7 +331,11 @@ public final class TextClassifierImpl implements TextClassifier { for (int i = 0; i < results.length; i++) { entityScores.put(results[i].getCollection(), results[i].getScore()); } - builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores); + Bundle extras = new Bundle(); + if (isSerializedEntityDataEnabled) { + ExtrasUtils.putEntities(extras, results); + } + builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores, extras); } final TextLinks links = builder.build(); final long endTimeMs = System.currentTimeMillis(); @@ -451,10 +460,6 @@ public final class TextClassifierImpl implements TextClassifier { Collection<String> expectedTypes = resolveActionTypesFromRequest(request); List<ConversationAction> conversationActions = new ArrayList<>(); for (ActionsSuggestionsModel.ActionSuggestion nativeSuggestion : nativeSuggestions) { - if (request.getMaxSuggestions() >= 0 - && conversationActions.size() == request.getMaxSuggestions()) { - break; - } String actionType = nativeSuggestion.getActionType(); if (!expectedTypes.contains(actionType)) { continue; @@ -484,6 +489,10 @@ public final class TextClassifierImpl implements TextClassifier { } conversationActions = ActionsSuggestionsHelper.removeActionsWithDuplicates(conversationActions); + if (request.getMaxSuggestions() >= 0 + && conversationActions.size() > request.getMaxSuggestions()) { + conversationActions = conversationActions.subList(0, request.getMaxSuggestions()); + } String resultId = ActionsSuggestionsHelper.createResultId( mContext, request.getConversation(), diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 26dba45666fc..137b67c6e63e 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -413,9 +413,6 @@ public class WebView extends AbsoluteLayout if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) { setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES); } - if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) { - setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES); - } if (context == null) { throw new IllegalArgumentException("Invalid context argument"); @@ -2799,11 +2796,6 @@ public class WebView extends AbsoluteLayout } @Override - public void onProvideContentCaptureStructure(ViewStructure structure, int flags) { - mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags); - } - - @Override public void autofill(SparseArray<AutofillValue>values) { mProvider.getViewDelegate().autofill(values); } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index c55f7d654548..c3bb9a0201d0 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -1318,8 +1318,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { @ViewStructureType int viewFor, int flags) { super.onProvideStructure(structure, viewFor, flags); - if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL - || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { + if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { final Adapter adapter = getAdapter(); if (adapter == null) return; diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 564cfdd20d76..51ca80524090 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -933,12 +933,11 @@ public final class SelectionActionModeHelper { final String language = ExtrasUtils.getEntityType(foreignLanguageExtra); final float score = ExtrasUtils.getScore(foreignLanguageExtra); final String model = ExtrasUtils.getModelName(foreignLanguageExtra); - return new TextClassifierEvent.Builder( - TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION, eventType) + return new TextClassifierEvent.LanguageDetectionEvent.Builder(eventType) .setEventContext(classificationContext) .setResultId(classification.getId()) .setEntityTypes(language) - .setScore(score) + .setScores(score) .setActionIndices(classification.getActions().indexOf(translateAction)) .setModelName(model) .build(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a961783dab7c..618b05f8cb00 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -161,8 +161,6 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AnimationUtils; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; -import android.view.contentcapture.ContentCaptureManager; -import android.view.contentcapture.ContentCaptureSession; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -978,9 +976,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) { setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES); } - if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) { - setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES); - } setTextInternal(""); @@ -10520,8 +10515,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Notify managers (such as {@link AutofillManager} and {@link ContentCaptureManager}) that are - * interested on text changes. + * Notify managers (such as {@link AutofillManager}) that are interested in text changes. */ private void notifyListeningManagersAfterTextChanged() { @@ -10537,22 +10531,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener afm.notifyValueChanged(TextView.this); } } - - // TODO(b/121045053): should use a flag / boolean to keep status of SHOWN / HIDDEN instead - // of using isLaidout(), so it's not called in cases where it's laid out but a - // notifyAppeared was not sent. - - // ContentCapture - if (isLaidOut() && isImportantForContentCapture() && isTextEditable()) { - final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class); - if (cm != null && cm.isContentCaptureEnabled()) { - final ContentCaptureSession session = getContentCaptureSession(); - if (session != null) { - // TODO(b/111276913): pass flags when edited by user / add CTS test - session.notifyViewTextChanged(getAutofillId(), getText()); - } - } - } } private boolean isAutofillable() { @@ -11386,8 +11364,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final boolean isPassword = hasPasswordTransformationMethod() || isPasswordInputType(getInputType()); - if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL - || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { + if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId); } @@ -11403,12 +11380,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL - || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { + if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { if (mLayout == null) { - if (viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { - Log.w(LOG_TAG, "onProvideContentCaptureStructure(): calling assumeLayout()"); - } assumeLayout(); } Layout layout = mLayout; @@ -11496,8 +11469,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - if (viewFor == VIEW_STRUCTURE_FOR_ASSIST - || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { + if (viewFor == VIEW_STRUCTURE_FOR_ASSIST) { // Extract style information that applies to the TextView as a whole. int style = 0; int typefaceStyle = getTypefaceStyle(); @@ -11525,8 +11497,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener structure.setTextStyle(getTextSize(), getCurrentTextColor(), AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style); } - if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL - || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { + if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { structure.setMinTextEms(getMinEms()); structure.setMaxTextEms(getMaxEms()); int maxLength = -1; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 4d4fe0397791..54338bf6a176 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -24,6 +24,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; import android.app.prediction.AppPredictionContext; @@ -83,6 +84,7 @@ import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; import android.service.chooser.IChooserTargetService; +import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.util.AttributeSet; import android.util.HashedStringCache; @@ -131,6 +133,7 @@ import java.util.List; public class ChooserActivity extends ResolverActivity { private static final String TAG = "ChooserActivity"; + /** * Boolean extra to change the following behavior: Normally, ChooserActivity finishes itself * in onStop when launched in a new task. If this extra is set to true, we do not finish @@ -141,7 +144,6 @@ public class ChooserActivity extends ResolverActivity { private static final boolean DEBUG = false; - /** * If {@link #USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS} and this is set to true, * {@link AppPredictionManager} will be queried for direct share targets. @@ -433,18 +435,8 @@ public class ChooserActivity extends ResolverActivity { .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType()) .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost)); - if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { - final IntentFilter filter = getTargetIntentFilter(); - Bundle extras = new Bundle(); - extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter); - AppPredictionManager appPredictionManager = - getSystemService(AppPredictionManager.class); - mAppPredictor = appPredictionManager.createAppPredictionSession( - new AppPredictionContext.Builder(this) - .setPredictedTargetCount(APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT) - .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE) - .setExtras(extras) - .build()); + AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); + if (appPredictor != null) { mAppPredictorCallback = resultList -> { if (isFinishing() || isDestroyed()) { return; @@ -467,8 +459,10 @@ public class ChooserActivity extends ResolverActivity { appTarget.getPackageName(), appTarget.getClassName()))); } sendShareShortcutInfoList(shareShortcutInfos, driList); + sendShortcutManagerShareTargetResultCompleted(); }; - mAppPredictor.registerPredictionUpdates(this.getMainExecutor(), mAppPredictorCallback); + appPredictor + .registerPredictionUpdates(this.getMainExecutor(), mAppPredictorCallback); } mChooserRowLayer = getResources().getDrawable(R.drawable.chooser_row_layer_list, null); @@ -872,7 +866,7 @@ public class ChooserActivity extends ResolverActivity { mChooserHandler.removeMessages(LIST_VIEW_UPDATE_MESSAGE); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT); - if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { + if (mAppPredictor != null) { mAppPredictor.unregisterPredictionUpdates(mAppPredictorCallback); mAppPredictor.destroy(); } @@ -1205,10 +1199,12 @@ public class ChooserActivity extends ResolverActivity { } private void queryDirectShareTargets(ChooserListAdapter adapter) { - if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { - mAppPredictor.requestPredictionUpdate(); + AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); + if (appPredictor != null) { + appPredictor.requestPredictionUpdate(); return; } + // Default to just querying ShortcutManager if AppPredictor not present. final IntentFilter filter = getTargetIntentFilter(); if (filter == null) { return; @@ -1248,12 +1244,16 @@ public class ChooserActivity extends ResolverActivity { } if (resultMessageSent) { - final Message msg = Message.obtain(); - msg.what = SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED; - mChooserHandler.sendMessage(msg); + sendShortcutManagerShareTargetResultCompleted(); } } + private void sendShortcutManagerShareTargetResultCompleted() { + final Message msg = Message.obtain(); + msg.what = SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED; + mChooserHandler.sendMessage(msg); + } + private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut) { ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo(); Bundle extras = new Bundle(); @@ -1309,9 +1309,7 @@ public class ChooserActivity extends ResolverActivity { void updateModelAndChooserCounts(TargetInfo info) { if (info != null) { - if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { - sendClickToAppPredictor(info); - } + sendClickToAppPredictor(info); final ResolveInfo ri = info.getResolveInfo(); Intent targetIntent = getTargetIntent(); if (ri != null && ri.activityInfo != null && targetIntent != null) { @@ -1332,6 +1330,10 @@ public class ChooserActivity extends ResolverActivity { } private void sendClickToAppPredictor(TargetInfo targetInfo) { + AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); + if (appPredictor == null) { + return; + } if (!(targetInfo instanceof ChooserTargetInfo)) { return; } @@ -1345,15 +1347,44 @@ public class ChooserActivity extends ResolverActivity { if (shortcutId == null) { return; } - mAppPredictor.notifyAppTargetEvent( + appPredictor.notifyAppTargetEvent( new AppTargetEvent.Builder( - new AppTarget.Builder(new AppTargetId(shortcutId)) - .setTarget(componentName.getPackageName(), getUser()) + // TODO(b/124404997) Send full shortcut info, not just Id with AppTargetId. + new AppTarget.Builder(new AppTargetId(shortcutId), + componentName.getPackageName(), getUser()) .setClassName(componentName.getClassName()) .build(), - AppTargetEvent.ACTION_LAUNCH - ).setLaunchLocation(LAUNCH_LOCATON_DIRECT_SHARE) - .build()); + AppTargetEvent.ACTION_LAUNCH) + .setLaunchLocation(LAUNCH_LOCATON_DIRECT_SHARE) + .build()); + } + + @Nullable + private AppPredictor getAppPredictor() { + if (mAppPredictor == null + && getPackageManager().getAppPredictionServicePackageName() != null) { + final IntentFilter filter = getTargetIntentFilter(); + Bundle extras = new Bundle(); + extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter); + AppPredictionContext appPredictionContext = new AppPredictionContext.Builder(this) + .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE) + .setPredictedTargetCount(APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT) + .setExtras(extras) + .build(); + AppPredictionManager appPredictionManager + = getSystemService(AppPredictionManager.class); + mAppPredictor = appPredictionManager.createAppPredictionSession(appPredictionContext); + } + return mAppPredictor; + } + + /** + * This will return an app predictor if it is enabled for direct share sorting + * and if one exists. Otherwise, it returns null. + */ + @Nullable + private AppPredictor getAppPredictorForDirectShareIfEnabled() { + return USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS ? getAppPredictor() : null; } void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) { @@ -1516,6 +1547,29 @@ public class ChooserActivity extends ResolverActivity { float getModifiedScore(); ChooserTarget getChooserTarget(); + + /** + * Do not label as 'equals', since this doesn't quite work + * as intended with java 8. + */ + default boolean isSimilar(ChooserTargetInfo other) { + if (other == null) return false; + + ChooserTarget ct1 = getChooserTarget(); + ChooserTarget ct2 = other.getChooserTarget(); + + // If either is null, there is not enough info to make an informed decision + // about equality, so just exit + if (ct1 == null || ct2 == null) return false; + + if (ct1.getComponentName().equals(ct2.getComponentName()) + && TextUtils.equals(getDisplayLabel(), other.getDisplayLabel()) + && TextUtils.equals(getExtendedInfo(), other.getExtendedInfo())) { + return true; + } + + return false; + } } /** @@ -1594,6 +1648,7 @@ public class ChooserActivity extends ResolverActivity { private final DisplayResolveInfo mSourceInfo; private final ResolveInfo mBackupResolveInfo; private final ChooserTarget mChooserTarget; + private final String mDisplayLabel; private Drawable mBadgeIcon = null; private CharSequence mBadgeContentDescription; private Drawable mDisplayIcon; @@ -1631,6 +1686,8 @@ public class ChooserActivity extends ResolverActivity { mFillInIntent = null; mFillInFlags = 0; + + mDisplayLabel = sanitizeDisplayLabel(chooserTarget.getTitle()); } private SelectableTargetInfo(SelectableTargetInfo other, Intent fillInIntent, int flags) { @@ -1643,6 +1700,14 @@ public class ChooserActivity extends ResolverActivity { mFillInIntent = fillInIntent; mFillInFlags = flags; mModifiedScore = other.mModifiedScore; + + mDisplayLabel = sanitizeDisplayLabel(mChooserTarget.getTitle()); + } + + private String sanitizeDisplayLabel(CharSequence label) { + SpannableStringBuilder sb = new SpannableStringBuilder(label); + sb.clearSpans(); + return sb.toString(); } public boolean isSuspended() { @@ -1781,7 +1846,7 @@ public class ChooserActivity extends ResolverActivity { @Override public CharSequence getDisplayLabel() { - return mChooserTarget.getTitle(); + return mDisplayLabel; } @Override @@ -2014,7 +2079,8 @@ public class ChooserActivity extends ResolverActivity { } } - if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) { + if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS + || USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { if (DEBUG) { Log.d(TAG, "querying direct share targets from ShortcutManager"); } @@ -2198,8 +2264,6 @@ public class ChooserActivity extends ResolverActivity { final float baseScore = getBaseScore(origTarget, isShortcutResult); Collections.sort(targets, mBaseTargetComparator); - - float lastScore = 0; boolean shouldNotify = false; for (int i = 0, N = Math.min(targets.size(), MAX_TARGETS_PER_SERVICE); i < N; i++) { @@ -2242,7 +2306,7 @@ public class ChooserActivity extends ResolverActivity { return CALLER_TARGET_SCORE_BOOST; } - if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { + if (getAppPredictorForDirectShareIfEnabled() != null) { return SHORTCUT_TARGET_SCORE_BOOST; } @@ -2274,8 +2338,15 @@ public class ChooserActivity extends ResolverActivity { return false; } - final float newScore = chooserTargetInfo.getModifiedScore(); + // Check for duplicates and abort if found + for (ChooserTargetInfo otherTargetInfo : mServiceTargets) { + if (chooserTargetInfo.isSimilar(otherTargetInfo)) { + return false; + } + } + int currentSize = mServiceTargets.size(); + final float newScore = chooserTargetInfo.getModifiedScore(); for (int i = 0; i < Math.min(currentSize, MAX_SERVICE_TARGETS); i++) { final ChooserTargetInfo serviceTarget = mServiceTargets.get(i); if (serviceTarget == null) { @@ -2362,15 +2433,11 @@ public class ChooserActivity extends ResolverActivity { * @return true if the view width has changed */ public boolean calculateChooserTargetWidth(int width) { - int targetMinWidth = getResources().getDimensionPixelSize( - R.dimen.chooser_target_width); - if (width == 0) { return false; } - int targetWidth = width / getMaxTargetsPerRow(); - int newWidth = Math.max(targetWidth, targetMinWidth); + int newWidth = width / getMaxTargetsPerRow(); if (newWidth != mChooserTargetWidth) { mChooserTargetWidth = newWidth; return true; @@ -2591,12 +2658,24 @@ public class ChooserActivity extends ResolverActivity { } } + /** + * Need to merge CALLER + ranked STANDARD into a single row. All other types + * are placed into their own row as determined by their target type, and dividers + * are added in the list to separate each type. + */ + int getRowType(int rowPosition) { + int positionType = mChooserListAdapter.getPositionTargetType(rowPosition); + if (positionType == ChooserListAdapter.TARGET_CALLER) { + return ChooserListAdapter.TARGET_STANDARD; + } + + return positionType; + } + void bindViewHolder(int rowPosition, RowViewHolder holder) { final int start = getFirstRowPosition(rowPosition); - final int startType = mChooserListAdapter.getPositionTargetType(start); - - final int lastStartType = mChooserListAdapter.getPositionTargetType( - getFirstRowPosition(rowPosition - 1)); + final int startType = getRowType(start); + final int lastStartType = getRowType(getFirstRowPosition(rowPosition - 1)); final ViewGroup row = holder.getViewGroup(); @@ -2608,7 +2687,7 @@ public class ChooserActivity extends ResolverActivity { int columnCount = holder.getColumnCount(); int end = start + columnCount - 1; - while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) { + while (getRowType(end) != startType && end >= start) { end--; } @@ -2660,14 +2739,15 @@ public class ChooserActivity extends ResolverActivity { return row * getMaxTargetsPerRow(); } - final int callerCount = mChooserListAdapter.getCallerTargetCount(); - final int callerRows = (int) Math.ceil((float) callerCount / getMaxTargetsPerRow()); - if (row < callerRows + serviceRows) { + final int callerAndRankedCount = mChooserListAdapter.getCallerTargetCount() + + mChooserListAdapter.getRankedTargetCount(); + final int callerAndRankedRows = getCallerAndRankedTargetRowCount(); + if (row < callerAndRankedRows + serviceRows) { return serviceCount + (row - serviceRows) * getMaxTargetsPerRow(); } - return callerCount + serviceCount - + (row - callerRows - serviceRows) * getMaxTargetsPerRow(); + return callerAndRankedCount + serviceCount + + (row - callerAndRankedRows - serviceRows) * getMaxTargetsPerRow(); } public void handleScroll(View v, int y, int oldy) { diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl index 69d9ccc9825b..b5f2147784c0 100644 --- a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl +++ b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl @@ -31,4 +31,5 @@ interface IMultiClientInputMethodPrivilegedOperations { in IMultiClientInputMethodSession multiClientSession, in InputChannel writeChannel); void reportImeWindowTarget(int clientId, int targetWindowHandle, in IBinder imeWindowToken); boolean isUidAllowedOnDisplay(int displayId, int uid); + void setActive(int clientId, boolean active); } diff --git a/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java index 922011707c4a..1cf68872e2cf 100644 --- a/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java @@ -212,4 +212,21 @@ public class MultiClientInputMethodPrivilegedOperations { } } + /** + * Calls {@link IMultiClientInputMethodPrivilegedOperations#setActive(int, boolean)}. + * @param clientId client ID to be set active/inactive + * @param active {@code true} set set active. + */ + @AnyThread + public void setActive(int clientId, boolean active) { + final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); + if (ops == null) { + return; + } + try { + ops.setActive(clientId, active); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index fd75f4fa4567..21f8d87e7e8c 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -45,6 +45,7 @@ import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.media.session.MediaController; @@ -115,6 +116,7 @@ import com.android.internal.widget.SwipeDismissLayout; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.List; /** * Android-specific Window. @@ -3926,4 +3928,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public WindowInsetsController getInsetsController() { return mDecor.getWindowInsetsController(); } + + @Override + public void setSystemGestureExclusionRects(@NonNull List<Rect> rects) { + getViewRootImpl().setRootSystemGestureExclusionRects(rects); + } + + @Override + @NonNull + public List<Rect> getSystemGestureExclusionRects() { + return getViewRootImpl().getRootSystemGestureExclusionRects(); + } } diff --git a/core/java/com/android/internal/util/TrafficStatsConstants.java b/core/java/com/android/internal/util/TrafficStatsConstants.java new file mode 100644 index 000000000000..2806ae2f9298 --- /dev/null +++ b/core/java/com/android/internal/util/TrafficStatsConstants.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +/** + * Constants for traffic stats. + * @hide + */ +public class TrafficStatsConstants { + // These tags are used by the network stack to do traffic for its own purposes. Traffic + // tagged with these will be counted toward the network stack and must stay inside the + // range defined by + // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_START} and + // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_END}. + public static final int TAG_SYSTEM_DHCP = 0xFFFFFE01; + public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFE02; + public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFE03; + + public static final int TAG_SYSTEM_NTP = 0xFFFFFF41; + public static final int TAG_SYSTEM_GPS = 0xFFFFFF44; + public static final int TAG_SYSTEM_PAC = 0xFFFFFF45; + + // These tags are used by the network stack to do traffic on behalf of apps. Traffic + // tagged with these will be counted toward the app on behalf of which the network + // stack is doing this traffic. These values must stay inside the range defined by + // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_START} and + // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_END}. + public static final int TAG_SYSTEM_PROBE = 0xFFFFFF81; +} diff --git a/core/jni/android_nio_utils.cpp b/core/jni/android_nio_utils.cpp index 19a1c7212fae..1e6d49e49b72 100644 --- a/core/jni/android_nio_utils.cpp +++ b/core/jni/android_nio_utils.cpp @@ -18,42 +18,29 @@ #include "core_jni_helpers.h" -void* android::nio_getPointer(JNIEnv *_env, jobject buffer, jarray *array) { - assert(array); +namespace android { - jint position; - jint limit; - jint elementSizeShift; - jlong pointer = jniGetNioBufferFields(_env, buffer, &position, &limit, &elementSizeShift); +AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit) + : fEnv(env), fCommit(commit) { + jlong pointer = jniGetNioBufferPointer(fEnv, nioBuffer); if (pointer != 0L) { - pointer += position << elementSizeShift; - return reinterpret_cast<void*>(pointer); + // Buffer is backed by a direct buffer. + fArray = nullptr; + fElements = nullptr; + fPointer = reinterpret_cast<void*>(pointer); + } else { + // Buffer is backed by a managed array. + jint byteOffset = jniGetNioBufferBaseArrayOffset(fEnv, nioBuffer); + fArray = jniGetNioBufferBaseArray(fEnv, nioBuffer); + fElements = fEnv->GetPrimitiveArrayCritical(fArray, /* isCopy= */ nullptr); + fPointer = reinterpret_cast<void*>(reinterpret_cast<char*>(fElements) + byteOffset); } - - jint offset = jniGetNioBufferBaseArrayOffset(_env, buffer); - *array = jniGetNioBufferBaseArray(_env, buffer); - void * data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0); - return reinterpret_cast<void*>(reinterpret_cast<char*>(data) + offset); -} - - -void android::nio_releasePointer(JNIEnv *_env, jarray array, void *data, - jboolean commit) { - _env->ReleasePrimitiveArrayCritical(array, data, - commit ? 0 : JNI_ABORT); } -/////////////////////////////////////////////////////////////////////////////// - -android::AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, - jboolean commit) { - fEnv = env; - fCommit = commit; - fPointer = android::nio_getPointer(env, nioBuffer, &fArray); -} - -android::AutoBufferPointer::~AutoBufferPointer() { - if (NULL != fArray) { - android::nio_releasePointer(fEnv, fArray, fPointer, fCommit); +AutoBufferPointer::~AutoBufferPointer() { + if (nullptr != fArray) { + fEnv->ReleasePrimitiveArrayCritical(fArray, fElements, fCommit ? 0 : JNI_ABORT); } } + +} // namespace android diff --git a/core/jni/android_nio_utils.h b/core/jni/android_nio_utils.h index c634cb917719..aa75dd0c7e32 100644 --- a/core/jni/android_nio_utils.h +++ b/core/jni/android_nio_utils.h @@ -20,52 +20,60 @@ #include <android_runtime/AndroidRuntime.h> namespace android { - -/** - * Given an nio.Buffer, return a pointer to it, beginning at its current - * position. The returned pointer is only valid for the current JNI stack-frame. - * For performance, it does not create any global references, so the getPointer - * (and releasePointer if array is returned non-null) must be done in the - * same JNI stack-frame. - * - * @param env The current JNI env - * @param buffer The nio.Buffer object - * @param array REQUIRED. Output. If on return it is set to non-null, then - * nio_releasePointer must be called with the array - * and the returned pointer when the caller is through with it. - * If on return it is set to null, do not call - * nio_releasePointer. - * @return The pointer to the memory in the buffer object - */ -void* nio_getPointer(JNIEnv *env, jobject buffer, jarray *array); /** - * Call this if android_nio_getPointer returned non-null in its array parameter. - * Pass that array and the returned pointer when you are done accessing the - * pointer. If called (i.e. array is non-null), it must be called in the same - * JNI stack-frame as getPointer + * Class providing scoped access to the memory backing a java.nio.Buffer instance. + * + * Instances of this class should only be allocated on the stack as heap allocation is not + * supported. * - * @param env The current JNI env - * @param buffer The array returned from android_nio_getPointer (!= null) - * @param pointer The pointer returned by android_nio_getPointer - * @param commit JNI_FALSE if the pointer was just read, and JNI_TRUE if - * the pointer was written to. + * Instances of this class do not create any global references for performance reasons. */ -void nio_releasePointer(JNIEnv *env, jarray array, void *pointer, - jboolean commit); - -class AutoBufferPointer { +class AutoBufferPointer final { public: + /** Constructor for an AutoBufferPointer instance. + * + * @param env The current JNI env + * @param nioBuffer Instance of a java.nio.Buffer whose memory will be accessed. + * @param commit JNI_TRUE if the underlying memory will be updated and should be + * copied back to the managed heap. JNI_FALSE if the data will + * not be modified or the modifications may be discarded. + * + * The commit parameter is only applicable if the buffer is backed by a managed heap + * array and the runtime had to provide a copy of the data rather than the original data. + */ AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit); + + /** Destructor for an AutoBufferPointer instance. + * + * Releases critical managed heap array pointer if acquired. + */ ~AutoBufferPointer(); + /** + * Returns a pointer to the current position of the buffer provided to the constructor. This + * pointer is only valid whilst the AutoBufferPointer instance remains in scope. + */ void* pointer() const { return fPointer; } private: - JNIEnv* fEnv; - void* fPointer; - jarray fArray; - jboolean fCommit; + JNIEnv* const fEnv; + void* fPointer; // Pointer to current buffer position when constructed. + void* fElements; // Pointer to array element 0 (null if buffer is direct, may be + // within fArray or point to a copy of the array). + jarray fArray; // Pointer to array on managed heap. + const jboolean fCommit; // Flag to commit data to source (when fElements is a copy of fArray). + + // Unsupported constructors and operators. + AutoBufferPointer() = delete; + AutoBufferPointer(AutoBufferPointer&) = delete; + AutoBufferPointer& operator=(AutoBufferPointer&) = delete; + static void* operator new(std::size_t); + static void* operator new[](std::size_t); + static void* operator new(std::size_t, void*); + static void* operator new[](std::size_t, void*); + static void operator delete(void*, std::size_t); + static void operator delete[](void*, std::size_t); }; } /* namespace android */ diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index f599913ce497..e2214d160fdc 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -73,6 +73,7 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <bionic_malloc.h> #include <cutils/ashmem.h> #include <cutils/fs.h> #include <cutils/multiuser.h> @@ -499,12 +500,9 @@ static void EnableDebugger() { } } -// The debug malloc library needs to know whether it's the zygote or a child. -extern "C" int gMallocLeakZygoteChild; - static void PreApplicationInit() { // The child process sets this to indicate it's not the zygote. - gMallocLeakZygoteChild = 1; + android_mallopt(M_SET_ZYGOTE_CHILD, nullptr, 0); // Set the jemalloc decay time to 1. mallopt(M_DECAY_TIME, 1); diff --git a/core/proto/android/os/enums.proto b/core/proto/android/os/enums.proto index c35706501c3d..566861b6e836 100644 --- a/core/proto/android/os/enums.proto +++ b/core/proto/android/os/enums.proto @@ -78,6 +78,41 @@ enum TemperatureTypeEnum { TEMPERATURE_TYPE_NPU = 9; } +// Device throttling severity +// These constants are defined in hardware/interfaces/thermal/2.0/types.hal. +// Any change to the types in the thermal hal should be made here as well. +enum ThrottlingSeverityEnum { + // Not under throttling. + NONE = 0; + // Light throttling where UX is not impacted. + LIGHT = 1; + // Moderate throttling where UX is not largely impacted. + MODERATE = 2; + // Severe throttling where UX is largely impacted. + // Similar to 1.0 throttlingThreshold. + SEVERE = 3; + // Platform has done everything to reduce power. + CRITICAL = 4; + // Key components in platform are shutting down due to thermal condition. + // Device functionalities will be limited. + EMERGENCY = 5; + // Need shutdown immediately. + SHUTDOWN = 6; +}; + +// Device cooling device types. +// These constants are defined in hardware/interfaces/thermal/2.0/types.hal. +// Any change to the types in the thermal hal should be made here as well. +enum CoolingTypeEnum { + FAN = 0; + BATTERY = 1; + CPU = 2; + GPU = 3; + MODEM = 4; + NPU = 5; + COMPONENT = 6; +}; + // Wakelock types, primarily used by android/os/PowerManager.java. enum WakeLockLevelEnum { // NOTE: Wake lock levels were previously defined as a bit field, except diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index cc3b3a4c7ccb..8714bf2505bb 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -828,6 +828,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_sdcardRead" android:description="@string/permdesc_sdcardRead" + android:permissionFlags="softRestricted|immutablyRestricted" android:protectionLevel="dangerous" /> <!-- Allows an application to write to external storage. @@ -848,6 +849,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_sdcardWrite" android:description="@string/permdesc_sdcardWrite" + android:permissionFlags="softRestricted|immutablyRestricted" android:protectionLevel="dangerous" /> <!-- Allows an application to access any geographic locations persisted in the diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml index 371bcfef7ec9..9fa7c5498ea6 100644 --- a/core/res/res/anim/activity_close_enter.xml +++ b/core/res/res/anim/activity_close_enter.xml @@ -19,15 +19,16 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> - <translate - android:fromYDelta="-2%" - android:toYDelta="0" - android:interpolator="@interpolator/fast_out_slow_in" - android:duration="425"/> - <alpha - android:fromAlpha="0.9" - android:toAlpha="1.0" - android:interpolator="@interpolator/activity_close_dim" - android:startOffset="0" - android:duration="425"/> + <scale + android:fromXScale="1.1" + android:toXScale="1" + android:fromYScale="1.1" + android:toYScale="1" + android:pivotX="50%" + android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="400"/> </set>
\ No newline at end of file diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml index d87f1003def5..1599ae8cb19f 100644 --- a/core/res/res/anim/activity_close_exit.xml +++ b/core/res/res/anim/activity_close_exit.xml @@ -20,25 +20,25 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top"> - <translate - android:fromYDelta="0" - android:toYDelta="4.1%" - android:interpolator="@interpolator/fast_out_slow_in" - android:duration="425"/> - <cliprect - android:fromLeft="0%" - android:fromTop="0%" - android:fromRight="100%" - android:fromBottom="100%" - android:toLeft="0%" - android:toTop="95.9%" - android:toRight="100%" - android:toBottom="100%" - android:interpolator="@interpolator/fast_out_extra_slow_in" - android:duration="425"/> <alpha - android:fromAlpha="1.0" - android:toAlpha="1.0" - android:interpolator="@interpolator/fast_out_linear_in" - android:duration="425"/> + android:fromAlpha="1" + android:toAlpha="0.0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/linear" + android:startOffset="33" + android:duration="50"/> + <scale + android:fromXScale="1" + android:toXScale="0.9" + android:fromYScale="1" + android:toYScale="0.9" + android:pivotX="50%" + android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="400"/> </set> diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml index cb0307026e2f..38d3e8ed06ce 100644 --- a/core/res/res/anim/activity_open_enter.xml +++ b/core/res/res/anim/activity_open_enter.xml @@ -18,20 +18,25 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> - <translate - android:fromYDelta="4.1%" - android:toYDelta="0" - android:interpolator="@interpolator/fast_out_slow_in" - android:duration="425"/> - <cliprect - android:fromLeft="0%" - android:fromTop="95.9%" - android:fromRight="100%" - android:fromBottom="100%" - android:toLeft="0%" - android:toTop="0%" - android:toRight="100%" - android:toBottom="100%" + <alpha + android:fromAlpha="0" + android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/linear" + android:startOffset="50" + android:duration="50"/> + <scale + android:fromXScale="0.85" + android:toXScale="1" + android:fromYScale="0.85" + android:toYScale="1" + android:pivotX="50%" + android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" android:interpolator="@interpolator/fast_out_extra_slow_in" - android:duration="425"/> + android:duration="400"/> </set> diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml index d52b150391fb..3865d2149f42 100644 --- a/core/res/res/anim/activity_open_exit.xml +++ b/core/res/res/anim/activity_open_exit.xml @@ -18,14 +18,28 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> - <translate - android:fromYDelta="0" - android:toYDelta="-2%" - android:interpolator="@interpolator/fast_out_slow_in" - android:duration="425"/> + + <!-- Fade out, over a black surface, which simulates a black scrim --> <alpha - android:fromAlpha="1.0" - android:toAlpha="0.9" + android:fromAlpha="1" + android:toAlpha="0.4" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" android:interpolator="@interpolator/linear" - android:duration="117"/> + android:startOffset="83" + android:duration="167"/> + + <scale + android:fromXScale="1" + android:toXScale="1.05" + android:fromYScale="1" + android:toYScale="1.05" + android:pivotX="50%" + android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="400"/> </set>
\ No newline at end of file diff --git a/core/res/res/drawable/chooser_row_layer_list.xml b/core/res/res/drawable/chooser_row_layer_list.xml index 0fb26e13fe57..080081521ddf 100644 --- a/core/res/res/drawable/chooser_row_layer_list.xml +++ b/core/res/res/drawable/chooser_row_layer_list.xml @@ -17,7 +17,7 @@ */ --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:bottom="-2dp" android:left="-2dp" android:right="-2dp"> + <item android:bottom="-5dp" android:left="-5dp" android:right="-5dp"> <shape android:shape="rectangle"> <stroke android:width="1dp" android:color="@color/chooser_row_divider"/> </shape> diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml index 3c9ffdb8e439..f3ca0af767b1 100644 --- a/core/res/res/layout/chooser_grid_preview_text.xml +++ b/core/res/res/layout/chooser_grid_preview_text.xml @@ -24,9 +24,9 @@ android:layout_height="wrap_content" android:orientation="vertical" android:paddingBottom="@dimen/chooser_view_spacing" - android:background="?attr/colorBackgroundFloating"> + android:background="?android:attr/colorBackgroundFloating"> - <LinearLayout + <RelativeLayout android:layout_width="@dimen/chooser_preview_width" android:layout_height="wrap_content" android:layout_gravity="center" @@ -35,28 +35,51 @@ android:paddingRight="@dimen/chooser_edge_margin_normal" android:layout_marginBottom="@dimen/chooser_view_spacing" android:id="@+id/content_preview_text_layout"> + <TextView android:id="@+id/content_preview_text" - android:layout_width="0dp" - android:layout_weight="1" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" + android:layout_alignParentStart="true" + android:layout_toStartOf="@id/copy_button" + android:layout_centerVertical="true" android:ellipsize="end" - android:gravity="start|top" - android:paddingRight="@dimen/chooser_view_spacing" + android:textColor="?android:attr/textColorPrimary" android:maxLines="2"/> - <ImageButton + + <LinearLayout android:id="@+id/copy_button" - android:layout_width="48dp" - android:layout_height="48dp" - android:padding="12dp" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_alignParentEnd="true" + android:layout_marginStart="@dimen/chooser_view_spacing" android:gravity="center" - android:layout_gravity="center_vertical" - android:src="@drawable/ic_content_copy_gm2" + android:minWidth="48dp" + android:minHeight="48dp" android:clickable="true" - android:contentDescription="@string/copy" - android:background="?attr/selectableItemBackgroundBorderless"/> - </LinearLayout> + android:background="?android:attr/selectableItemBackgroundBorderless"> + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:gravity="top|center_horizontal" + android:src="@drawable/ic_content_copy_gm2" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:gravity="center_horizontal" + android:text="@string/copy" + android:textColor="?android:textColorSecondary" + android:textSize="12sp" + android:maxWidth="72dp" + android:maxLines="2" + android:ellipsize="end" /> + </LinearLayout> + </RelativeLayout> <!-- Required sub-layout so we can get the nice rounded corners--> <!-- around this section --> @@ -89,7 +112,8 @@ android:layout_gravity="center_vertical" android:ellipsize="end" android:maxLines="2" - android:textSize="20sp"/> + android:textSize="20sp" + android:textColor="?android:attr/textColorPrimary"/> </LinearLayout> </LinearLayout> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index ed8f2c196b72..ab9a298f3060 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2485,24 +2485,7 @@ <flag name="noExcludeDescendants" value="0x8" /> </attr> - <!-- Hints the Android System whether the view node associated with this View should be - use for content capture purposes. --> - <attr name="importantForContentCapture"> - <!-- Let the Android System use its heuristics to determine if the view is important for content capture. --> - <flag name="auto" value="0" /> - <!-- Hint the Android System that this view is important for content capture, - and its children (if any) will be traversed.. --> - <flag name="yes" value="0x1" /> - <!-- Hint the Android System that this view is *not* important for content capture, - but its children (if any) will be traversed.. --> - <flag name="no" value="0x2" /> - <!-- Hint the Android System that this view is important for content capture, - but its children (if any) will not be traversed. --> - <flag name="yesExcludeDescendants" value="0x4" /> - <!-- Hint the Android System that this view is *not* important for content capture, - and its children (if any) will not be traversed. --> - <flag name="noExcludeDescendants" value="0x8" /> - </attr> + <attr name="__removed6" /> <!-- Boolean that controls whether a view can take focus while in touch mode. If this is true for a view, that view can gain focus when clicked on, and can keep diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index bfa57e46984b..bbc784a10db3 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -328,6 +328,12 @@ would be granted. The weak grant depends on the permission. --> <flag name="softRestricted" value="0x8" /> + <!-- This permission is restricted immutably which means that its + restriction state may be specified only on the first install of + the app and will stay in this initial whitelist state until + the app is uninstalled. + --> + <flag name="immutablyRestricted" value="0x10" /> </attr> <!-- Specified the name of a group that this permission is associated @@ -1707,17 +1713,18 @@ See {@link android.media.AudioPlaybackCaptureConfiguration} for more detail. --> <attr name="allowAudioPlaybackCapture" format="boolean" /> - <!-- If {@code true} this app allows shared/external storage media to be - a sandboxed view that only contains files owned by the app. - <p> - Sandboxed apps can continue to discover and read media belonging to other - apps via {@code MediaStore}. + <!-- If {@code true} this app would like to run under the legacy storage + model. Note that this may not always be respected due to policy or + backwards compatibility reasons. + + <p>Apps not requesting legacy storage can continue to discover and + read media belonging to other apps via {@code MediaStore}. <p> The default value is: - - {@code true} for apps with targetSdkVersion >= 29 (Q). - - {@code false} for apps with targetSdkVersion < 29. + - {@code false} for apps with targetSdkVersion >= 29 (Q). + - {@code true} for apps with targetSdkVersion < 29. --> - <attr name="allowExternalStorageSandbox" format="boolean" /> + <attr name="requestLegacyExternalStorage" format="boolean" /> </declare-styleable> <!-- The <code>permission</code> tag declares a security permission that can be used to control access from other packages to specific components or diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index edcc0572cb2e..4d6fda980755 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -745,10 +745,6 @@ <!-- XXXXXX END OF RESOURCES USING WRONG NAMING CONVENTION --> - <!-- If this is true, notification effects will be played by the notification server. - When false, car notification effects will be handled elsewhere. --> - <bool name="config_enableServerNotificationEffectsForAutomotive">false</bool> - <!-- If this is true, the screen will come on when you unplug usb/power/whatever. --> <bool name="config_unplugTurnsOnScreen">false</bool> @@ -3487,6 +3483,12 @@ <item>com.android.messaging</item> </string-array> + <!-- An array of packages that can make sound on the ringer stream in priority-only DND + mode --> + <string-array translatable="false" name="config_priorityOnlyDndExemptPackages"> + <item>com.android.dialer</item> + </string-array> + <!-- An array of packages which can listen for notifications on low ram devices. --> <string-array translatable="false" name="config_allowedManagedServicesOnLowRamDevices" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 9a8c754cf197..a7af144ab9ed 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2922,7 +2922,7 @@ <public name="settingsSliceUri" /> <public name="shell" /> <public name="interactiveUiTimeout" /> - <public name="importantForContentCapture" /> + <public name="__removed6" /> <public name="supportsMultipleDisplays" /> <public name="useAppZygote" /> <public name="__removed1" /> @@ -2942,7 +2942,7 @@ <public name="allowClearUserDataOnFailedRestore"/> <public name="allowAudioPlaybackCapture"/> <public name="secureElementName" /> - <public name="allowExternalStorageSandbox"/> + <public name="requestLegacyExternalStorage"/> <public name="ensuringStatusBarContrastWhenTransparent" /> <public name="ensuringNavigationBarContrastWhenTransparent" /> <public name="identifier" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index f8a2ac9f3e18..4bd2cc758cbb 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1521,10 +1521,10 @@ <string name="face_acquired_too_high">Move phone higher.</string> <!-- Message shown during face acquisition when the user is too low relatively to sensor [CHAR LIMIT=50] --> <string name="face_acquired_too_low">Move phone lower.</string> - <!-- Message shown during face acquisition when the user is too right relatively to sensor [CHAR LIMIT=50] --> - <string name="face_acquired_too_right">Move phone to the right.</string> - <!-- Message shown during face acquisition when the user is too left relatively to sensor [CHAR LIMIT=50] --> - <string name="face_acquired_too_left">Move phone to the left.</string> + <!-- Message shown during face acquisition when only the right part of the user's face was detected [CHAR LIMIT=50] --> + <string name="face_acquired_too_right">Move phone to the left.</string> + <!-- Message shown during face acquisition when only the left part of the user's face was detected [CHAR LIMIT=50] --> + <string name="face_acquired_too_left">Move phone to the right.</string> <!-- Message shown during face acquisition when the user is not front facing the sensor [CHAR LIMIT=50] --> <string name="face_acquired_poor_gaze">Look at the screen with your eyes open.</string> <!-- Message shown during face acquisition when the user is not detected [CHAR LIMIT=50] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index da2f89083fef..924b036813a6 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1910,7 +1910,6 @@ <java-symbol type="array" name="config_testLocationProviders" /> <java-symbol type="array" name="config_defaultNotificationVibePattern" /> <java-symbol type="array" name="config_notificationFallbackVibePattern" /> - <java-symbol type="bool" name="config_enableServerNotificationEffectsForAutomotive" /> <java-symbol type="bool" name="config_useAttentionLight" /> <java-symbol type="bool" name="config_adaptive_sleep_available" /> <java-symbol type="bool" name="config_animateScreenLights" /> @@ -3189,6 +3188,7 @@ <java-symbol type="array" name="config_convert_to_emergency_number_map" /> <java-symbol type="array" name="config_nonBlockableNotificationPackages" /> + <java-symbol type="array" name="config_priorityOnlyDndExemptPackages" /> <java-symbol type="array" name="config_allowedManagedServicesOnLowRamDevices" /> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 9cb34895dea4..711eaa7edc2a 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -38,6 +38,7 @@ import android.content.res.Configuration; import android.os.IBinder; import android.util.MergedConfiguration; import android.view.Display; +import android.view.View; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; @@ -153,6 +154,34 @@ public class ActivityThreadTest { } @Test + public void testHandleActivity_assetsChanged() { + final TestActivity activity = mActivityTestRule.launchActivity(new Intent()); + + final IBinder[] token = new IBinder[1]; + final View[] decorView = new View[1]; + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + final ActivityThread activityThread = activity.getActivityThread(); + + token[0] = activity.getActivityToken(); + decorView[0] = activity.getWindow().getDecorView(); + + // Relaunches all activities + activityThread.handleApplicationInfoChanged(activity.getApplicationInfo()); + }); + + final View[] newDecorView = new View[1]; + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + final ActivityThread activityThread = activity.getActivityThread(); + + final Activity newActivity = activityThread.getActivity(token[0]); + newDecorView[0] = activity.getWindow().getDecorView(); + }); + + assertEquals("Window must be preserved", decorView[0], newDecorView[0]); + } + + @Test public void testHandleActivityConfigurationChanged_DropStaleConfigurations() { final TestActivity activity = mActivityTestRule.launchActivity(new Intent()); diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java index d2a1dd9a7b5c..2280cf1cccfa 100644 --- a/core/tests/coretests/src/android/graphics/BitmapTest.java +++ b/core/tests/coretests/src/android/graphics/BitmapTest.java @@ -22,6 +22,10 @@ import androidx.test.filters.SmallTest; import junit.framework.TestCase; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + public class BitmapTest extends TestCase { @SmallTest @@ -262,4 +266,231 @@ public class BitmapTest extends TestCase { assertFalse(hardwareBitmap.isMutable()); assertEquals(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), hardwareBitmap.getColorSpace()); } + + @SmallTest + public void testCopyWithDirectByteBuffer() { + // Initialize Bitmap + final int width = 2; + final int height = 2; + final int bytesPerPixel = 2; + Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); + + // Copy bytes to direct buffer, buffer is padded by fixed amount (pad bytes) either side + // of bitmap. + final int pad = 1; + final byte padValue = 0x5a; + final int bytesPerElement = 1; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; + ByteBuffer directBuffer = ByteBuffer.allocateDirect(bufferSize); + + // Write padding + directBuffer.put(0, padValue); + directBuffer.put(directBuffer.limit() - 1, padValue); + + // Copy bitmap + directBuffer.position(pad); + bm1.copyPixelsToBuffer(directBuffer); + assertEquals(directBuffer.position(), + pad + width * height * bytesPerPixel / bytesPerElement); + + // Check padding + assertEquals(directBuffer.get(0), padValue); + assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue); + + // Create bitmap from direct buffer and check match. + directBuffer.position(pad); + Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm2.copyPixelsFromBuffer(directBuffer); + assertTrue(bm2.sameAs(bm1)); + } + + @SmallTest + public void testCopyWithDirectShortBuffer() { + // Initialize Bitmap + final int width = 2; + final int height = 2; + final int bytesPerPixel = 2; + Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); + + // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side + // of bitmap. + final int pad = 1; + final short padValue = 0x55aa; + final int bytesPerElement = 2; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; + ShortBuffer directBuffer = + ByteBuffer.allocateDirect(bufferSize * bytesPerElement).asShortBuffer(); + + // Write padding + directBuffer.put(0, padValue); + directBuffer.put(directBuffer.limit() - 1, padValue); + + // Copy bitmap + directBuffer.position(pad); + bm1.copyPixelsToBuffer(directBuffer); + assertEquals(directBuffer.position(), + pad + width * height * bytesPerPixel / bytesPerElement); + + // Check padding + assertEquals(directBuffer.get(0), padValue); + assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue); + + // Create bitmap from heap buffer and check match. + directBuffer.position(pad); + Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm2.copyPixelsFromBuffer(directBuffer); + assertTrue(bm2.sameAs(bm1)); + } + + @SmallTest + public void testCopyWithDirectIntBuffer() { + // Initialize Bitmap + final int width = 2; + final int height = 2; + final int bytesPerPixel = 2; + Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); + + // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side + // of bitmap. + final int pad = 1; + final int padValue = 0x55aa5a5a; + final int bytesPerElement = 4; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; + IntBuffer directBuffer = + ByteBuffer.allocateDirect(bufferSize * bytesPerElement).asIntBuffer(); + + // Write padding + directBuffer.put(0, padValue); + directBuffer.put(directBuffer.limit() - 1, padValue); + + // Copy bitmap + directBuffer.position(pad); + bm1.copyPixelsToBuffer(directBuffer); + assertEquals(directBuffer.position(), + pad + width * height * bytesPerPixel / bytesPerElement); + + // Check padding + assertEquals(directBuffer.get(0), padValue); + assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue); + + // Create bitmap from heap buffer and check match. + directBuffer.position(pad); + Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm2.copyPixelsFromBuffer(directBuffer); + assertTrue(bm2.sameAs(bm1)); + } + + @SmallTest + public void testCopyWithHeapByteBuffer() { + // Initialize Bitmap + final int width = 2; + final int height = 2; + final int bytesPerPixel = 2; + Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); + + // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side + // of bitmap. + final int pad = 1; + final byte padValue = 0x5a; + final int bytesPerElement = 1; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; + ByteBuffer heapBuffer = ByteBuffer.allocate(bufferSize); + + // Write padding + heapBuffer.put(0, padValue); + heapBuffer.put(heapBuffer.limit() - 1, padValue); + + // Copy bitmap + heapBuffer.position(pad); + bm1.copyPixelsToBuffer(heapBuffer); + assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement); + + // Check padding + assertEquals(heapBuffer.get(0), padValue); + assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue); + + // Create bitmap from heap buffer and check match. + heapBuffer.position(pad); + Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm2.copyPixelsFromBuffer(heapBuffer); + assertTrue(bm2.sameAs(bm1)); + } + + @SmallTest + public void testCopyWithHeapShortBuffer() { + // Initialize Bitmap + final int width = 2; + final int height = 2; + final int bytesPerPixel = 2; + Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); + + // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side + // of bitmap. + final int pad = 1; + final short padValue = 0x55aa; + final int bytesPerElement = 2; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; + ShortBuffer heapBuffer = ShortBuffer.allocate(bufferSize); + + // Write padding + heapBuffer.put(0, padValue); + heapBuffer.put(heapBuffer.limit() - 1, padValue); + + // Copy bitmap + heapBuffer.position(pad); + bm1.copyPixelsToBuffer(heapBuffer); + assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement); + + // Check padding + assertEquals(heapBuffer.get(0), padValue); + assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue); + + // Create bitmap from heap buffer and check match. + heapBuffer.position(pad); + Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm2.copyPixelsFromBuffer(heapBuffer); + assertTrue(bm2.sameAs(bm1)); + } + + @SmallTest + public void testCopyWithHeapIntBuffer() { + // Initialize Bitmap + final int width = 2; + final int height = 2; + final int bytesPerPixel = 2; + Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); + + // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side + // of bitmap. + final int pad = 1; + final int padValue = 0x55aa5a5a; + final int bytesPerElement = 4; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; + IntBuffer heapBuffer = IntBuffer.allocate(bufferSize); + + // Write padding + heapBuffer.put(0, padValue); + heapBuffer.put(heapBuffer.limit() - 1, padValue); + + // Copy bitmap + heapBuffer.position(pad); + bm1.copyPixelsToBuffer(heapBuffer); + assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement); + + // Check padding + assertEquals(heapBuffer.get(0), padValue); + assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue); + + // Create bitmap from heap buffer and check match. + heapBuffer.position(pad); + Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm2.copyPixelsFromBuffer(heapBuffer); + assertTrue(bm2.sameAs(bm1)); + } } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index fafd8333f236..0e94abc0dcac 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -709,7 +709,9 @@ public class SettingsBackupTest { Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS, Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS, - Settings.Secure.BIOMETRIC_DEBUG_ENABLED); + Settings.Secure.BIOMETRIC_DEBUG_ENABLED, + Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, + Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED); @Test public void systemSettingsBackedUpOrBlacklisted() { diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index ebbbdec7d376..bdd3038cfee5 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -39,11 +39,13 @@ import android.platform.test.annotations.Presubmit; import android.util.SparseArray; import android.view.SurfaceControl.Transaction; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; +import android.view.test.InsetsModeSession; -import androidx.test.filters.FlakyTest; import androidx.test.runner.AndroidJUnit4; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -62,7 +64,6 @@ import java.util.List; * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. */ @Presubmit -@FlakyTest(detail = "Promote once confirmed non-flaky") @RunWith(AndroidJUnit4.class) public class InsetsAnimationControlImplTest { @@ -72,15 +73,25 @@ public class InsetsAnimationControlImplTest { private SurfaceControl mTopLeash; private SurfaceControl mNavLeash; private InsetsState mInsetsState; + private static InsetsModeSession sInsetsModeSession; @Mock Transaction mMockTransaction; @Mock InsetsController mMockController; @Mock WindowInsetsAnimationControlListener mMockListener; @Mock SyncRtSurfaceTransactionApplier mMockTransactionApplier; + @BeforeClass + public static void setupOnce() { + sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL); + } + + @AfterClass + public static void tearDownOnce() throws Exception { + sInsetsModeSession.close(); + } + @Before public void setup() { - ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL; MockitoAnnotations.initMocks(this); mTopLeash = new SurfaceControl.Builder(mSession) .setName("testSurface") diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 4d8d3f68f875..1e558287e21b 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -43,7 +43,6 @@ import android.view.WindowManager.LayoutParams; import android.widget.TextView; import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; @@ -63,7 +62,6 @@ import java.util.concurrent.CountDownLatch; * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. */ @Presubmit -@FlakyTest(detail = "Promote once confirmed non-flaky") @RunWith(AndroidJUnit4.class) public class InsetsControllerTest { diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index a32fa778e736..971e143927b3 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -34,7 +34,6 @@ import android.view.WindowManager.LayoutParams; import android.widget.TextView; import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; @@ -53,7 +52,6 @@ import org.mockito.MockitoAnnotations; * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. */ @Presubmit -@FlakyTest(detail = "Promote once confirmed non-flaky") @RunWith(AndroidJUnit4.class) public class InsetsSourceConsumerTest { diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java index b55a9c600a61..533a58ef1dfb 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -24,7 +24,6 @@ import android.graphics.Insets; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; -import androidx.test.filters.FlakyTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; @@ -41,7 +40,6 @@ import org.junit.runner.RunWith; * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. */ @Presubmit -@FlakyTest(detail = "Promote once confirmed non-flaky") @RunWith(AndroidJUnit4.class) public class InsetsSourceTest { diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 8e167da84e08..a73269a81b11 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -40,7 +40,6 @@ import android.util.SparseIntArray; import android.view.WindowInsets.Type; import android.view.test.InsetsModeSession; -import androidx.test.filters.FlakyTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; @@ -56,7 +55,6 @@ import org.junit.runner.RunWith; * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. */ @Presubmit -@FlakyTest(detail = "Promote once confirmed non-flaky") @RunWith(AndroidJUnit4.class) public class InsetsStateTest { diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java index cd885e0b9bcf..7c255c9168cc 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java @@ -47,6 +47,6 @@ public class ContentCaptureManagerTest { @Test public void testRemoveUserData_invalid() { - assertThrows(NullPointerException.class, () -> mManager.removeUserData(null)); + assertThrows(NullPointerException.class, () -> mManager.removeData(null)); } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index 433991e86212..aeb8949c6976 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -362,6 +362,38 @@ public class TextClassifierTest { } @Test + public void testGenerateLinks_entityData() { + if (isTextClassifierDisabled()) return; + String text = "The number is +12122537077."; + Bundle extras = new Bundle(); + ExtrasUtils.putIsSerializedEntityDataEnabled(extras, true); + TextLinks.Request request = new TextLinks.Request.Builder(text).setExtras(extras).build(); + + TextLinks textLinks = mClassifier.generateLinks(request); + + Truth.assertThat(textLinks.getLinks()).hasSize(1); + TextLinks.TextLink textLink = textLinks.getLinks().iterator().next(); + List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras()); + Truth.assertThat(entities).hasSize(1); + Bundle entity = entities.get(0); + Truth.assertThat(ExtrasUtils.getEntityType(entity)).isEqualTo(TextClassifier.TYPE_PHONE); + } + + @Test + public void testGenerateLinks_entityData_disabled() { + if (isTextClassifierDisabled()) return; + String text = "The number is +12122537077."; + TextLinks.Request request = new TextLinks.Request.Builder(text).build(); + + TextLinks textLinks = mClassifier.generateLinks(request); + + Truth.assertThat(textLinks.getLinks()).hasSize(1); + TextLinks.TextLink textLink = textLinks.getLinks().iterator().next(); + List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras()); + Truth.assertThat(entities).isNull(); + } + + @Test public void testDetectLanguage() { if (isTextClassifierDisabled()) return; String text = "This is English text"; @@ -380,7 +412,7 @@ public class TextClassifierTest { } @Test - public void testSuggestConversationActions_textReplyOnly_maxThree() { + public void testSuggestConversationActions_textReplyOnly_maxOne() { if (isTextClassifierDisabled()) return; ConversationActions.Message message = new ConversationActions.Message.Builder( @@ -399,12 +431,11 @@ public class TextClassifierTest { .build(); ConversationActions conversationActions = mClassifier.suggestConversationActions(request); - assertTrue(conversationActions.getConversationActions().size() > 0); - for (ConversationAction conversationAction : - conversationActions.getConversationActions()) { - assertThat(conversationAction, - isConversationAction(ConversationAction.TYPE_TEXT_REPLY)); - } + Truth.assertThat(conversationActions.getConversationActions()).hasSize(1); + ConversationAction conversationAction = conversationActions.getConversationActions().get(0); + Truth.assertThat(conversationAction.getType()).isEqualTo( + ConversationAction.TYPE_TEXT_REPLY); + Truth.assertThat(conversationAction.getTextReply()).isNotNull(); } @Test @@ -493,6 +524,24 @@ public class TextClassifierTest { ExtrasUtils.getSerializedEntityData(conversationAction.getExtras())).isNotEmpty(); } + @Test + public void testSuggetsConversationActions_deduplicate() { + if (isTextClassifierDisabled()) return; + ConversationActions.Message message = + new ConversationActions.Message.Builder( + ConversationActions.Message.PERSON_USER_OTHERS) + .setText("a@android.com b@android.com") + .build(); + ConversationActions.Request request = + new ConversationActions.Request.Builder(Collections.singletonList(message)) + .setMaxSuggestions(3) + .build(); + + ConversationActions conversationActions = mClassifier.suggestConversationActions(request); + + Truth.assertThat(conversationActions.getConversationActions()).isEmpty(); + } + private boolean isTextClassifierDisabled() { return mClassifier == null || mClassifier == TextClassifier.NO_OP; } diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java index 1980a604fdd2..2c540e560f6b 100644 --- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java @@ -49,8 +49,6 @@ import org.mockito.MockitoAnnotations; public class TextClassifierEventTronLoggerTest { private static final String WIDGET_TYPE = "notification"; private static final String PACKAGE_NAME = "pkg"; - private static final long EVENT_TIME = System.currentTimeMillis(); - @Mock private MetricsLogger mMetricsLogger; @@ -68,13 +66,11 @@ public class TextClassifierEventTronLoggerTest { TextClassificationContext textClassificationContext = new TextClassificationContext.Builder(PACKAGE_NAME, WIDGET_TYPE) .build(); - TextClassifierEvent textClassifierEvent = - new TextClassifierEvent.Builder( - TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, + TextClassifierEvent.ConversationActionsEvent textClassifierEvent = + new TextClassifierEvent.ConversationActionsEvent.Builder( TextClassifierEvent.TYPE_SMART_ACTION) .setEntityTypes(ConversationAction.TYPE_CALL_PHONE) - .setScore(0.5f) - .setEventTime(EVENT_TIME) + .setScores(0.5f) .setEventContext(textClassificationContext) .build(); @@ -83,10 +79,8 @@ public class TextClassifierEventTronLoggerTest { ArgumentCaptor<LogMaker> captor = ArgumentCaptor.forClass(LogMaker.class); Mockito.verify(mMetricsLogger).write(captor.capture()); LogMaker logMaker = captor.getValue(); - assertThat(logMaker.getCategory()).isEqualTo( - CONVERSATION_ACTIONS); - assertThat(logMaker.getSubtype()).isEqualTo( - ACTION_TEXT_SELECTION_SMART_SHARE); + assertThat(logMaker.getCategory()).isEqualTo(CONVERSATION_ACTIONS); + assertThat(logMaker.getSubtype()).isEqualTo(ACTION_TEXT_SELECTION_SMART_SHARE); assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE)) .isEqualTo(ConversationAction.TYPE_CALL_PHONE); assertThat((float) logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE)) @@ -101,9 +95,8 @@ public class TextClassifierEventTronLoggerTest { @Test public void testWriteEvent_unsupportedCategory() { - TextClassifierEvent textClassifierEvent = - new TextClassifierEvent.Builder( - TextClassifierEvent.CATEGORY_SELECTION, + TextClassifierEvent.TextSelectionEvent textClassifierEvent = + new TextClassifierEvent.TextSelectionEvent.Builder( TextClassifierEvent.TYPE_SMART_ACTION) .build(); 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 00b4a225c55f..ac039dddb66c 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -42,6 +42,7 @@ import android.content.ClipboardManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.Cursor; import android.graphics.Bitmap; @@ -53,7 +54,6 @@ import android.metrics.LogMaker; import android.net.Uri; import android.service.chooser.ChooserTarget; -import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; @@ -62,10 +62,14 @@ import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Function; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @@ -75,21 +79,48 @@ import java.util.List; /** * Chooser activity instrumentation tests */ -@RunWith(AndroidJUnit4.class) +@RunWith(Parameterized.class) public class ChooserActivityTest { + private static final Function<PackageManager, PackageManager> DEFAULT_PM = pm -> pm; + private static final Function<PackageManager, PackageManager> NO_APP_PREDICTION_SERVICE_PM = + pm -> { + PackageManager mock = Mockito.spy(pm); + when(mock.getAppPredictionServicePackageName()).thenReturn(null); + return mock; + }; + + @Parameterized.Parameters + public static Collection packageManagers() { + return Arrays.asList(new Object[][] { + {0, "Default PackageManager", DEFAULT_PM}, + {1, "No App Prediction Service", NO_APP_PREDICTION_SERVICE_PM} + }); + } + private static final int CONTENT_PREVIEW_IMAGE = 1; private static final int CONTENT_PREVIEW_FILE = 2; private static final int CONTENT_PREVIEW_TEXT = 3; + private Function<PackageManager, PackageManager> mPackageManagerOverride; + private int mTestNum; @Rule public ActivityTestRule<ChooserWrapperActivity> mActivityRule = new ActivityTestRule<>(ChooserWrapperActivity.class, false, false); + public ChooserActivityTest( + int testNum, + String testName, + Function<PackageManager, PackageManager> packageManagerOverride) { + mPackageManagerOverride = packageManagerOverride; + mTestNum = testNum; + } + @Before public void cleanOverrideData() { sOverrides.reset(); + sOverrides.createPackageManager = mPackageManagerOverride; } @Test diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp new file mode 100644 index 000000000000..ae3ff8612eee --- /dev/null +++ b/core/tests/mockingcoretests/Android.bp @@ -0,0 +1,53 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + name: "FrameworksMockingCoreTests", + + srcs: [ + "src/**/*.java", + ], + + static_libs: [ + "frameworks-base-testutils", + "services.core", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-target-extended-minus-junit4", + "platform-test-annotations", + "truth-prebuilt", + "testables", + "ub-uiautomator", + ], + + libs: [ + "android.test.base", + "android.test.mock", + "android.test.runner", + ], + + // These are not normally accessible from apps so they must be explicitly included. + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + platform_apis: true, + test_suites: ["device-tests"], + + certificate: "platform", +} diff --git a/core/tests/mockingcoretests/AndroidManifest.xml b/core/tests/mockingcoretests/AndroidManifest.xml new file mode 100644 index 000000000000..b9ee0852366d --- /dev/null +++ b/core/tests/mockingcoretests/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + android:installLocation="internalOnly" + package="com.android.frameworks.mockingcoretests" + android:sharedUserId="com.android.uid.test"> + + <application android:supportsRtl="true" android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + + <activity android:name="android.app.activity.ActivityThreadClientTest$TestActivity" + android:exported="true"> + </activity> + + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.mockingcoretests" + android:label="Frameworks Mocking Core Tests" /> +</manifest> diff --git a/core/tests/mockingcoretests/AndroidTest.xml b/core/tests/mockingcoretests/AndroidTest.xml new file mode 100644 index 000000000000..47aa41003336 --- /dev/null +++ b/core/tests/mockingcoretests/AndroidTest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> +<configuration description="Runs Frameworks Mocking Core Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="FrameworksMockingCoreTests.apk" /> + </target_preparer> + <option name="test-tag" value="FrameworksMockingCoreTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.frameworks.mockingcoretests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/core/tests/mockingcoretests/README b/core/tests/mockingcoretests/README new file mode 100644 index 000000000000..c0f65c29a8a4 --- /dev/null +++ b/core/tests/mockingcoretests/README @@ -0,0 +1,33 @@ +* 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. + + +INTRODUCTION + +The Android platform core tests that require additional mocking capabilities and use Extended +Mockito, such as the ability to stub static methods. ExtendedMockito is not fully compatible with +regular Mockito library, so tests that use it have to be verified and adapted. For that reason a +separate module is used instead of adding to FrameworksCoreTests. + +For more information about ExtendedMockito see documentation of +com.android.dx.mockito.inline.extended.ExtendedMockito class. + +See ../coretests/README for more information on FrameworksCoreTests. + + +INSTRUCTIONS + +To build, install and run: + + atest FrameworksMockingCoreTests diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java new file mode 100644 index 000000000000..86d55ea4f5c2 --- /dev/null +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -0,0 +1,362 @@ +/* + * 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.app.activity; + +import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY; +import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; +import static android.app.servertransaction.ActivityLifecycleItem.ON_START; +import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + +import android.app.Activity; +import android.app.ActivityTaskManager; +import android.app.ActivityThread; +import android.app.ActivityThread.ActivityClientRecord; +import android.app.IActivityTaskManager; +import android.app.servertransaction.PendingTransactionActions; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.os.Binder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.view.WindowManagerGlobal; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.MediumTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +/** + * Test for verifying {@link android.app.ActivityThread} class. + * + * <p>Build/Install/Run: + * atest FrameworksMockingCoreTests:android.app.activity.ActivityThreadClientTest + * + * <p>This test class is a part of Window Manager Service tests and specified in + * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. + */ +@RunWith(AndroidJUnit4.class) +@MediumTest +public class ActivityThreadClientTest { + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnCreate() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + clientSession.launchActivity(r); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_CREATE, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnCreate_Finished() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + Activity activity = clientSession.launchActivity(r); + activity.finish(); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_CREATE, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnStart() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + clientSession.launchActivity(r); + clientSession.startActivity(r); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_START, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnStart_Finished() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + Activity activity = clientSession.launchActivity(r); + clientSession.startActivity(r); + activity.finish(); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_START, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnResume() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + clientSession.launchActivity(r); + clientSession.startActivity(r); + clientSession.resumeActivity(r); + assertEquals(ON_RESUME, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_START, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnPause() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + clientSession.launchActivity(r); + clientSession.startActivity(r); + clientSession.resumeActivity(r); + clientSession.pauseActivity(r); + assertEquals(ON_PAUSE, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_START, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnStop() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + clientSession.launchActivity(r); + clientSession.startActivity(r); + clientSession.resumeActivity(r); + clientSession.pauseActivity(r); + clientSession.stopActivity(r); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_STOP, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testLifecycleAfterFinished_OnCreate() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + Activity activity = clientSession.launchActivity(r); + activity.finish(); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.startActivity(r); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.resumeActivity(r); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.pauseActivity(r); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.stopActivity(r); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.destroyActivity(r); + assertEquals(ON_DESTROY, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testLifecycleAfterFinished_OnStart() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + Activity activity = clientSession.launchActivity(r); + clientSession.startActivity(r); + activity.finish(); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.resumeActivity(r); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.pauseActivity(r); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.stopActivity(r); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.destroyActivity(r); + assertEquals(ON_DESTROY, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testLifecycleAfterFinished_OnResume() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + Activity activity = clientSession.launchActivity(r); + clientSession.startActivity(r); + clientSession.resumeActivity(r); + activity.finish(); + assertEquals(ON_RESUME, r.getLifecycleState()); + + clientSession.pauseActivity(r); + assertEquals(ON_PAUSE, r.getLifecycleState()); + + clientSession.stopActivity(r); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.destroyActivity(r); + assertEquals(ON_DESTROY, r.getLifecycleState()); + } + } + + private class ClientMockSession implements AutoCloseable { + private MockitoSession mMockSession; + private ActivityThread mThread; + + private ClientMockSession() throws RemoteException { + mThread = ActivityThread.currentActivityThread(); + mMockSession = mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(ActivityTaskManager.class) + .spyStatic(WindowManagerGlobal.class) + .startMocking(); + doReturn(Mockito.mock(WindowManagerGlobal.class)) + .when(WindowManagerGlobal::getInstance); + IActivityTaskManager mockAtm = Mockito.mock(IActivityTaskManager.class); + doReturn(mockAtm).when(ActivityTaskManager::getService); + when(mockAtm.finishActivity(any(), anyInt(), any(), anyInt())).thenReturn(true); + } + + private Activity launchActivity(ActivityClientRecord r) { + return mThread.handleLaunchActivity(r, null /* pendingActions */, + null /* customIntent */); + } + + private void startActivity(ActivityClientRecord r) { + mThread.handleStartActivity(r, null /* pendingActions */); + } + + private void resumeActivity(ActivityClientRecord r) { + mThread.handleResumeActivity(r.token, true /* finalStateRequest */, + true /* isForward */, "test"); + } + + private void pauseActivity(ActivityClientRecord r) { + mThread.handlePauseActivity(r.token, false /* finished */, + false /* userLeaving */, 0 /* configChanges */, null /* pendingActions */, + "test"); + } + + private void stopActivity(ActivityClientRecord r) { + mThread.handleStopActivity(r.token, false /* show */, 0 /* configChanges */, + new PendingTransactionActions(), false /* finalStateRequest */, "test"); + } + + private void destroyActivity(ActivityClientRecord r) { + mThread.handleDestroyActivity(r.token, true /* finishing */, 0 /* configChanges */, + false /* getNonConfigInstance */, "test"); + } + + private void changeVisibility(ActivityClientRecord r, boolean show) { + mThread.handleWindowVisibility(r.token, show); + } + + private ActivityClientRecord stubActivityRecord() { + ComponentName component = new ComponentName( + InstrumentationRegistry.getInstrumentation().getContext(), TestActivity.class); + ActivityInfo info = new ActivityInfo(); + info.packageName = component.getPackageName(); + info.name = component.getClassName(); + info.exported = true; + info.applicationInfo = new ApplicationInfo(); + info.applicationInfo.packageName = info.packageName; + info.applicationInfo.uid = UserHandle.myUserId(); + + return new ActivityClientRecord(new Binder(), Intent.makeMainActivity(component), + 0 /* ident */, info, new Configuration(), + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* referrer */, + null /* voiceInteractor */, null /* state */, null /* persistentState */, + null /* pendingResults */, null /* pendingNewIntents */, true /* isForward */, + null /* profilerInfo */, mThread /* client */); + } + + @Override + public void close() { + mMockSession.finishMocking(); + } + } + + // Test activity + public static class TestActivity extends Activity { + } +} diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 0e957df4dc14..c54208b96031 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -180,6 +180,9 @@ <assign-permission name="android.permission.STATSCOMPANION" uid="statsd" /> <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="statsd" /> + <split-permission name="android.permission.ACCESS_FINE_LOCATION"> + <new-permission name="android.permission.ACCESS_COARSE_LOCATION" /> + </split-permission> <split-permission name="android.permission.WRITE_EXTERNAL_STORAGE"> <new-permission name="android.permission.READ_EXTERNAL_STORAGE" /> </split-permission> diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 11e4cb835d65..2ad2e76cc696 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -3510,6 +3510,7 @@ struct ResTable::Package ResStringPool keyStrings; size_t typeIdOffset; + bool definesOverlayable = false; }; // A group of objects describing a particular resource package. @@ -6861,6 +6862,10 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, ALOGW("Found multiple library tables, ignoring..."); } } else { + if (ctype == RES_TABLE_OVERLAYABLE_TYPE) { + package->definesOverlayable = true; + } + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), endPos, "ResTable_package:unknown"); if (err != NO_ERROR) { @@ -7138,6 +7143,11 @@ status_t ResTable::createIdmap(const ResTable& targetResTable, return UNKNOWN_ERROR; } + // Idmap is not aware of overlayable, exit since policy checks can't be done + if (targetResTable.mPackageGroups[0]->packages[0]->definesOverlayable) { + return UNKNOWN_ERROR; + } + const ResTable_package* targetPackageStruct = targetResTable.mPackageGroups[0]->packages[0]->package; const size_t tmpNameSize = arraysize(targetPackageStruct->name); diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 9b1f25986d56..99988542d619 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -17,8 +17,8 @@ #include "Properties.h" #include "Debug.h" #include "DeviceInfo.h" -#include "SkTraceEventCommon.h" #include "HWUIProperties.sysprop.h" +#include "SkTraceEventCommon.h" #include <algorithm> #include <cstdlib> @@ -67,7 +67,7 @@ bool Properties::debuggingEnabled = false; bool Properties::isolatedProcess = false; int Properties::contextPriority = 0; -int Properties::defaultRenderAhead = 0; +uint32_t Properties::defaultRenderAhead = 0; static int property_get_int(const char* key, int defaultValue) { char buf[PROPERTY_VALUE_MAX] = { @@ -130,12 +130,9 @@ bool Properties::load() { enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, true); - defaultRenderAhead = std::max(0, std::min(2, property_get_int(PROPERTY_RENDERAHEAD, - render_ahead().value_or(0)))); - - if (defaultRenderAhead && sRenderPipelineType == RenderPipelineType::SkiaVulkan) { - ALOGW("hwui.render_ahead of %d ignored because pipeline is skiavk", defaultRenderAhead); - } + defaultRenderAhead = + std::max(0u, std::min(2u, static_cast<uint32_t>(property_get_int( + PROPERTY_RENDERAHEAD, render_ahead().value_or(0))))); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 3e91c63fcbde..3105e58362ec 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -253,7 +253,7 @@ public: ANDROID_API static int contextPriority; - static int defaultRenderAhead; + static uint32_t defaultRenderAhead; private: static ProfileType sProfileType; diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp index cdad20ec6caa..dc53dd6c27c3 100644 --- a/libs/hwui/TreeInfo.cpp +++ b/libs/hwui/TreeInfo.cpp @@ -25,6 +25,7 @@ TreeInfo::TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContex , prepareTextures(mode == MODE_FULL) , canvasContext(canvasContext) , damageGenerationId(canvasContext.getFrameNumber()) - , disableForceDark(canvasContext.useForceDark() ? 0 : 1) {} + , disableForceDark(canvasContext.useForceDark() ? 0 : 1) + , screenSize(canvasContext.getNextFrameSize()) {} } // namespace android::uirenderer diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 04eabac395f0..7e8d12fd4597 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -20,6 +20,7 @@ #include "utils/Macros.h" #include <utils/Timers.h> +#include "SkSize.h" #include <string> @@ -96,6 +97,8 @@ public: int disableForceDark; + const SkISize screenSize; + struct Out { bool hasFunctors = false; // This is only updated if evaluateAnimations is true diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index da905cf9e63a..5418b337c371 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -547,6 +547,11 @@ void Tree::Cache::clear() { } void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) { + if (canvas->quickReject(bounds)) { + // The RenderNode is on screen, but the AVD is not. + return; + } + // Update the paint for any animatable properties SkPaint paint = inPaint; paint.setAlpha(mProperties.getRootAlpha() * 255); diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp index 8508274676fd..66aa8c203799 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.cpp +++ b/libs/hwui/pipeline/skia/ShaderCache.cpp @@ -15,6 +15,7 @@ */ #include "ShaderCache.h" +#include <GrContext.h> #include <log/log.h> #include <openssl/sha.h> #include <algorithm> @@ -23,7 +24,6 @@ #include "FileBlobCache.h" #include "Properties.h" #include "utils/TraceUtils.h" -#include <GrContext.h> namespace android { namespace uirenderer { diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index 29d5ef233338..41bcfc25f5c1 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -22,6 +22,7 @@ #include "renderthread/CanvasContext.h" #include <SkImagePriv.h> +#include <SkPathOps.h> namespace android { namespace uirenderer { @@ -35,7 +36,7 @@ void SkiaDisplayList::syncContents(const WebViewSyncData& data) { animatedImage->syncProperties(); } for (auto& vectorDrawable : mVectorDrawables) { - vectorDrawable->syncProperties(); + vectorDrawable.first->syncProperties(); } } @@ -51,6 +52,29 @@ void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) } } +static bool intersects(const SkISize screenSize, const Matrix4& mat, const SkRect& bounds) { + Vector3 points[] = { Vector3 {bounds.fLeft, bounds.fTop, 0}, + Vector3 {bounds.fRight, bounds.fTop, 0}, + Vector3 {bounds.fRight, bounds.fBottom, 0}, + Vector3 {bounds.fLeft, bounds.fBottom, 0}}; + float minX, minY, maxX, maxY; + bool first = true; + for (auto& point : points) { + mat.mapPoint3d(point); + if (first) { + minX = maxX = point.x; + minY = maxY = point.y; + first = false; + } else { + minX = std::min(minX, point.x); + minY = std::min(minY, point.y); + maxX = std::max(maxX, point.x); + maxY = std::max(maxY, point.y); + } + } + return SkRect::Make(screenSize).intersects(SkRect::MakeLTRB(minX, minY, maxX, maxY)); +} + bool SkiaDisplayList::prepareListAndChildren( TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) { @@ -107,15 +131,23 @@ bool SkiaDisplayList::prepareListAndChildren( } } - for (auto& vectorDrawable : mVectorDrawables) { + for (auto& vectorDrawablePair : mVectorDrawables) { // If any vector drawable in the display list needs update, damage the node. + auto& vectorDrawable = vectorDrawablePair.first; if (vectorDrawable->isDirty()) { - isDirty = true; - static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline()) - ->getVectorDrawables() - ->push_back(vectorDrawable); + Matrix4 totalMatrix; + info.damageAccumulator->computeCurrentTransform(&totalMatrix); + Matrix4 canvasMatrix(vectorDrawablePair.second); + totalMatrix.multiply(canvasMatrix); + const SkRect& bounds = vectorDrawable->properties().getBounds(); + if (intersects(info.screenSize, totalMatrix, bounds)) { + isDirty = true; + static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline()) + ->getVectorDrawables() + ->push_back(vectorDrawable); + vectorDrawable->setPropertyChangeWillBeConsumed(true); + } } - vectorDrawable->setPropertyChangeWillBeConsumed(true); } return isDirty; } diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 3219ad1deeff..b79103787023 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -22,6 +22,7 @@ #include "TreeInfo.h" #include "hwui/AnimatedImageDrawable.h" #include "utils/LinearAllocator.h" +#include "utils/Pair.h" #include <deque> @@ -41,12 +42,6 @@ typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; namespace skiapipeline { -/** - * This class is intended to be self contained, but still subclasses from - * DisplayList to make it easier to support switching between the two at - * runtime. The downside of this inheritance is that we pay for the overhead - * of the parent class construction/destruction without any real benefit. - */ class SkiaDisplayList { public: size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); } @@ -156,7 +151,17 @@ public: std::deque<RenderNodeDrawable> mChildNodes; std::deque<FunctorDrawable*> mChildFunctors; std::vector<SkImage*> mMutableImages; - std::vector<VectorDrawableRoot*> mVectorDrawables; +private: + std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables; +public: + void appendVD(VectorDrawableRoot* r) { + appendVD(r, SkMatrix::I()); + } + + void appendVD(VectorDrawableRoot* r, const SkMatrix& mat) { + mVectorDrawables.push_back(Pair<VectorDrawableRoot*, SkMatrix>(r, mat)); + } + std::vector<AnimatedImageDrawable*> mAnimatedImages; DisplayListData mDisplayList; diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 570e895a012d..9248eadbd0ef 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -101,7 +101,7 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con SkiaPipeline::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, - SkMatrix::I()); + SkMatrix::I()); layerUpdateQueue->clear(); // Draw visual debugging features @@ -156,8 +156,23 @@ void SkiaOpenGLPipeline::onStop() { } } +static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { + int query_value; + int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); + if (err != 0 || query_value < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); + return; + } + auto min_undequeued_buffers = static_cast<uint32_t>(query_value); + + int bufferCount = min_undequeued_buffers + 2 + extraBuffers; + ALOGD("Setting buffer count to %d, min_undequeued %u, extraBuffers %u", + bufferCount, min_undequeued_buffers, extraBuffers); + native_window_set_buffer_count(window, bufferCount); +} + bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, - ColorMode colorMode) { + ColorMode colorMode, uint32_t extraBuffers) { if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; @@ -177,6 +192,7 @@ bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh if (mEglSurface != EGL_NO_SURFACE) { const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer); mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); + setBufferCount(surface, extraBuffers); return true; } diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 66929226a5e2..3fe0f92b1924 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -44,7 +44,7 @@ public: FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, - renderthread::ColorMode colorMode) override; + renderthread::ColorMode colorMode, uint32_t extraBuffers) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 721a115c1381..ccc1701dcc0b 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -250,8 +250,9 @@ SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { } if (mCaptureSequence > 0 || mPictureCapturedCallback) { mRecorder.reset(new SkPictureRecorder()); - SkCanvas* pictureCanvas = mRecorder->beginRecording(surface->width(), surface->height(), nullptr, - SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); + SkCanvas* pictureCanvas = + mRecorder->beginRecording(surface->width(), surface->height(), nullptr, + SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height()); mNwayCanvas->addCanvas(surface->getCanvas()); mNwayCanvas->addCanvas(pictureCanvas); @@ -276,8 +277,7 @@ void SkiaPipeline::endCapture(SkSurface* surface) { if (1 == mCaptureSequence) { savePictureAsync(data, mCapturedFile); } else { - savePictureAsync(data, - mCapturedFile + "_" + std::to_string(mCaptureSequence)); + savePictureAsync(data, mCapturedFile + "_" + std::to_string(mCaptureSequence)); } mCaptureSequence--; } @@ -327,7 +327,7 @@ static Rect nodeBounds(RenderNode& node) { auto& props = node.properties(); return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom()); } -} +} // namespace void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, @@ -464,10 +464,20 @@ void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) { // (3) Requires RGBA colors (instead of BGRA). static const uint32_t kOverdrawColors[2][6] = { { - 0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f, + 0x00000000, + 0x00000000, + 0x2f2f0000, + 0x2f002f00, + 0x3f00003f, + 0x7f00007f, }, { - 0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f, + 0x00000000, + 0x00000000, + 0x2f2f0000, + 0x4f004f4f, + 0x5f50335f, + 0x7f00007f, }, }; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index d9456355cb88..0a2894945dc8 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -142,8 +142,7 @@ void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, void SkiaRecordingCanvas::drawWebViewFunctor(int functor) { FunctorDrawable* functorDrawable; if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - functorDrawable = - mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, asSkCanvas()); + functorDrawable = mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, asSkCanvas()); } else { functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas()); } @@ -153,7 +152,9 @@ void SkiaRecordingCanvas::drawWebViewFunctor(int functor) { void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { mRecorder.drawVectorDrawable(tree); - mDisplayList->mVectorDrawables.push_back(tree); + SkMatrix mat; + this->getMatrix(&mat); + mDisplayList->appendVD(tree, mat); } // ---------------------------------------------------------------------------- diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index edde6d3e05c0..e8cb219db320 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -18,12 +18,12 @@ #include "DeferredLayerUpdater.h" #include "Readback.h" +#include "ShaderCache.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" #include "VkInteropFunctorDrawable.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" -#include "ShaderCache.h" #include <SkSurface.h> #include <SkTypes.h> @@ -70,8 +70,8 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con return false; } SkiaPipeline::updateLighting(lightGeometry, lightInfo); - renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, - backBuffer, mVkSurface->getCurrentPreTransform()); + renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer, + mVkSurface->getCurrentPreTransform()); ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext()); layerUpdateQueue->clear(); @@ -116,7 +116,7 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { void SkiaVulkanPipeline::onStop() {} bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, - ColorMode colorMode) { + ColorMode colorMode, uint32_t extraBuffers) { if (mVkSurface) { mVkManager.destroySurface(mVkSurface); mVkSurface = nullptr; @@ -125,8 +125,9 @@ bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh setSurfaceColorProperties(colorMode); if (surface) { mRenderThread.requireVkContext(); - mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace, - mSurfaceColorType, mRenderThread.getGrContext()); + mVkSurface = + mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace, mSurfaceColorType, + mRenderThread.getGrContext(), extraBuffers); } return mVkSurface != nullptr; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 77a7ab171ee1..31734783de7f 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -43,7 +43,7 @@ public: FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, - renderthread::ColorMode colorMode) override; + renderthread::ColorMode colorMode, uint32_t extraBuffers) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp index 1b9e53b21adb..112792611fc3 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -17,16 +17,16 @@ #include "VkFunctorDrawable.h" #include <private/hwui/DrawVkInfo.h> -#include "renderthread/VulkanManager.h" -#include "renderthread/RenderThread.h" -#include <SkAndroidFrameworkUtils.h> #include <GrBackendDrawableInfo.h> +#include <SkAndroidFrameworkUtils.h> #include <SkImage.h> #include <utils/Color.h> #include <utils/Trace.h> #include <utils/TraceUtils.h> #include <vk/GrVkTypes.h> #include <thread> +#include "renderthread/RenderThread.h" +#include "renderthread/VulkanManager.h" #include "thread/ThreadBase.h" #include "utils/TimeUtils.h" @@ -64,13 +64,13 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { SkMatrix44 mat4(mMatrix); VkFunctorDrawParams params{ - .width = mImageInfo.width(), - .height = mImageInfo.height(), - .color_space_ptr = mImageInfo.colorSpace(), - .clip_left = mClip.fLeft, - .clip_top = mClip.fTop, - .clip_right = mClip.fRight, - .clip_bottom = mClip.fBottom, + .width = mImageInfo.width(), + .height = mImageInfo.height(), + .color_space_ptr = mImageInfo.colorSpace(), + .clip_left = mClip.fLeft, + .clip_top = mClip.fTop, + .clip_right = mClip.fRight, + .clip_bottom = mClip.fBottom, }; mat4.asColMajorf(¶ms.transform[0]); params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer; @@ -87,8 +87,7 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { vulkan_info.fDrawBounds->extent.height = mClip.fBottom - mClip.fTop; } -VkFunctorDrawable::~VkFunctorDrawable() { -} +VkFunctorDrawable::~VkFunctorDrawable() {} void VkFunctorDrawable::onDraw(SkCanvas* canvas) { // "canvas" is either SkNWayCanvas created by SkiaPipeline::tryCapture (SKP capture use case) or @@ -106,9 +105,8 @@ void VkFunctorDrawable::onDraw(SkCanvas* canvas) { SkCanvas* gpuCanvas = SkAndroidFrameworkUtils::getBaseWrappedCanvas(canvas); // Enforce "canvas" must be an AlphaFilterCanvas. For GPU canvas, the call should come from // onSnapGpuDrawHandler. - LOG_ALWAYS_FATAL_IF( - gpuCanvas == canvas, - "VkFunctorDrawable::onDraw() should not be called with a GPU canvas!"); + LOG_ALWAYS_FATAL_IF(gpuCanvas == canvas, + "VkFunctorDrawable::onDraw() should not be called with a GPU canvas!"); // This will invoke onSnapGpuDrawHandler and regular draw flow. gpuCanvas->drawDrawable(this); diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 8b02c11911ca..a31081c9a451 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -21,17 +21,16 @@ #include "RenderThread.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" -#include "Properties.h" #include "renderstate/RenderState.h" #include "thread/CommonPool.h" #include <GrContextOptions.h> #include <SkExecutor.h> #include <SkGraphics.h> +#include <SkMathPriv.h> #include <gui/Surface.h> #include <math.h> #include <set> -#include <SkMathPriv.h> namespace android { namespace uirenderer { @@ -79,14 +78,13 @@ void CacheManager::updateContextCacheSizes() { class CommonPoolExecutor : public SkExecutor { public: - virtual void add(std::function<void(void)> func) override { - CommonPool::post(std::move(func)); - } + virtual void add(std::function<void(void)> func) override { CommonPool::post(std::move(func)); } }; static CommonPoolExecutor sDefaultExecutor; -void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity, ssize_t size) { +void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity, + ssize_t size) { contextOptions->fAllowPathMaskCaching = true; // This sets the maximum size for a single texture atlas in the GPU font cache. If necessary, @@ -180,7 +178,8 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) } const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL - ? "GlLayer" : "VkLayer"; + ? "GlLayer" + : "VkLayer"; size_t layerMemoryTotal = 0; for (std::set<Layer*>::iterator it = renderState->mActiveLayers.begin(); it != renderState->mActiveLayers.end(); it++) { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 4808d68b89ab..2957b143a343 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -17,6 +17,7 @@ #include "CanvasContext.h" #include <GpuMemoryTracker.h> +#include "../Properties.h" #include "AnimationContext.h" #include "EglManager.h" #include "Frame.h" @@ -31,7 +32,6 @@ #include "utils/GLUtils.h" #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" -#include "../Properties.h" #include <cutils/properties.h> #include <private/hwui/DrawGlInfo.h> @@ -41,6 +41,7 @@ #include <sys/stat.h> #include <algorithm> +#include <cstdint> #include <cstdlib> #include <functional> @@ -153,7 +154,8 @@ void CanvasContext::setSurface(sp<Surface>&& surface) { } ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; - bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode); + bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode, + mRenderAheadDepth); mFrameNumber = -1; @@ -298,7 +300,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy mAnimationContext->startFrame(info.mode); mRenderPipeline->onPrepareTree(); - for (const sp<RenderNode> &node : mRenderNodes) { + for (const sp<RenderNode>& node : mRenderNodes) { // Only the primary target node will be drawn full - all other nodes would get drawn in // real time mode. In case of a window, the primary node is the window content and the other // node(s) are non client / filler nodes. @@ -322,7 +324,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy if (CC_LIKELY(mSwapHistory.size() && !Properties::forceDrawFrame)) { nsecs_t latestVsync = mRenderThread.timeLord().latestVsync(); - SwapHistory &lastSwap = mSwapHistory.back(); + SwapHistory& lastSwap = mSwapHistory.back(); nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync); // The slight fudge-factor is to deal with cases where // the vsync was estimated due to being slow handling the signal. @@ -405,8 +407,7 @@ void CanvasContext::draw() { SkRect dirty; mDamageAccumulator.finish(&dirty); - if (dirty.isEmpty() && Properties::skipEmptyFrames - && !surfaceRequiresRedraw()) { + if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); return; } @@ -416,21 +417,21 @@ void CanvasContext::draw() { Frame frame = mRenderPipeline->getFrame(); SkRect windowDirty = computeDirtyRect(frame, &dirty); + if (mRenderAheadDepth) { + auto presentTime = + mCurrentFrameInfo->get(FrameInfoIndex::Vsync) + + (mRenderThread.timeLord().frameIntervalNanos() * (mRenderAheadDepth + 1)); + native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime); + } bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, - mContentDrawBounds, mOpaque, mLightInfo, - mRenderNodes, &(profiler())); + mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, + &(profiler())); int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1; waitOnFences(); - if (mRenderAheadDepth) { - auto presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) + - (mRenderThread.timeLord().frameIntervalNanos() * (mRenderAheadDepth + 1)); - native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime); - } - bool requireSwap = false; bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); @@ -510,6 +511,17 @@ void CanvasContext::doFrame() { prepareAndDraw(nullptr); } +SkISize CanvasContext::getNextFrameSize() const { + ReliableSurface* surface = mNativeSurface.get(); + if (surface) { + SkISize size; + surface->query(NATIVE_WINDOW_WIDTH, &size.fWidth); + surface->query(NATIVE_WINDOW_HEIGHT, &size.fHeight); + return size; + } + return {INT32_MAX, INT32_MAX}; +} + void CanvasContext::prepareAndDraw(RenderNode* node) { ATRACE_CALL(); @@ -645,21 +657,13 @@ bool CanvasContext::surfaceRequiresRedraw() { } void CanvasContext::applyRenderAheadSettings() { - if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - // TODO: Fix SkiaVulkan's assumptions on buffer counts. And SIGBUS crashes. - mRenderAheadDepth = 0; - return; - } - if (mNativeSurface) { - native_window_set_buffer_count(mNativeSurface.get(), 3 + mRenderAheadDepth); - if (!mRenderAheadDepth) { - native_window_set_buffers_timestamp(mNativeSurface.get(), NATIVE_WINDOW_TIMESTAMP_AUTO); - } + if (mNativeSurface && !mRenderAheadDepth) { + native_window_set_buffers_timestamp(mNativeSurface.get(), NATIVE_WINDOW_TIMESTAMP_AUTO); } } -void CanvasContext::setRenderAheadDepth(int renderAhead) { - if (renderAhead < 0 || renderAhead > 2 || renderAhead == mRenderAheadDepth) { +void CanvasContext::setRenderAheadDepth(uint32_t renderAhead) { + if (renderAhead > 2 || renderAhead == mRenderAheadDepth || mNativeSurface) { return; } mRenderAheadDepth = renderAhead; diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 4a3119a55c77..912b1257de7b 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -17,30 +17,31 @@ #pragma once #include "DamageAccumulator.h" -#include "Lighting.h" #include "FrameInfo.h" #include "FrameInfoVisualizer.h" #include "FrameMetricsReporter.h" #include "IContextFactory.h" #include "IRenderPipeline.h" #include "LayerUpdateQueue.h" -#include "RenderNode.h" +#include "Lighting.h" #include "ReliableSurface.h" +#include "RenderNode.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include <EGL/egl.h> #include <SkBitmap.h> #include <SkRect.h> +#include <SkSize.h> #include <cutils/compiler.h> #include <gui/Surface.h> #include <utils/Functor.h> #include <functional> +#include <future> #include <set> #include <string> #include <vector> -#include <future> namespace android { namespace uirenderer { @@ -112,7 +113,7 @@ public: void setSurface(sp<Surface>&& surface); bool pauseSurface(); void setStopped(bool stopped); - bool hasSurface() { return mNativeSurface.get(); } + bool hasSurface() const { return mNativeSurface.get(); } void allocateBuffers(); void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); @@ -187,9 +188,7 @@ public: mRenderPipeline->setPictureCapturedCallback(callback); } - void setForceDark(bool enable) { - mUseForceDark = enable; - } + void setForceDark(bool enable) { mUseForceDark = enable; } bool useForceDark() { // The force-dark override has the highest priority, followed by the disable setting @@ -204,7 +203,10 @@ public: return mUseForceDark; } - void setRenderAheadDepth(int renderAhead); + // Must be called before setSurface + void setRenderAheadDepth(uint32_t renderAhead); + + SkISize getNextFrameSize() const; private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, @@ -238,7 +240,7 @@ private: // painted onto its surface. bool mIsDirty = false; SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default; - int mRenderAheadDepth = 0; + uint32_t mRenderAheadDepth = 0; struct SwapHistory { SkRect damage; nsecs_t vsyncTime; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 51eeab7e46ce..91dc3bc6e603 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -109,9 +109,8 @@ void DrawFrameTask::run() { // Even if we aren't drawing this vsync pulse the next frame number will still be accurate if (CC_UNLIKELY(callback)) { - context->enqueueFrameWork([callback, frameNr = context->getFrameNumber()]() { - callback(frameNr); - }); + context->enqueueFrameWork( + [callback, frameNr = context->getFrameNumber()]() { callback(frameNr); }); } if (CC_LIKELY(canDrawThisFrame)) { diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 2cc3f362e172..1d553342415c 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -29,10 +29,10 @@ #include <EGL/eglext.h> #include <GLES/gl.h> +#include <gui/Surface.h> +#include <system/window.h> #include <string> #include <vector> -#include <system/window.h> -#include <gui/Surface.h> #define GLES_VERSION 2 @@ -171,8 +171,7 @@ EGLConfig EglManager::load8BitsConfig(EGLDisplay display, EglManager::SwapBehavi EGL_NONE}; EGLConfig config = EGL_NO_CONFIG_KHR; EGLint numConfigs = 1; - if (!eglChooseConfig(display, attribs, &config, numConfigs, &numConfigs) || - numConfigs != 1) { + if (!eglChooseConfig(display, attribs, &config, numConfigs, &numConfigs) || numConfigs != 1) { return EGL_NO_CONFIG_KHR; } return config; @@ -203,8 +202,7 @@ EGLConfig EglManager::loadFP16Config(EGLDisplay display, SwapBehavior swapBehavi EGL_NONE}; EGLConfig config = EGL_NO_CONFIG_KHR; EGLint numConfigs = 1; - if (!eglChooseConfig(display, attribs, &config, numConfigs, &numConfigs) || - numConfigs != 1) { + if (!eglChooseConfig(display, attribs, &config, numConfigs, &numConfigs) || numConfigs != 1) { return EGL_NO_CONFIG_KHR; } return config; @@ -262,7 +260,7 @@ void EglManager::loadConfigs() { mEglConfigWideGamut = loadFP16Config(mEglDisplay, mSwapBehavior); if (mEglConfigWideGamut == EGL_NO_CONFIG_KHR) { ALOGE("Device claims wide gamut support, cannot find matching config, error = %s", - eglErrorString()); + eglErrorString()); EglExtensions.pixelFormatFloat = false; } } else if (wideColorType == SkColorType::kN32_SkColorType) { @@ -350,7 +348,7 @@ Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window, EGLSurface surface = eglCreateWindowSurface( mEglDisplay, wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs); if (surface == EGL_NO_SURFACE) { - return Error<EGLint> { eglGetError() }; + return Error<EGLint>{eglGetError()}; } if (mSwapBehavior != SwapBehavior::Preserved) { @@ -525,12 +523,8 @@ status_t EglManager::fenceWait(sp<Fence>& fence) { ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno); return -errno; } - EGLint attribs[] = { - EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, - EGL_NONE - }; - EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, - EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); if (sync == EGL_NO_SYNC_KHR) { close(fenceFd); ALOGE("EglManager::fenceWait: error creating EGL fence: %#x", eglGetError()); @@ -559,18 +553,16 @@ status_t EglManager::fenceWait(sp<Fence>& fence) { } status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, - sp<Fence>& nativeFence) { + sp<Fence>& nativeFence) { if (!hasEglContext()) { ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized"); return INVALID_OPERATION; } if (SyncFeatures::getInstance().useNativeFenceSync()) { - EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, - EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); if (sync == EGL_NO_SYNC_KHR) { - ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", - eglGetError()); + ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", eglGetError()); return UNKNOWN_ERROR; } glFlush(); @@ -578,7 +570,8 @@ status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, eglDestroySyncKHR(mEglDisplay, sync); if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { ALOGE("EglManager::createReleaseFence: error dup'ing native fence " - "fd: %#x", eglGetError()); + "fd: %#x", + eglGetError()); return UNKNOWN_ERROR; } nativeFence = new Fence(fenceFd); @@ -592,7 +585,7 @@ status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, EGLint result = eglClientWaitSyncKHR(mEglDisplay, *eglFence, 0, 1000000000); if (result == EGL_FALSE) { ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x", - eglGetError()); + eglGetError()); return UNKNOWN_ERROR; } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence"); diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 0502eb88b6a5..3b81014c05e2 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -66,8 +66,8 @@ public: virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; - virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, - ColorMode colorMode) = 0; + virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode, + uint32_t extraBuffers) = 0; virtual void onStop() = 0; virtual bool isSurfaceReady() = 0; virtual bool isContextReady() = 0; diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index 6f2b9df918e3..ad1fc4921781 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -34,13 +34,13 @@ struct SurfaceExposer : Surface { // Make warnings happy SurfaceExposer() = delete; - using Surface::setBufferCount; - using Surface::setSwapInterval; - using Surface::dequeueBuffer; - using Surface::queueBuffer; using Surface::cancelBuffer; + using Surface::dequeueBuffer; using Surface::lockBuffer_DEPRECATED; using Surface::perform; + using Surface::queueBuffer; + using Surface::setBufferCount; + using Surface::setSwapInterval; }; #define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__) @@ -300,17 +300,9 @@ int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) { int result = callProtected(getWrapped(window), perform, operation, args); va_end(args); - switch (operation) { - case NATIVE_WINDOW_SET_BUFFERS_FORMAT: - case NATIVE_WINDOW_SET_USAGE: - case NATIVE_WINDOW_SET_USAGE64: - va_start(args, operation); - getSelf(window)->perform(operation, args); - va_end(args); - break; - default: - break; - } + va_start(args, operation); + getSelf(window)->perform(operation, args); + va_end(args); return result; } diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index b58bab1191ed..1a1b9dac37f6 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -84,7 +84,7 @@ void RenderProxy::setName(const char* name) { void RenderProxy::setSurface(const sp<Surface>& surface) { mRenderThread.queue().post( - [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); }); + [this, surf = surface]() mutable { mContext->setSurface(std::move(surf)); }); } void RenderProxy::allocateBuffers() { @@ -251,7 +251,7 @@ void RenderProxy::dumpGraphicsMemory(int fd) { void RenderProxy::setProcessStatsBuffer(int fd) { auto& rt = RenderThread::getInstance(); - rt.queue().post([&rt, fd = dup(fd) ]() { + rt.queue().post([&rt, fd = dup(fd)]() { rt.globalProfileData().switchStorageToAshmem(fd); close(fd); }); @@ -285,7 +285,7 @@ void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) void RenderProxy::setPictureCapturedCallback( const std::function<void(sk_sp<SkPicture>&&)>& callback) { mRenderThread.queue().post( - [ this, cb = callback ]() { mContext->setPictureCapturedCallback(cb); }); + [this, cb = callback]() { mContext->setPictureCapturedCallback(cb); }); } void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) { @@ -297,13 +297,13 @@ void RenderProxy::setFrameCompleteCallback(std::function<void(int64_t)>&& callba } void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observerPtr) { - mRenderThread.queue().post([ this, observer = sp{observerPtr} ]() { + mRenderThread.queue().post([this, observer = sp{observerPtr}]() { mContext->addFrameMetricsObserver(observer.get()); }); } void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observerPtr) { - mRenderThread.queue().post([ this, observer = sp{observerPtr} ]() { + mRenderThread.queue().post([this, observer = sp{observerPtr}]() { mContext->removeFrameMetricsObserver(observer.get()); }); } @@ -313,9 +313,8 @@ void RenderProxy::setForceDark(bool enable) { } void RenderProxy::setRenderAheadDepth(int renderAhead) { - mRenderThread.queue().post([ context = mContext, renderAhead ] { - context->setRenderAheadDepth(renderAhead); - }); + mRenderThread.queue().post( + [context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); }); } int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom, @@ -393,9 +392,7 @@ void RenderProxy::releaseVDAtlasEntries() { void RenderProxy::preload() { // Create RenderThread object and start the thread. Then preload Vulkan/EGL driver. auto& thread = RenderThread::getInstance(); - thread.queue().post([&thread]() { - thread.preload(); - }); + thread.queue().post([&thread]() { thread.preload(); }); } } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index b76e49ce94a0..eca7d88e4e48 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -16,6 +16,7 @@ #include "RenderThread.h" +#include "../HardwareBitmapUploader.h" #include "CanvasContext.h" #include "DeviceInfo.h" #include "EglManager.h" @@ -29,7 +30,6 @@ #include "utils/FatVector.h" #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" -#include "../HardwareBitmapUploader.h" #ifdef HWUI_GLES_WRAP_ENABLED #include "debug/GlesDriver.h" @@ -410,9 +410,7 @@ bool RenderThread::isCurrent() { void RenderThread::preload() { // EGL driver is always preloaded only if HWUI renders with GL. if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { - std::thread eglInitThread([]() { - eglGetDisplay(EGL_DEFAULT_DISPLAY); - }); + std::thread eglInitThread([]() { eglGetDisplay(EGL_DEFAULT_DISPLAY); }); eglInitThread.detach(); } else { requireVkContext(); diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 5f43b488bcf2..6bb26fd6c675 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -22,8 +22,8 @@ #include "../JankTracker.h" #include "CacheManager.h" #include "TimeLord.h" -#include "thread/ThreadBase.h" #include "WebViewFunctorManager.h" +#include "thread/ThreadBase.h" #include "utils/TimeUtils.h" #include <GrContext.h> diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 4011329fa2da..5edf3301b2e8 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -29,7 +29,6 @@ #include <GrBackendSurface.h> #include <GrContext.h> #include <GrTypes.h> -#include <GrTypes.h> #include <vk/GrVkExtensions.h> #include <vk/GrVkTypes.h> @@ -43,7 +42,7 @@ static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& fe // so we can get access to the pNext for the next struct. struct CommonVulkanHeader { VkStructureType sType; - void* pNext; + void* pNext; }; void* pNext = features.pNext; @@ -94,13 +93,13 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe VkResult err; constexpr VkApplicationInfo app_info = { - VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType - nullptr, // pNext - "android framework", // pApplicationName - 0, // applicationVersion - "android framework", // pEngineName - 0, // engineVerison - mAPIVersion, // apiVersion + VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType + nullptr, // pNext + "android framework", // pApplicationName + 0, // applicationVersion + "android framework", // pEngineName + 0, // engineVerison + mAPIVersion, // apiVersion }; { @@ -128,14 +127,14 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe } const VkInstanceCreateInfo instance_create = { - VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType - nullptr, // pNext - 0, // flags - &app_info, // pApplicationInfo - 0, // enabledLayerNameCount - nullptr, // ppEnabledLayerNames - (uint32_t) mInstanceExtensions.size(), // enabledExtensionNameCount - mInstanceExtensions.data(), // ppEnabledExtensionNames + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType + nullptr, // pNext + 0, // flags + &app_info, // pApplicationInfo + 0, // enabledLayerNameCount + nullptr, // ppEnabledLayerNames + (uint32_t)mInstanceExtensions.size(), // enabledExtensionNameCount + mInstanceExtensions.data(), // ppEnabledExtensionNames }; GET_PROC(CreateInstance); @@ -200,11 +199,11 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe { uint32_t extensionCount = 0; err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount, - nullptr); + nullptr); LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err); mDeviceExtensionsOwner.resize(extensionCount); err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount, - mDeviceExtensionsOwner.data()); + mDeviceExtensionsOwner.data()); LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err); bool hasKHRSwapchainExtension = false; for (const VkExtensionProperties& extension : mDeviceExtensionsOwner) { @@ -216,7 +215,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe LOG_ALWAYS_FATAL_IF(!hasKHRSwapchainExtension); } - auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) { + auto getProc = [](const char* proc_name, VkInstance instance, VkDevice device) { if (device != VK_NULL_HANDLE) { return vkGetDeviceProcAddr(device, proc_name); } @@ -224,7 +223,8 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe }; grExtensions.init(getProc, mInstance, mPhysicalDevice, mInstanceExtensions.size(), - mInstanceExtensions.data(), mDeviceExtensions.size(), mDeviceExtensions.data()); + mInstanceExtensions.data(), mDeviceExtensions.size(), + mDeviceExtensions.data()); LOG_ALWAYS_FATAL_IF(!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)); @@ -237,7 +237,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe if (grExtensions.hasExtension(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME, 2)) { VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT* blend; - blend = (VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT*) malloc( + blend = (VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT*)malloc( sizeof(VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT)); LOG_ALWAYS_FATAL_IF(!blend); blend->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT; @@ -247,7 +247,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe } VkPhysicalDeviceSamplerYcbcrConversionFeatures* ycbcrFeature; - ycbcrFeature = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*) malloc( + ycbcrFeature = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*)malloc( sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures)); LOG_ALWAYS_FATAL_IF(!ycbcrFeature); ycbcrFeature->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; @@ -261,17 +261,17 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe // and we can't depend on it on all platforms features.features.robustBufferAccess = VK_FALSE; - float queuePriorities[1] = { 0.0 }; + float queuePriorities[1] = {0.0}; void* queueNextPtr = nullptr; VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo; - if (Properties::contextPriority != 0 - && grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { + if (Properties::contextPriority != 0 && + grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { memset(&queuePriorityCreateInfo, 0, sizeof(VkDeviceQueueGlobalPriorityCreateInfoEXT)); queuePriorityCreateInfo.sType = - VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT; + VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT; queuePriorityCreateInfo.pNext = nullptr; switch (Properties::contextPriority) { case EGL_CONTEXT_PRIORITY_LOW_IMG: @@ -285,41 +285,40 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe break; default: LOG_ALWAYS_FATAL("Unsupported context priority"); - } - queueNextPtr = &queuePriorityCreateInfo; + } + queueNextPtr = &queuePriorityCreateInfo; } const VkDeviceQueueCreateInfo queueInfo[2] = { - { - VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType - queueNextPtr, // pNext - 0, // VkDeviceQueueCreateFlags - mGraphicsQueueIndex, // queueFamilyIndex - 1, // queueCount - queuePriorities, // pQueuePriorities - }, - { - VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType - queueNextPtr, // pNext - 0, // VkDeviceQueueCreateFlags - mPresentQueueIndex, // queueFamilyIndex - 1, // queueCount - queuePriorities, // pQueuePriorities - } - }; + { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType + queueNextPtr, // pNext + 0, // VkDeviceQueueCreateFlags + mGraphicsQueueIndex, // queueFamilyIndex + 1, // queueCount + queuePriorities, // pQueuePriorities + }, + { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType + queueNextPtr, // pNext + 0, // VkDeviceQueueCreateFlags + mPresentQueueIndex, // queueFamilyIndex + 1, // queueCount + queuePriorities, // pQueuePriorities + }}; uint32_t queueInfoCount = (mPresentQueueIndex != mGraphicsQueueIndex) ? 2 : 1; const VkDeviceCreateInfo deviceInfo = { - VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType - &features, // pNext - 0, // VkDeviceCreateFlags - queueInfoCount, // queueCreateInfoCount - queueInfo, // pQueueCreateInfos - 0, // layerCount - nullptr, // ppEnabledLayerNames - (uint32_t) mDeviceExtensions.size(), // extensionCount - mDeviceExtensions.data(), // ppEnabledExtensionNames - nullptr, // ppEnabledFeatures + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType + &features, // pNext + 0, // VkDeviceCreateFlags + queueInfoCount, // queueCreateInfoCount + queueInfo, // pQueueCreateInfos + 0, // layerCount + nullptr, // ppEnabledLayerNames + (uint32_t)mDeviceExtensions.size(), // extensionCount + mDeviceExtensions.data(), // ppEnabledExtensionNames + nullptr, // ppEnabledFeatures }; LOG_ALWAYS_FATAL_IF(mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice)); @@ -371,8 +370,8 @@ void VulkanManager::initialize() { // this needs to be on the render queue commandPoolInfo.queueFamilyIndex = mGraphicsQueueIndex; commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - SkDEBUGCODE(VkResult res =) mCreateCommandPool(mDevice, &commandPoolInfo, nullptr, - &mCommandPool); + SkDEBUGCODE(VkResult res =) + mCreateCommandPool(mDevice, &commandPoolInfo, nullptr, &mCommandPool); SkASSERT(VK_SUCCESS == res); } LOG_ALWAYS_FATAL_IF(mCommandPool == VK_NULL_HANDLE); @@ -391,7 +390,7 @@ void VulkanManager::initialize() { } sk_sp<GrContext> VulkanManager::createContext(const GrContextOptions& options) { - auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) { + auto getProc = [](const char* proc_name, VkInstance instance, VkDevice device) { if (device != VK_NULL_HANDLE) { return vkGetDeviceProcAddr(device, proc_name); } @@ -431,7 +430,6 @@ VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const { } Frame VulkanManager::dequeueNextBuffer(VulkanSurface* surface) { - VulkanSurface::NativeBufferInfo* bufferInfo = surface->dequeueNativeBuffer(); if (bufferInfo == nullptr) { @@ -480,7 +478,7 @@ Frame VulkanManager::dequeueNextBuffer(VulkanSurface* surface) { bufferInfo->skSurface->wait(1, &backendSemaphore); // The following flush blocks the GPU immediately instead of waiting for other // drawing ops. It seems dequeue_fence is not respected otherwise. - //TODO: remove the flush after finding why backendSemaphore is not working. + // TODO: remove the flush after finding why backendSemaphore is not working. bufferInfo->skSurface->flush(); } } @@ -557,15 +555,15 @@ void VulkanManager::destroySurface(VulkanSurface* surface) { VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode, sk_sp<SkColorSpace> surfaceColorSpace, - SkColorType surfaceColorType, - GrContext* grContext) { + SkColorType surfaceColorType, GrContext* grContext, + uint32_t extraBuffers) { LOG_ALWAYS_FATAL_IF(!hasVkContext(), "Not initialized"); if (!window) { return nullptr; } return VulkanSurface::Create(window, colorMode, surfaceColorType, surfaceColorSpace, grContext, - *this); + *this, extraBuffers); } bool VulkanManager::setupDummyCommandBuffer() { diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index a7a43cc39a45..1a3a0e485523 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -18,16 +18,16 @@ #define VULKANMANAGER_H #if !defined(VK_USE_PLATFORM_ANDROID_KHR) -# define VK_USE_PLATFORM_ANDROID_KHR +#define VK_USE_PLATFORM_ANDROID_KHR #endif #include <vulkan/vulkan.h> #include <GrContextOptions.h> -#include <vk/GrVkExtensions.h> #include <SkSurface.h> #include <ui/Fence.h> #include <utils/StrongPointer.h> #include <vk/GrVkBackendContext.h> +#include <vk/GrVkExtensions.h> #include "Frame.h" #include "IRenderPipeline.h" #include "VulkanSurface.h" @@ -59,8 +59,8 @@ public: // Create and destroy functions for wrapping an ANativeWindow in a VulkanSurface VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode, sk_sp<SkColorSpace> surfaceColorSpace, - SkColorType surfaceColorType, - GrContext* grContext); + SkColorType surfaceColorType, GrContext* grContext, + uint32_t extraBuffers); void destroySurface(VulkanSurface* surface); Frame dequeueNextBuffer(VulkanSurface* surface); diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index be78b694f53a..df6b9ed2cdcb 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -16,12 +16,12 @@ #include "VulkanSurface.h" -#include <algorithm> #include <SkSurface.h> +#include <algorithm> #include "VulkanManager.h" -#include "utils/TraceUtils.h" #include "utils/Color.h" +#include "utils/TraceUtils.h" namespace android { namespace uirenderer { @@ -31,10 +31,9 @@ static bool IsTransformSupported(int transform) { // For now, only support pure rotations, not flip or flip-and-rotate, until we have // more time to test them and build sample code. As far as I know we never actually // use anything besides pure rotations anyway. - return transform == 0 - || transform == NATIVE_WINDOW_TRANSFORM_ROT_90 - || transform == NATIVE_WINDOW_TRANSFORM_ROT_180 - || transform == NATIVE_WINDOW_TRANSFORM_ROT_270; + return transform == 0 || transform == NATIVE_WINDOW_TRANSFORM_ROT_90 || + transform == NATIVE_WINDOW_TRANSFORM_ROT_180 || + transform == NATIVE_WINDOW_TRANSFORM_ROT_270; } static int InvertTransform(int transform) { @@ -85,16 +84,16 @@ static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) { } void VulkanSurface::ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize, - const SkISize& maxSize) { + const SkISize& maxSize) { SkISize& windowSize = windowInfo->size; // clamp width & height to handle currentExtent of -1 and protect us from broken hints - if (windowSize.width() < minSize.width() || windowSize.width() > maxSize.width() - || windowSize.height() < minSize.height() || windowSize.height() > maxSize.height()) { + if (windowSize.width() < minSize.width() || windowSize.width() > maxSize.width() || + windowSize.height() < minSize.height() || windowSize.height() > maxSize.height()) { int width = std::min(maxSize.width(), std::max(minSize.width(), windowSize.width())); int height = std::min(maxSize.height(), std::max(minSize.height(), windowSize.height())); - ALOGE("Invalid Window Dimensions [%d, %d]; clamping to [%d, %d]", - windowSize.width(), windowSize.height(), width, height); + ALOGE("Invalid Window Dimensions [%d, %d]; clamping to [%d, %d]", windowSize.width(), + windowSize.height(), width, height); windowSize.set(width, height); } @@ -145,12 +144,8 @@ class VkSurfaceAutoDeleter { public: VkSurfaceAutoDeleter(VkInstance instance, VkSurfaceKHR surface, PFN_vkDestroySurfaceKHR destroySurfaceKHR) - : mInstance(instance) - , mSurface(surface) - , mDestroySurfaceKHR(destroySurfaceKHR) {} - ~VkSurfaceAutoDeleter() { - destroy(); - } + : mInstance(instance), mSurface(surface), mDestroySurfaceKHR(destroySurfaceKHR) {} + ~VkSurfaceAutoDeleter() { destroy(); } void destroy() { if (mSurface != VK_NULL_HANDLE) { @@ -166,9 +161,9 @@ private: }; VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, - SkColorType colorType, sk_sp<SkColorSpace> colorSpace, - GrContext* grContext, const VulkanManager& vkManager) { - + SkColorType colorType, sk_sp<SkColorSpace> colorSpace, + GrContext* grContext, const VulkanManager& vkManager, + uint32_t extraBuffers) { VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; @@ -188,10 +183,11 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, vkManager.mDestroySurfaceKHR); SkDEBUGCODE(VkBool32 supported; res = vkManager.mGetPhysicalDeviceSurfaceSupportKHR( - vkManager.mPhysicalDevice, vkManager.mPresentQueueIndex, vkSurface, &supported); - // All physical devices and queue families on Android must be capable of - // presentation with any native window. - SkASSERT(VK_SUCCESS == res && supported);); + vkManager.mPhysicalDevice, vkManager.mPresentQueueIndex, + vkSurface, &supported); + // All physical devices and queue families on Android must be capable of + // presentation with any native window. + SkASSERT(VK_SUCCESS == res && supported);); // check for capabilities VkSurfaceCapabilitiesKHR caps; @@ -225,14 +221,13 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, int query_value; int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != 0 || query_value < 0) { - ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, - query_value); + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); return nullptr; } auto min_undequeued_buffers = static_cast<uint32_t>(query_value); - windowInfo.bufferCount = min_undequeued_buffers - + std::max(VulkanSurface::sTargetBufferCount, caps.minImageCount); + windowInfo.bufferCount = min_undequeued_buffers + + std::max(sTargetBufferCount + extraBuffers, caps.minImageCount); if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) { // Application must settle for fewer images than desired: windowInfo.bufferCount = caps.maxImageCount; @@ -241,8 +236,7 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, // Currently Skia requires the images to be color attachments and support all transfer // operations. VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; LOG_ALWAYS_FATAL_IF((caps.supportedUsageFlags & usageFlags) != usageFlags); @@ -336,7 +330,8 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window err = native_window_set_buffers_data_space(window, windowInfo.dataspace); if (err != 0) { ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_data_space(%d) " - "failed: %s (%d)", windowInfo.dataspace, strerror(-err), err); + "failed: %s (%d)", + windowInfo.dataspace, strerror(-err), err); return false; } @@ -344,7 +339,8 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window err = native_window_set_buffers_dimensions(window, size.width(), size.height()); if (err != 0) { ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) " - "failed: %s (%d)", size.width(), size.height(), strerror(-err), err); + "failed: %s (%d)", + size.width(), size.height(), strerror(-err), err); return false; } @@ -357,7 +353,8 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window err = native_window_set_buffers_transform(window, InvertTransform(windowInfo.transform)); if (err != 0) { ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_transform(%d) " - "failed: %s (%d)", windowInfo.transform, strerror(-err), err); + "failed: %s (%d)", + windowInfo.transform, strerror(-err), err); return false; } @@ -366,7 +363,8 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE); if (err != 0) { ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) " - "failed: %s (%d)", strerror(-err), err); + "failed: %s (%d)", + strerror(-err), err); return false; } @@ -388,12 +386,12 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window } VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, - SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext) + SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext) : mNativeWindow(window) , mWindowInfo(windowInfo) , mGrContext(grContext) , mMinWindowSize(minWindowSize) - , mMaxWindowSize(maxWindowSize) { } + , mMaxWindowSize(maxWindowSize) {} VulkanSurface::~VulkanSurface() { releaseBuffers(); @@ -436,8 +434,7 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { // value at the end of the function if everything dequeued correctly. mCurrentBufferInfo = nullptr; - - //check if the native window has been resized or rotated and update accordingly + // check if the native window has been resized or rotated and update accordingly SkISize newSize = SkISize::MakeEmpty(); int transformHint = 0; mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth); @@ -457,8 +454,8 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { newWindowInfo.actualSize.height()); if (err != 0) { ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)", - newWindowInfo.actualSize.width(), - newWindowInfo.actualSize.height(), strerror(-err), err); + newWindowInfo.actualSize.width(), newWindowInfo.actualSize.height(), + strerror(-err), err); return nullptr; } // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The @@ -469,7 +466,7 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { if (newWindowInfo.transform != mWindowInfo.transform) { err = native_window_set_buffers_transform(mNativeWindow.get(), - InvertTransform(newWindowInfo.transform)); + InvertTransform(newWindowInfo.transform)); if (err != 0) { ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", newWindowInfo.transform, strerror(-err), err); @@ -512,11 +509,9 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { VulkanSurface::NativeBufferInfo* bufferInfo = &mNativeBuffers[idx]; if (bufferInfo->skSurface.get() == nullptr) { - bufferInfo->skSurface = - SkSurface::MakeFromAHardwareBuffer(mGrContext, - ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()), - kTopLeft_GrSurfaceOrigin, DataSpaceToColorSpace(mWindowInfo.dataspace), - nullptr); + bufferInfo->skSurface = SkSurface::MakeFromAHardwareBuffer( + mGrContext, ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()), + kTopLeft_GrSurfaceOrigin, DataSpaceToColorSpace(mWindowInfo.dataspace), nullptr); if (bufferInfo->skSurface.get() == nullptr) { ALOGE("SkSurface::MakeFromAHardwareBuffer failed"); mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd); @@ -530,19 +525,19 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { bool VulkanSurface::presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd) { if (!dirtyRect.isEmpty()) { - SkRect transformedRect; - mWindowInfo.preTransform.mapRect(&transformedRect, dirtyRect); - SkIRect transformedIRect; - transformedRect.roundOut(&transformedIRect); - transformedIRect.intersect(0, 0, mWindowInfo.size.fWidth, mWindowInfo.size.fHeight); + // native_window_set_surface_damage takes a rectangle in prerotated space + // with a bottom-left origin. That is, top > bottom. + // The dirtyRect is also in prerotated space, so we just need to switch it to + // a bottom-left origin space. - // map to bottom-left coordinate system + SkIRect irect; + dirtyRect.roundOut(&irect); android_native_rect_t aRect; - aRect.left = transformedIRect.x(); - aRect.top = mWindowInfo.size.fHeight - (transformedIRect.y() + transformedIRect.height()); - aRect.right = aRect.left + transformedIRect.width(); - aRect.bottom = aRect.top - transformedIRect.height(); + aRect.left = irect.left(); + aRect.top = logicalHeight() - irect.top(); + aRect.right = irect.right(); + aRect.bottom = logicalHeight() - irect.bottom(); int err = native_window_set_surface_damage(mNativeWindow.get(), &aRect, 1); ALOGE_IF(err != 0, "native_window_set_surface_damage failed: %s (%d)", strerror(-err), err); diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index 305483fce2d5..b7af596ae762 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -19,8 +19,8 @@ #include <system/window.h> #include <vulkan/vulkan.h> -#include <SkSize.h> #include <SkRefCnt.h> +#include <SkSize.h> #include "IRenderPipeline.h" @@ -34,12 +34,9 @@ class VulkanManager; class VulkanSurface { public: - static VulkanSurface* Create(ANativeWindow* window, - ColorMode colorMode, - SkColorType colorType, - sk_sp<SkColorSpace> colorSpace, - GrContext* grContext, - const VulkanManager& vkManager); + static VulkanSurface* Create(ANativeWindow* window, ColorMode colorMode, SkColorType colorType, + sk_sp<SkColorSpace> colorSpace, GrContext* grContext, + const VulkanManager& vkManager, uint32_t extraBuffers); ~VulkanSurface(); sk_sp<SkSurface> getCurrentSkSurface() { @@ -104,15 +101,10 @@ private: SkMatrix preTransform; }; - VulkanSurface(ANativeWindow* window, - const WindowInfo& windowInfo, - SkISize minWindowSize, - SkISize maxWindowSize, - GrContext* grContext); - static bool UpdateWindow(ANativeWindow* window, - const WindowInfo& windowInfo); - static void ComputeWindowSizeAndTransform(WindowInfo* windowInfo, - const SkISize& minSize, + VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, SkISize minWindowSize, + SkISize maxWindowSize, GrContext* grContext); + static bool UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo); + static void ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize, const SkISize& maxSize); void releaseBuffers(); diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 1b4cf7e144bd..6fb164a99ae4 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -23,6 +23,7 @@ #include "pipeline/skia/GLFunctorDrawable.h" #include "pipeline/skia/SkiaDisplayList.h" #include "renderthread/CanvasContext.h" +#include "tests/common/TestContext.h" #include "tests/common/TestUtils.h" using namespace android; @@ -50,13 +51,13 @@ TEST(SkiaDisplayList, reset) { GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas); skiaDL->mChildFunctors.push_back(&functorDrawable); skiaDL->mMutableImages.push_back(nullptr); - skiaDL->mVectorDrawables.push_back(nullptr); + skiaDL->appendVD(nullptr); skiaDL->mProjectionReceiver = &drawable; ASSERT_FALSE(skiaDL->mChildNodes.empty()); ASSERT_FALSE(skiaDL->mChildFunctors.empty()); ASSERT_FALSE(skiaDL->mMutableImages.empty()); - ASSERT_FALSE(skiaDL->mVectorDrawables.empty()); + ASSERT_TRUE(skiaDL->hasVectorDrawables()); ASSERT_FALSE(skiaDL->isEmpty()); ASSERT_TRUE(skiaDL->mProjectionReceiver); @@ -65,7 +66,7 @@ TEST(SkiaDisplayList, reset) { ASSERT_TRUE(skiaDL->mChildNodes.empty()); ASSERT_TRUE(skiaDL->mChildFunctors.empty()); ASSERT_TRUE(skiaDL->mMutableImages.empty()); - ASSERT_TRUE(skiaDL->mVectorDrawables.empty()); + ASSERT_FALSE(skiaDL->hasVectorDrawables()); ASSERT_TRUE(skiaDL->isEmpty()); ASSERT_FALSE(skiaDL->mProjectionReceiver); } @@ -110,7 +111,7 @@ TEST(SkiaDisplayList, syncContexts) { SkRect bounds = SkRect::MakeWH(200, 200); VectorDrawableRoot vectorDrawable(new VectorDrawable::Group()); vectorDrawable.mutateStagingProperties()->setBounds(bounds); - skiaDL.mVectorDrawables.push_back(&vectorDrawable); + skiaDL.appendVD(&vectorDrawable); // ensure that the functor and vectorDrawable are properly synced TestUtils::runOnRenderThread([&](auto&) { @@ -149,9 +150,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { SkiaDisplayList skiaDL; + // The VectorDrawableRoot needs to have bounds on screen (and therefore not + // empty) in order to have PropertyChangeWillBeConsumed set. + const auto bounds = SkRect::MakeIWH(100, 100); + // prepare with a clean VD VectorDrawableRoot cleanVD(new VectorDrawable::Group()); - skiaDL.mVectorDrawables.push_back(&cleanVD); + cleanVD.mutateProperties()->setBounds(bounds); + skiaDL.appendVD(&cleanVD); cleanVD.getBitmapUpdateIfDirty(); // this clears the dirty bit ASSERT_FALSE(cleanVD.isDirty()); @@ -159,11 +165,12 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { TestUtils::MockTreeObserver observer; ASSERT_FALSE(skiaDL.prepareListAndChildren(observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); - ASSERT_TRUE(cleanVD.getPropertyChangeWillBeConsumed()); + ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed()); // prepare again this time adding a dirty VD VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); - skiaDL.mVectorDrawables.push_back(&dirtyVD); + dirtyVD.mutateProperties()->setBounds(bounds); + skiaDL.appendVD(&dirtyVD); ASSERT_TRUE(dirtyVD.isDirty()); ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); @@ -191,6 +198,169 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { canvasContext->destroy(); } +RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) { + auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); + ContextFactory contextFactory; + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory)); + + // Set up a Surface so that we can position the VectorDrawable offscreen. + test::TestContext testContext; + testContext.setRenderOffscreen(true); + auto surface = testContext.surface(); + int width, height; + surface->query(NATIVE_WINDOW_WIDTH, &width); + surface->query(NATIVE_WINDOW_HEIGHT, &height); + canvasContext->setSurface(std::move(surface)); + + TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get()); + DamageAccumulator damageAccumulator; + info.damageAccumulator = &damageAccumulator; + + // The VectorDrawableRoot needs to have bounds on screen (and therefore not + // empty) in order to have PropertyChangeWillBeConsumed set. + const auto bounds = SkRect::MakeIWH(100, 100); + + for (const SkRect b : {bounds.makeOffset(width, 0), + bounds.makeOffset(0, height), + bounds.makeOffset(-bounds.width(), 0), + bounds.makeOffset(0, -bounds.height())}) { + SkiaDisplayList skiaDL; + VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); + dirtyVD.mutateProperties()->setBounds(b); + skiaDL.appendVD(&dirtyVD); + + ASSERT_TRUE(dirtyVD.isDirty()); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + + TestUtils::MockTreeObserver observer; + ASSERT_FALSE(skiaDL.prepareListAndChildren( + observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + } + + // The DamageAccumulator's transform can also result in the + // VectorDrawableRoot being offscreen. + for (const SkISize translate : { SkISize{width, 0}, + SkISize{0, height}, + SkISize{-width, 0}, + SkISize{0, -height}}) { + Matrix4 mat4; + mat4.translate(translate.fWidth, translate.fHeight); + damageAccumulator.pushTransform(&mat4); + + SkiaDisplayList skiaDL; + VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); + dirtyVD.mutateProperties()->setBounds(bounds); + skiaDL.appendVD(&dirtyVD); + + ASSERT_TRUE(dirtyVD.isDirty()); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + + TestUtils::MockTreeObserver observer; + ASSERT_FALSE(skiaDL.prepareListAndChildren( + observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + damageAccumulator.popTransform(); + } + + // Another way to be offscreen: a matrix from the draw call. + for (const SkMatrix translate : { SkMatrix::MakeTrans(width, 0), + SkMatrix::MakeTrans(0, height), + SkMatrix::MakeTrans(-width, 0), + SkMatrix::MakeTrans(0, -height)}) { + SkiaDisplayList skiaDL; + VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); + dirtyVD.mutateProperties()->setBounds(bounds); + skiaDL.appendVD(&dirtyVD, translate); + + ASSERT_TRUE(dirtyVD.isDirty()); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + + TestUtils::MockTreeObserver observer; + ASSERT_FALSE(skiaDL.prepareListAndChildren( + observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + } + + // Verify that the matrices are combined in the right order. + { + // Rotate and then translate, so the VD is offscreen. + Matrix4 mat4; + mat4.loadRotate(180); + damageAccumulator.pushTransform(&mat4); + + SkiaDisplayList skiaDL; + VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); + dirtyVD.mutateProperties()->setBounds(bounds); + SkMatrix translate = SkMatrix::MakeTrans(50, 50); + skiaDL.appendVD(&dirtyVD, translate); + + ASSERT_TRUE(dirtyVD.isDirty()); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + + TestUtils::MockTreeObserver observer; + ASSERT_FALSE(skiaDL.prepareListAndChildren( + observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + damageAccumulator.popTransform(); + } + { + // Switch the order of rotate and translate, so it is on screen. + Matrix4 mat4; + mat4.translate(50, 50); + damageAccumulator.pushTransform(&mat4); + + SkiaDisplayList skiaDL; + VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); + dirtyVD.mutateProperties()->setBounds(bounds); + SkMatrix rotate; + rotate.setRotate(180); + skiaDL.appendVD(&dirtyVD, rotate); + + ASSERT_TRUE(dirtyVD.isDirty()); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + + TestUtils::MockTreeObserver observer; + ASSERT_TRUE(skiaDL.prepareListAndChildren( + observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); + ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed()); + damageAccumulator.popTransform(); + } + { + // An AVD that is larger than the screen. + SkiaDisplayList skiaDL; + VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); + dirtyVD.mutateProperties()->setBounds(SkRect::MakeLTRB(-1, -1, width + 1, height + 1)); + skiaDL.appendVD(&dirtyVD); + + ASSERT_TRUE(dirtyVD.isDirty()); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + + TestUtils::MockTreeObserver observer; + ASSERT_TRUE(skiaDL.prepareListAndChildren( + observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); + ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed()); + } + { + // An AVD whose bounds are not a rectangle after applying a matrix. + SkiaDisplayList skiaDL; + VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); + dirtyVD.mutateProperties()->setBounds(bounds); + SkMatrix mat; + mat.setRotate(45, 50, 50); + skiaDL.appendVD(&dirtyVD, mat); + + ASSERT_TRUE(dirtyVD.isDirty()); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + + TestUtils::MockTreeObserver observer; + ASSERT_TRUE(skiaDL.prepareListAndChildren( + observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); + ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed()); + } +} + TEST(SkiaDisplayList, updateChildren) { SkiaDisplayList skiaDL; diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index e86cf42fee4d..a671bdada09a 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -31,6 +31,9 @@ #include "renderthread/CanvasContext.h" #include "tests/common/TestUtils.h" +#include <gui/BufferItemConsumer.h> +#include <gui/Surface.h> + using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::renderthread; @@ -421,10 +424,20 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { EXPECT_EQ(1, surface->canvas()->mDrawCounter); } +static sp<Surface> createDummySurface() { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + producer->setMaxDequeuedBufferCount(1); + producer->setAsyncMode(true); + return new Surface(producer); +} + RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { + auto surface = createDummySurface(); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); EXPECT_FALSE(pipeline->isSurfaceReady()); - EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::SRGB)); + EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, ColorMode::SRGB, 0)); EXPECT_TRUE(pipeline->isSurfaceReady()); renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); diff --git a/media/Android.bp b/media/Android.bp index 8746046c220d..70dacb20cde7 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -20,10 +20,6 @@ java_library { ], }, - static_libs: [ - "mediaplayer2-protos", - ], - permitted_packages: [ "android.media", ], @@ -43,7 +39,6 @@ filegroup { name: "updatable-media-srcs", srcs: [ ":mediasession2-srcs", - ":mediaplayer2-srcs", ], } @@ -51,7 +46,6 @@ filegroup { name: "updatable-media-srcs-without-aidls", srcs : [ ":mediasession2-srcs-without-aidls", - ":mediaplayer2-srcs", ], } @@ -64,7 +58,6 @@ filegroup { "apex/java/android/media/IMediaSession2Service.aidl", "apex/java/android/media/MediaConstants.java", "apex/java/android/media/MediaController2.java", - "apex/java/android/media/MediaItem2.java", "apex/java/android/media/MediaSession2.java", "apex/java/android/media/MediaSession2Service.java", "apex/java/android/media/Session2Command.java", @@ -110,8 +103,7 @@ metalava_updatable_media_args = " --error UnhiddenSystemApi " + "--hide MissingPermission --hide BroadcastBehavior " + "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " + "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " + - "--hide HiddenTypedefConstant --show-annotation android.annotation.SystemApi " + - " --show-annotation android.annotation.TestApi " + "--hide HiddenTypedefConstant --show-annotation android.annotation.SystemApi " droidstubs { name: "updatable-media-stubs", diff --git a/media/apex/java/android/media/BufferingParams.java b/media/apex/java/android/media/BufferingParams.java index 83594d474b37..943f14277fc3 100644 --- a/media/apex/java/android/media/BufferingParams.java +++ b/media/apex/java/android/media/BufferingParams.java @@ -17,7 +17,6 @@ package android.media; import android.annotation.IntDef; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -64,7 +63,6 @@ import java.lang.annotation.RetentionPolicy; * <p>Users should use {@link Builder} to change {@link BufferingParams}. * @hide */ -@TestApi public final class BufferingParams implements Parcelable { private static final int BUFFERING_NO_MARK = -1; diff --git a/media/apex/java/android/media/MediaItem2.java b/media/apex/java/android/media/MediaItem2.java deleted file mode 100644 index ff0d43e41350..000000000000 --- a/media/apex/java/android/media/MediaItem2.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media; - -import static android.media.MediaMetadata.METADATA_KEY_MEDIA_ID; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; -import android.util.Log; -import android.util.Pair; - -import com.android.internal.annotations.GuardedBy; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executor; - -/** - * A class with information on a single media item with the metadata information. - * <p> - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. - * <p> - */ -public final class MediaItem2 implements Parcelable { - private static final String TAG = "MediaItem2"; - - // intentionally less than long.MAX_VALUE. - // Declare this first to avoid 'illegal forward reference'. - static final long LONG_MAX = 0x7ffffffffffffffL; - - /** - * Used when a position is unknown. - * - * @see #getEndPosition() - */ - public static final long POSITION_UNKNOWN = LONG_MAX; - - public static final @android.annotation.NonNull Parcelable.Creator<MediaItem2> CREATOR = - new Parcelable.Creator<MediaItem2>() { - @Override - public MediaItem2 createFromParcel(Parcel in) { - return new MediaItem2(in); - } - - @Override - public MediaItem2[] newArray(int size) { - return new MediaItem2[size]; - } - }; - - private static final long UNKNOWN_TIME = -1; - - private final long mStartPositionMs; - private final long mEndPositionMs; - - private final Object mLock = new Object(); - - @GuardedBy("mLock") - private MediaMetadata mMetadata; - @GuardedBy("mLock") - private final List<Pair<OnMetadataChangedListener, Executor>> mListeners = new ArrayList<>(); - - /** - * Used by {@link MediaItem2.Builder}. - */ - // Note: Needs to be protected when we want to allow 3rd party player to define customized - // MediaItem2. - @SuppressWarnings("WeakerAccess") /* synthetic access */ - MediaItem2(Builder builder) { - this(builder.mMetadata, builder.mStartPositionMs, builder.mEndPositionMs); - } - - /** - * Used by Parcelable.Creator. - */ - // Note: Needs to be protected when we want to allow 3rd party player to define customized - // MediaItem2. - @SuppressWarnings("WeakerAccess") /* synthetic access */ - MediaItem2(Parcel in) { - this(in.readParcelable(MediaItem2.class.getClassLoader()), in.readLong(), in.readLong()); - } - - @SuppressWarnings("WeakerAccess") /* synthetic access */ - MediaItem2(MediaItem2 item) { - this(item.mMetadata, item.mStartPositionMs, item.mEndPositionMs); - } - - @SuppressWarnings("WeakerAccess") /* synthetic access */ - MediaItem2(@Nullable MediaMetadata metadata, long startPositionMs, long endPositionMs) { - if (startPositionMs > endPositionMs) { - throw new IllegalArgumentException("Illegal start/end position: " - + startPositionMs + " : " + endPositionMs); - } - if (metadata != null && metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { - long durationMs = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION); - if (durationMs != UNKNOWN_TIME && endPositionMs != POSITION_UNKNOWN - && endPositionMs > durationMs) { - throw new IllegalArgumentException("endPositionMs shouldn't be greater than" - + " duration in the metdata, endPositionMs=" + endPositionMs - + ", durationMs=" + durationMs); - } - } - mMetadata = metadata; - mStartPositionMs = startPositionMs; - mEndPositionMs = endPositionMs; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(getClass().getSimpleName()); - synchronized (mLock) { - sb.append("{mMetadata=").append(mMetadata); - sb.append(", mStartPositionMs=").append(mStartPositionMs); - sb.append(", mEndPositionMs=").append(mEndPositionMs); - sb.append('}'); - } - return sb.toString(); - } - - /** - * Sets metadata. If the metadata is not {@code null}, its id should be matched with this - * instance's media id. - * - * @param metadata metadata to update - * @see MediaMetadata#METADATA_KEY_MEDIA_ID - */ - public void setMetadata(@Nullable MediaMetadata metadata) { - List<Pair<OnMetadataChangedListener, Executor>> listeners = new ArrayList<>(); - synchronized (mLock) { - if (mMetadata != null && metadata != null - && !TextUtils.equals(getMediaId(), metadata.getString(METADATA_KEY_MEDIA_ID))) { - Log.d(TAG, "MediaItem2's media ID shouldn't be changed"); - return; - } - mMetadata = metadata; - listeners.addAll(mListeners); - } - - for (Pair<OnMetadataChangedListener, Executor> pair : listeners) { - final OnMetadataChangedListener listener = pair.first; - pair.second.execute(new Runnable() { - @Override - public void run() { - listener.onMetadataChanged(MediaItem2.this); - } - }); - } - } - - /** - * Gets the metadata of the media. - * - * @return metadata from the session - */ - public @Nullable MediaMetadata getMetadata() { - synchronized (mLock) { - return mMetadata; - } - } - - /** - * Return the position in milliseconds at which the playback will start. - * @return the position in milliseconds at which the playback will start - */ - public long getStartPosition() { - return mStartPositionMs; - } - - /** - * Return the position in milliseconds at which the playback will end. - * {@link #POSITION_UNKNOWN} means ending at the end of source content. - * @return the position in milliseconds at which the playback will end - */ - public long getEndPosition() { - return mEndPositionMs; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(mMetadata, 0); - dest.writeLong(mStartPositionMs); - dest.writeLong(mEndPositionMs); - } - - /** - * Gets the media id for this item. If it's not {@code null}, it's a persistent unique key - * for the underlying media content. - * - * @return media Id from the session - */ - @Nullable String getMediaId() { - synchronized (mLock) { - return mMetadata != null - ? mMetadata.getString(METADATA_KEY_MEDIA_ID) : null; - } - } - - void addOnMetadataChangedListener(Executor executor, OnMetadataChangedListener listener) { - synchronized (mLock) { - for (Pair<OnMetadataChangedListener, Executor> pair : mListeners) { - if (pair.first == listener) { - return; - } - } - mListeners.add(new Pair<>(listener, executor)); - } - } - - void removeOnMetadataChangedListener(OnMetadataChangedListener listener) { - synchronized (mLock) { - for (int i = mListeners.size() - 1; i >= 0; i--) { - if (mListeners.get(i).first == listener) { - mListeners.remove(i); - return; - } - } - } - } - - /** - * Builder for {@link MediaItem2}. - */ - public static final class Builder { - @SuppressWarnings("WeakerAccess") /* synthetic access */ - MediaMetadata mMetadata; - @SuppressWarnings("WeakerAccess") /* synthetic access */ - long mStartPositionMs = 0; - @SuppressWarnings("WeakerAccess") /* synthetic access */ - long mEndPositionMs = POSITION_UNKNOWN; - - /** - * Set the metadata of this instance. {@code null} for unset. - * - * @param metadata metadata - * @return this instance for chaining - */ - public @NonNull Builder setMetadata(@Nullable MediaMetadata metadata) { - mMetadata = metadata; - return this; - } - - /** - * Sets the start position in milliseconds at which the playback will start. - * Any negative number is treated as 0. - * - * @param position the start position in milliseconds at which the playback will start - * @return the same Builder instance. - */ - public @NonNull Builder setStartPosition(long position) { - if (position < 0) { - position = 0; - } - mStartPositionMs = position; - return this; - } - - /** - * Sets the end position in milliseconds at which the playback will end. - * Any negative number is treated as maximum length of the media item. - * - * @param position the end position in milliseconds at which the playback will end - * @return the same Builder instance. - */ - public @NonNull Builder setEndPosition(long position) { - if (position < 0) { - position = POSITION_UNKNOWN; - } - mEndPositionMs = position; - return this; - } - - /** - * Build {@link MediaItem2}. - * - * @return a new {@link MediaItem2}. - */ - public @NonNull MediaItem2 build() { - return new MediaItem2(this); - } - } - - interface OnMetadataChangedListener { - void onMetadataChanged(MediaItem2 item); - } -} diff --git a/media/apex/java/android/media/Session2Command.java b/media/apex/java/android/media/Session2Command.java index 7f73dc123063..7c752e198f3a 100644 --- a/media/apex/java/android/media/Session2Command.java +++ b/media/apex/java/android/media/Session2Command.java @@ -30,7 +30,7 @@ import java.util.Objects; * <p> * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command. * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and - * {@link #getCustomCommand()} shouldn't be {@code null}. + * {@link #getCustomAction()} shouldn't be {@code null}. * <p> * Refer to the * <a href="{@docRoot}reference/androidx/media2/SessionCommand2.html">AndroidX SessionCommand</a> @@ -63,8 +63,8 @@ public final class Session2Command implements Parcelable { private final int mCommandCode; // Nonnull if it's custom command - private final String mCustomCommand; - private final Bundle mExtras; + private final String mCustomAction; + private final Bundle mCustomExtras; /** * Constructor for creating a command predefined in AndroidX media2. @@ -76,8 +76,8 @@ public final class Session2Command implements Parcelable { throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM"); } mCommandCode = commandCode; - mCustomCommand = null; - mExtras = null; + mCustomAction = null; + mCustomExtras = null; } /** @@ -91,8 +91,8 @@ public final class Session2Command implements Parcelable { throw new IllegalArgumentException("action shouldn't be null"); } mCommandCode = COMMAND_CODE_CUSTOM; - mCustomCommand = action; - mExtras = extras; + mCustomAction = action; + mCustomExtras = extras; } /** @@ -101,8 +101,8 @@ public final class Session2Command implements Parcelable { @SuppressWarnings("WeakerAccess") /* synthetic access */ Session2Command(Parcel in) { mCommandCode = in.readInt(); - mCustomCommand = in.readString(); - mExtras = in.readBundle(); + mCustomAction = in.readString(); + mCustomExtras = in.readBundle(); } /** @@ -118,8 +118,8 @@ public final class Session2Command implements Parcelable { * This will return {@code null} for a predefined command. */ @Nullable - public String getCustomCommand() { - return mCustomCommand; + public String getCustomAction() { + return mCustomAction; } /** @@ -127,8 +127,8 @@ public final class Session2Command implements Parcelable { * This will return {@code null} for a predefined command. */ @Nullable - public Bundle getExtras() { - return mExtras; + public Bundle getCustomExtras() { + return mCustomExtras; } @Override @@ -142,8 +142,8 @@ public final class Session2Command implements Parcelable { throw new IllegalArgumentException("parcel shouldn't be null"); } dest.writeInt(mCommandCode); - dest.writeString(mCustomCommand); - dest.writeBundle(mExtras); + dest.writeString(mCustomAction); + dest.writeBundle(mCustomExtras); } @Override @@ -153,12 +153,12 @@ public final class Session2Command implements Parcelable { } Session2Command other = (Session2Command) obj; return mCommandCode == other.mCommandCode - && TextUtils.equals(mCustomCommand, other.mCustomCommand); + && TextUtils.equals(mCustomAction, other.mCustomAction); } @Override public int hashCode() { - return Objects.hash(mCustomCommand, mCommandCode); + return Objects.hash(mCustomAction, mCommandCode); } /** diff --git a/media/java/android/media/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java index 3aaa7dfc05d0..ee89509951df 100644 --- a/media/java/android/media/AudioFocusInfo.java +++ b/media/java/android/media/AudioFocusInfo.java @@ -18,6 +18,7 @@ package android.media; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -27,6 +28,7 @@ import java.util.Objects; * @hide * A class to encapsulate information about an audio focus owner or request. */ +@TestApi @SystemApi public final class AudioFocusInfo implements Parcelable { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 8f699eb9e6fc..f8e43437d244 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -26,6 +26,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.NotificationManager; import android.app.PendingIntent; @@ -1512,7 +1513,22 @@ public class AudioManager { int result = AudioSystem.setAllowedCapturePolicy(Process.myUid(), flags); if (result != AudioSystem.AUDIO_STATUS_OK) { Log.e(TAG, "Could not setAllowedCapturePolicy: " + result); + return; } + mCapturePolicy = capturePolicy; + } + + @AudioAttributes.CapturePolicy + private int mCapturePolicy = AudioAttributes.ALLOW_CAPTURE_BY_ALL; + + /** + * Return the capture policy. + * @return the capture policy set by {@link #setAllowedCapturePolicy(int)} or + * the default if it was not called. + */ + @AudioAttributes.CapturePolicy + public int getAllowedCapturePolicy() { + return mCapturePolicy; } //==================================================================== @@ -3035,6 +3051,7 @@ public class AudioManager { * @param requestResult the result to the focus request to be passed to the requester * @param ap a valid registered {@link AudioPolicy} configured as a focus policy. */ + @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull AudioFocusInfo afi, @@ -3074,6 +3091,7 @@ public class AudioManager { * if there was an error sending the request. * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null. */ + @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange, @@ -3336,6 +3354,7 @@ public class AudioManager { * {@link android.Manifest.permission#MODIFY_AUDIO_ROUTING} permission, * {@link #SUCCESS} otherwise. */ + @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull AudioPolicy policy) { @@ -3370,6 +3389,7 @@ public class AudioManager { * Unregisters an {@link AudioPolicy} asynchronously. * @param policy the non-null {@link AudioPolicy} to unregister. */ + @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull AudioPolicy policy) { @@ -3396,6 +3416,7 @@ public class AudioManager { * associated with mixes of this policy. * @param policy the non-null {@link AudioPolicy} to unregister. */ + @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull AudioPolicy policy) { @@ -3410,6 +3431,20 @@ public class AudioManager { } } + /** + * @hide + * @return true if an AudioPolicy was previously registered + */ + @TestApi + public boolean hasRegisteredDynamicPolicy() { + final IAudioService service = getService(); + try { + return service.hasRegisteredDynamicPolicy(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + //==================================================================== // Notification of playback activity & playback configuration /** @@ -4488,6 +4523,7 @@ public class AudioManager { */ /** @hide */ + @TestApi @SystemApi public static final int SUCCESS = AudioSystem.SUCCESS; /** diff --git a/media/java/android/media/AudioPlaybackCaptureConfiguration.java b/media/java/android/media/AudioPlaybackCaptureConfiguration.java index d61f14c25c7e..fe5005a7cc9a 100644 --- a/media/java/android/media/AudioPlaybackCaptureConfiguration.java +++ b/media/java/android/media/AudioPlaybackCaptureConfiguration.java @@ -20,11 +20,14 @@ import android.annotation.NonNull; import android.media.AudioAttributes.AttributeUsage; import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioMixingRule; +import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion; import android.media.projection.MediaProjection; import android.os.RemoteException; import com.android.internal.util.Preconditions; +import java.util.function.ToIntFunction; + /** * Configuration for capturing audio played by other apps. * @@ -83,6 +86,39 @@ public final class AudioPlaybackCaptureConfiguration { return mProjection; } + /** @return the usages passed to {@link Builder#addMatchingUsage(int)}. */ + @AttributeUsage + public @NonNull int[] getMatchingUsages() { + return getIntPredicates(AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE, + criterion -> criterion.getAudioAttributes().getUsage()); + } + + /** @return the UIDs passed to {@link Builder#addMatchingUid(int)}. */ + public @NonNull int[] getMatchingUids() { + return getIntPredicates(AudioMixingRule.RULE_MATCH_UID, + criterion -> criterion.getIntProp()); + } + + /** @return the usages passed to {@link Builder#excludeUsage(int)}. */ + @AttributeUsage + public @NonNull int[] getExcludeUsages() { + return getIntPredicates(AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_USAGE, + criterion -> criterion.getAudioAttributes().getUsage()); + } + + /** @return the UIDs passed to {@link Builder#excludeUid(int)}. */ + public @NonNull int[] getExcludeUids() { + return getIntPredicates(AudioMixingRule.RULE_EXCLUDE_UID, + criterion -> criterion.getIntProp()); + } + + private int[] getIntPredicates(int rule, + ToIntFunction<AudioMixMatchCriterion> getPredicate) { + return mAudioMixingRule.getCriteria().stream() + .filter(criterion -> criterion.getRule() == rule) + .mapToInt(getPredicate) + .toArray(); + } /** * Returns a mix that routes audio back into the app while still playing it from the speakers. diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 790e189e9109..d9d614f7ead4 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -975,13 +975,9 @@ public class AudioTrack extends PlayerBase throw new UnsupportedOperationException( "Offload and low latency modes are incompatible"); } - if (mAttributes.getUsage() != AudioAttributes.USAGE_MEDIA) { - throw new UnsupportedOperationException( - "Cannot create AudioTrack, offload requires USAGE_MEDIA"); - } if (!AudioSystem.isOffloadSupported(mFormat, mAttributes)) { throw new UnsupportedOperationException( - "Cannot create AudioTrack, offload format not supported"); + "Cannot create AudioTrack, offload format / attributes not supported"); } } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index eddbee46252e..85de00a2a989 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -211,6 +211,8 @@ interface IAudioService { void setVolumePolicy(in VolumePolicy policy); + boolean hasRegisteredDynamicPolicy(); + void registerRecordingCallback(in IRecordingConfigDispatcher rcdb); oneway void unregisterRecordingCallback(in IRecordingConfigDispatcher rcdb); diff --git a/media/java/android/media/MiniThumbFile.java b/media/java/android/media/MiniThumbFile.java deleted file mode 100644 index f704acde7cf7..000000000000 --- a/media/java/android/media/MiniThumbFile.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (C) 2009 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.media; - -import android.annotation.UnsupportedAppUsage; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.util.Log; - -import dalvik.system.VMRuntime; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.util.Hashtable; - -/** - * This class handles the mini-thumb file. A mini-thumb file consists - * of blocks, indexed by id. Each block has BYTES_PER_MINTHUMB bytes in the - * following format: - * - * 1 byte status (0 = empty, 1 = mini-thumb available) - * 8 bytes magic (a magic number to match what's in the database) - * 4 bytes data length (LEN) - * LEN bytes jpeg data - * (the remaining bytes are unused) - * - * @hide This file is shared between MediaStore and MediaProvider and should remained internal use - * only. - * @deprecated thumbnails are now maintained in separate files, and this file - * format is no longer used. - */ -@Deprecated -public class MiniThumbFile { - private static final String TAG = "MiniThumbFile"; - private static final int MINI_THUMB_DATA_FILE_VERSION = 4; - public static final int BYTES_PER_MINTHUMB = 10000; - private static final int HEADER_SIZE = 1 + 8 + 4; - private Uri mUri; - private RandomAccessFile mMiniThumbFile; - private FileChannel mChannel; - private ByteBuffer mBuffer; - private ByteBuffer mEmptyBuffer; - private static final Hashtable<String, MiniThumbFile> sThumbFiles = - new Hashtable<String, MiniThumbFile>(); - - /** - * We store different types of thumbnails in different files. To remain backward compatibility, - * we should hashcode of content://media/external/images/media remains the same. - */ - @UnsupportedAppUsage - public static synchronized void reset() { - for (MiniThumbFile file : sThumbFiles.values()) { - file.deactivate(); - } - sThumbFiles.clear(); - } - - public static synchronized MiniThumbFile instance(Uri uri) { - if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) { - throw new UnsupportedOperationException(); - } - String type = uri.getPathSegments().get(1); - MiniThumbFile file = sThumbFiles.get(type); - // Log.v(TAG, "get minithumbfile for type: "+type); - if (file == null) { - file = new MiniThumbFile( - Uri.parse("content://media/external/" + type + "/media")); - sThumbFiles.put(type, file); - } - - return file; - } - - private String randomAccessFilePath(int version) { - String directoryName = - Environment.getExternalStorageDirectory().toString() - + "/DCIM/.thumbnails"; - return directoryName + "/.thumbdata" + version + "-" + mUri.hashCode(); - } - - private void removeOldFile() { - String oldPath = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION - 1); - File oldFile = new File(oldPath); - if (oldFile.exists()) { - try { - oldFile.delete(); - } catch (SecurityException ex) { - // ignore - } - } - } - - private RandomAccessFile miniThumbDataFile() { - if (mMiniThumbFile == null) { - removeOldFile(); - String path = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION); - File directory = new File(path).getParentFile(); - if (!directory.isDirectory()) { - if (!directory.mkdirs()) { - Log.e(TAG, "Unable to create .thumbnails directory " - + directory.toString()); - } - } - File f = new File(path); - try { - mMiniThumbFile = new RandomAccessFile(f, "rw"); - } catch (IOException ex) { - // Open as read-only so we can at least read the existing - // thumbnails. - try { - mMiniThumbFile = new RandomAccessFile(f, "r"); - } catch (IOException ex2) { - // ignore exception - } - } - if (mMiniThumbFile != null) { - mChannel = mMiniThumbFile.getChannel(); - } - } - return mMiniThumbFile; - } - - private MiniThumbFile(Uri uri) { - mUri = uri; - mBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB); - mEmptyBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB); - } - - public synchronized void deactivate() { - if (mMiniThumbFile != null) { - try { - mMiniThumbFile.close(); - mMiniThumbFile = null; - } catch (IOException ex) { - // ignore exception - } - } - } - - // Get the magic number for the specified id in the mini-thumb file. - // Returns 0 if the magic is not available. - public synchronized long getMagic(long id) { - // check the mini thumb file for the right data. Right is - // defined as having the right magic number at the offset - // reserved for this "id". - RandomAccessFile r = miniThumbDataFile(); - if (r != null) { - long pos = id * BYTES_PER_MINTHUMB; - FileLock lock = null; - try { - mBuffer.clear(); - mBuffer.limit(1 + 8); - - lock = mChannel.lock(pos, 1 + 8, true); - // check that we can read the following 9 bytes - // (1 for the "status" and 8 for the long) - if (mChannel.read(mBuffer, pos) == 9) { - mBuffer.position(0); - if (mBuffer.get() == 1) { - return mBuffer.getLong(); - } - } - } catch (IOException ex) { - Log.v(TAG, "Got exception checking file magic: ", ex); - } catch (RuntimeException ex) { - // Other NIO related exception like disk full, read only channel..etc - Log.e(TAG, "Got exception when reading magic, id = " + id + - ", disk full or mount read-only? " + ex.getClass()); - } finally { - try { - if (lock != null) lock.release(); - } - catch (IOException ex) { - // ignore it. - } - } - } - return 0; - } - - public synchronized void eraseMiniThumb(long id) { - RandomAccessFile r = miniThumbDataFile(); - if (r != null) { - long pos = id * BYTES_PER_MINTHUMB; - FileLock lock = null; - try { - mBuffer.clear(); - mBuffer.limit(1 + 8); - - lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false); - // check that we can read the following 9 bytes - // (1 for the "status" and 8 for the long) - if (mChannel.read(mBuffer, pos) == 9) { - mBuffer.position(0); - if (mBuffer.get() == 1) { - long currentMagic = mBuffer.getLong(); - if (currentMagic == 0) { - // there is no thumbnail stored here - Log.i(TAG, "no thumbnail for id " + id); - return; - } - // zero out the thumbnail slot - // Log.v(TAG, "clearing slot " + id + ", magic " + currentMagic - // + " at offset " + pos); - mChannel.write(mEmptyBuffer, pos); - } - } else { - // Log.v(TAG, "No slot"); - } - } catch (IOException ex) { - Log.v(TAG, "Got exception checking file magic: ", ex); - } catch (RuntimeException ex) { - // Other NIO related exception like disk full, read only channel..etc - Log.e(TAG, "Got exception when reading magic, id = " + id + - ", disk full or mount read-only? " + ex.getClass()); - } finally { - try { - if (lock != null) lock.release(); - } - catch (IOException ex) { - // ignore it. - } - } - } else { - // Log.v(TAG, "No data file"); - } - } - - public synchronized void saveMiniThumbToFile(byte[] data, long id, long magic) - throws IOException { - RandomAccessFile r = miniThumbDataFile(); - if (r == null) return; - - long pos = id * BYTES_PER_MINTHUMB; - FileLock lock = null; - try { - if (data != null) { - if (data.length > BYTES_PER_MINTHUMB - HEADER_SIZE) { - // not enough space to store it. - return; - } - mBuffer.clear(); - mBuffer.put((byte) 1); - mBuffer.putLong(magic); - mBuffer.putInt(data.length); - mBuffer.put(data); - mBuffer.flip(); - - lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false); - mChannel.write(mBuffer, pos); - } - } catch (IOException ex) { - Log.e(TAG, "couldn't save mini thumbnail data for " - + id + "; ", ex); - throw ex; - } catch (RuntimeException ex) { - // Other NIO related exception like disk full, read only channel..etc - Log.e(TAG, "couldn't save mini thumbnail data for " - + id + "; disk full or mount read-only? " + ex.getClass()); - } finally { - try { - if (lock != null) lock.release(); - } - catch (IOException ex) { - // ignore it. - } - } - } - - /** - * Gallery app can use this method to retrieve mini-thumbnail. Full size - * images share the same IDs with their corresponding thumbnails. - * - * @param id the ID of the image (same of full size image). - * @param data the buffer to store mini-thumbnail. - */ - public synchronized byte [] getMiniThumbFromFile(long id, byte [] data) { - RandomAccessFile r = miniThumbDataFile(); - if (r == null) return null; - - long pos = id * BYTES_PER_MINTHUMB; - FileLock lock = null; - try { - mBuffer.clear(); - lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, true); - int size = mChannel.read(mBuffer, pos); - if (size > 1 + 8 + 4) { // flag, magic, length - mBuffer.position(0); - byte flag = mBuffer.get(); - long magic = mBuffer.getLong(); - int length = mBuffer.getInt(); - - if (size >= 1 + 8 + 4 + length && length != 0 && magic != 0 && flag == 1 && - data.length >= length) { - mBuffer.get(data, 0, length); - return data; - } - } - } catch (IOException ex) { - Log.w(TAG, "got exception when reading thumbnail id=" + id + ", exception: " + ex); - } catch (RuntimeException ex) { - // Other NIO related exception like disk full, read only channel..etc - Log.e(TAG, "Got exception when reading thumbnail, id = " + id + - ", disk full or mount read-only? " + ex.getClass()); - } finally { - try { - if (lock != null) lock.release(); - } - catch (IOException ex) { - // ignore it. - } - } - return null; - } -} diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index fefb0d7a55bf..e207721f1918 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -495,15 +495,17 @@ public class RingtoneManager { if (mCursor == null || !mCursor.moveToPosition(position)) { return null; } - - return getUriFromCursor(mCursor); + + return getUriFromCursor(mContext, mCursor); } - private static Uri getUriFromCursor(Cursor cursor) { - return ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)), cursor - .getLong(ID_COLUMN_INDEX)); + private static Uri getUriFromCursor(Context context, Cursor cursor) { + final Uri uri = ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)), + cursor.getLong(ID_COLUMN_INDEX)); + final Uri canonicalized = context.getContentResolver().canonicalize(uri); + return (canonicalized != null) ? canonicalized : uri; } - + /** * Gets the position of a {@link Uri} within this {@link RingtoneManager}. * @@ -569,7 +571,7 @@ public class RingtoneManager { Uri uri = null; if (cursor.moveToFirst()) { - uri = getUriFromCursor(cursor); + uri = getUriFromCursor(context, cursor); } cursor.close(); diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java index 09f17c099609..2f03d26830f2 100644 --- a/media/java/android/media/audiopolicy/AudioMix.java +++ b/media/java/android/media/audiopolicy/AudioMix.java @@ -19,6 +19,7 @@ package android.media.audiopolicy; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.media.AudioDeviceInfo; import android.media.AudioFormat; @@ -31,6 +32,7 @@ import java.util.Objects; /** * @hide */ +@TestApi @SystemApi public class AudioMix { diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java index 947b06cdf6b2..ed2fdae71fb0 100644 --- a/media/java/android/media/audiopolicy/AudioMixingRule.java +++ b/media/java/android/media/audiopolicy/AudioMixingRule.java @@ -18,6 +18,7 @@ package android.media.audiopolicy; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.media.AudioAttributes; import android.os.Parcel; @@ -41,6 +42,7 @@ import java.util.Objects; * .build(); * </pre> */ +@TestApi @SystemApi public class AudioMixingRule { @@ -92,7 +94,8 @@ public class AudioMixingRule { public static final int RULE_EXCLUDE_UID = RULE_EXCLUSION_MASK | RULE_MATCH_UID; - static final class AudioMixMatchCriterion { + /** @hide */ + public static final class AudioMixMatchCriterion { @UnsupportedAppUsage final AudioAttributes mAttr; @UnsupportedAppUsage @@ -137,6 +140,10 @@ public class AudioMixingRule { dest.writeInt(-1); } } + + public AudioAttributes getAudioAttributes() { return mAttr; } + public int getIntProp() { return mIntProp; } + public int getRule() { return mRule; } } boolean isAffectingUsage(int usage) { @@ -163,7 +170,8 @@ public class AudioMixingRule { int getTargetMixType() { return mTargetMixType; } @UnsupportedAppUsage private final ArrayList<AudioMixMatchCriterion> mCriteria; - ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; } + /** @hide */ + public ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; } @UnsupportedAppUsage private boolean mAllowPrivilegedPlaybackCapture = false; diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 00f601388164..39474e13a2d0 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageManager; @@ -55,6 +56,7 @@ import java.util.List; * @hide * AudioPolicy provides access to the management of audio routing and audio focus. */ +@TestApi @SystemApi public class AudioPolicy { @@ -237,6 +239,7 @@ public class AudioPolicy { } /** + * @hide * Test method to declare whether this audio focus policy is for test purposes only. * Having a test policy registered will disable the current focus policy and replace it * with this test policy. When unregistered, the previous focus policy will be restored. @@ -245,6 +248,7 @@ public class AudioPolicy { * @param isTestFocusPolicy true if the focus policy to register is for testing purposes. * @return the same Builder instance */ + @TestApi @NonNull public Builder setIsTestFocusPolicy(boolean isTestFocusPolicy) { mIsTestFocusPolicy = isTestFocusPolicy; @@ -412,6 +416,7 @@ public class AudioPolicy { * @param devices list of devices to which the audio stream of the application may be routed. * @return true if the change was successful, false otherwise. */ + @TestApi @SystemApi public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) { if (devices == null) { @@ -453,6 +458,7 @@ public class AudioPolicy { * @param uid UID of the application affected. * @return true if the change was successful, false otherwise. */ + @TestApi @SystemApi public boolean removeUidDeviceAffinity(int uid) { synchronized (mLock) { diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 6361fd81d2af..4ac6d35e351f 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -36,6 +36,7 @@ import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.Log; +import android.util.SparseArray; import android.view.Display; import android.view.WindowManager; @@ -69,8 +70,6 @@ public class MtpDatabase implements AutoCloseable { private final Context mContext; private final ContentProviderClient mMediaProvider; - private final String mVolumeName; - private final Uri mObjectsUri; private final AtomicBoolean mClosed = new AtomicBoolean(); private final CloseGuard mCloseGuard = CloseGuard.get(); @@ -78,10 +77,10 @@ public class MtpDatabase implements AutoCloseable { private final HashMap<String, MtpStorage> mStorageMap = new HashMap<>(); // cached property groups for single properties - private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty = new HashMap<>(); + private final SparseArray<MtpPropertyGroup> mPropertyGroupsByProperty = new SparseArray<>(); // cached property groups for all properties for a given format - private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByFormat = new HashMap<>(); + private final SparseArray<MtpPropertyGroup> mPropertyGroupsByFormat = new SparseArray<>(); // SharedPreferences for writable MTP device properties private SharedPreferences mDeviceProperties; @@ -271,14 +270,11 @@ public class MtpDatabase implements AutoCloseable { } }; - public MtpDatabase(Context context, String volumeName, - String[] subDirectories) { + public MtpDatabase(Context context, String[] subDirectories) { native_setup(); mContext = Objects.requireNonNull(context); mMediaProvider = context.getContentResolver() .acquireContentProviderClient(MediaStore.AUTHORITY); - mVolumeName = volumeName; - mObjectsUri = Files.getMtpObjectsUri(volumeName); mManager = new MtpStorageManager(new MtpStorageManager.MtpNotifier() { @Override public void sendObjectAdded(int id) { @@ -526,8 +522,7 @@ public class MtpDatabase implements AutoCloseable { propertyGroup = mPropertyGroupsByFormat.get(format); if (propertyGroup == null) { final int[] propertyList = getSupportedObjectProperties(format); - propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName, - propertyList); + propertyGroup = new MtpPropertyGroup(propertyList); mPropertyGroupsByFormat.put(format, propertyGroup); } } else { @@ -535,12 +530,11 @@ public class MtpDatabase implements AutoCloseable { propertyGroup = mPropertyGroupsByProperty.get(property); if (propertyGroup == null) { final int[] propertyList = new int[]{property}; - propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName, - propertyList); + propertyGroup = new MtpPropertyGroup(propertyList); mPropertyGroupsByProperty.put(property, propertyGroup); } } - int err = propertyGroup.getPropertyList(obj, ret); + int err = propertyGroup.getPropertyList(mMediaProvider, obj.getVolumeName(), obj, ret); if (err != MtpConstants.RESPONSE_OK) { return new MtpPropertyList(err); } @@ -581,7 +575,8 @@ public class MtpDatabase implements AutoCloseable { try { // note - we are relying on a special case in MediaProvider.update() to update // the paths for all children in the case where this is a directory. - mMediaProvider.update(mObjectsUri, values, PATH_WHERE, whereArgs); + final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName()); + mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs); } catch (RemoteException e) { Log.e(TAG, "RemoteException in mMediaProvider.update", e); } @@ -640,12 +635,12 @@ public class MtpDatabase implements AutoCloseable { if (obj.getParent().isRoot()) { values.put(Files.FileColumns.PARENT, 0); } else { - int parentId = findInMedia(path.getParent()); + int parentId = findInMedia(newParentObj, path.getParent()); if (parentId != -1) { values.put(Files.FileColumns.PARENT, parentId); } else { // The new parent isn't in MediaProvider, so delete the object instead - deleteFromMedia(oldPath, obj.isDir()); + deleteFromMedia(obj, oldPath, obj.isDir()); return; } } @@ -655,13 +650,14 @@ public class MtpDatabase implements AutoCloseable { try { int parentId = -1; if (!oldParentObj.isRoot()) { - parentId = findInMedia(oldPath.getParent()); + parentId = findInMedia(oldParentObj, oldPath.getParent()); } if (oldParentObj.isRoot() || parentId != -1) { // Old parent exists in MediaProvider - perform a move // note - we are relying on a special case in MediaProvider.update() to update // the paths for all children in the case where this is a directory. - mMediaProvider.update(mObjectsUri, values, PATH_WHERE, whereArgs); + final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName()); + mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs); } else { // Old parent doesn't exist - add the object MediaStore.scanFile(mContext, path.toFile()); @@ -823,14 +819,16 @@ public class MtpDatabase implements AutoCloseable { if (!mManager.endRemoveObject(obj, success)) Log.e(TAG, "Failed to end remove object"); if (success) - deleteFromMedia(obj.getPath(), obj.isDir()); + deleteFromMedia(obj, obj.getPath(), obj.isDir()); } - private int findInMedia(Path path) { + private int findInMedia(MtpStorageManager.MtpObject obj, Path path) { + final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName()); + int ret = -1; Cursor c = null; try { - c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE, + c = mMediaProvider.query(objectsUri, ID_PROJECTION, PATH_WHERE, new String[]{path.toString()}, null, null); if (c != null && c.moveToNext()) { ret = c.getInt(0); @@ -844,12 +842,13 @@ public class MtpDatabase implements AutoCloseable { return ret; } - private void deleteFromMedia(Path path, boolean isDir) { + private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) { + final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName()); try { // Delete the object(s) from MediaProvider, but ignore errors. if (isDir) { // recursive case - delete all children first - mMediaProvider.delete(mObjectsUri, + mMediaProvider.delete(objectsUri, // the 'like' makes it use the index, the 'lower()' makes it correct // when the path contains sqlite wildcard characters "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)", @@ -858,7 +857,7 @@ public class MtpDatabase implements AutoCloseable { } String[] whereArgs = new String[]{path.toString()}; - if (mMediaProvider.delete(mObjectsUri, PATH_WHERE, whereArgs) > 0) { + if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) { if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) { MediaStore.scanFile(mContext, path.getParent().toFile()); } @@ -876,10 +875,10 @@ public class MtpDatabase implements AutoCloseable { if (obj == null) return null; // Translate this handle to the MediaProvider Handle - handle = findInMedia(obj.getPath()); + handle = findInMedia(obj, obj.getPath()); if (handle == -1) return null; - Uri uri = Files.getMtpReferencesUri(mVolumeName, handle); + Uri uri = Files.getMtpReferencesUri(obj.getVolumeName(), handle); Cursor c = null; try { c = mMediaProvider.query(uri, PATH_PROJECTION, null, null, null, null); @@ -912,17 +911,17 @@ public class MtpDatabase implements AutoCloseable { if (obj == null) return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; // Translate this handle to the MediaProvider Handle - handle = findInMedia(obj.getPath()); + handle = findInMedia(obj, obj.getPath()); if (handle == -1) return MtpConstants.RESPONSE_GENERAL_ERROR; - Uri uri = Files.getMtpReferencesUri(mVolumeName, handle); + Uri uri = Files.getMtpReferencesUri(obj.getVolumeName(), handle); ArrayList<ContentValues> valuesList = new ArrayList<>(); for (int id : references) { // Translate each reference id to the MediaProvider Id MtpStorageManager.MtpObject refObj = mManager.getObject(id); if (refObj == null) continue; - int refHandle = findInMedia(refObj.getPath()); + int refHandle = findInMedia(refObj, refObj.getPath()); if (refHandle == -1) continue; ContentValues values = new ContentValues(); diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java index 6d5be8ef6985..5bb0c1b4ef27 100644 --- a/media/java/android/mtp/MtpPropertyGroup.java +++ b/media/java/android/mtp/MtpPropertyGroup.java @@ -46,9 +46,6 @@ class MtpPropertyGroup { } } - private final ContentProviderClient mProvider; - private final String mVolumeName; - // list of all properties in this group private final Property[] mProperties; @@ -58,10 +55,7 @@ class MtpPropertyGroup { private static final String PATH_WHERE = Files.FileColumns.DATA + "=?"; // constructs a property group for a list of properties - public MtpPropertyGroup(ContentProviderClient provider, String volumeName, int[] properties) { - mProvider = provider; - mVolumeName = volumeName; - + public MtpPropertyGroup(int[] properties) { int count = properties.length; ArrayList<String> columns = new ArrayList<>(count); columns.add(Files.FileColumns._ID); @@ -175,7 +169,8 @@ class MtpPropertyGroup { * object and adds them to the given property list. * @return Response_OK if the operation succeeded. */ - public int getPropertyList(MtpStorageManager.MtpObject object, MtpPropertyList list) { + public int getPropertyList(ContentProviderClient provider, String volumeName, + MtpStorageManager.MtpObject object, MtpPropertyList list) { Cursor c = null; int id = object.getId(); String path = object.getPath().toString(); @@ -184,8 +179,8 @@ class MtpPropertyGroup { try { // Look up the entry in MediaProvider only if one of those properties is needed. final Uri uri = MtpDatabase.getObjectPropertiesUri(object.getFormat(), - mVolumeName); - c = mProvider.query(uri, mColumns, + volumeName); + c = provider.query(uri, mColumns, PATH_WHERE, new String[] {path}, null, null); if (c != null && !c.moveToNext()) { c.close(); diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java index c714b3cad296..65d0fef74b25 100644 --- a/media/java/android/mtp/MtpStorage.java +++ b/media/java/android/mtp/MtpStorage.java @@ -18,6 +18,7 @@ package android.mtp; import android.annotation.UnsupportedAppUsage; import android.os.storage.StorageVolume; +import android.provider.MediaStore; /** * This class represents a storage unit on an MTP device. @@ -27,12 +28,12 @@ import android.os.storage.StorageVolume; * @hide */ public class MtpStorage { - private final int mStorageId; private final String mPath; private final String mDescription; private final boolean mRemovable; private final long mMaxFileSize; + private final String mVolumeName; public MtpStorage(StorageVolume volume, int storageId) { mStorageId = storageId; @@ -40,6 +41,11 @@ public class MtpStorage { mDescription = volume.getDescription(null); mRemovable = volume.isRemovable(); mMaxFileSize = volume.getMaxFileSize(); + if (volume.isPrimary()) { + mVolumeName = MediaStore.VOLUME_EXTERNAL_PRIMARY; + } else { + mVolumeName = volume.getNormalizedUuid(); + } } /** @@ -88,4 +94,8 @@ public class MtpStorage { public long getMaxFileSize() { return mMaxFileSize; } + + public String getVolumeName() { + return mVolumeName; + } } diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java index f14e7d704ea1..e783788d0158 100644 --- a/media/java/android/mtp/MtpStorageManager.java +++ b/media/java/android/mtp/MtpStorageManager.java @@ -21,6 +21,8 @@ import android.os.FileObserver; import android.os.storage.StorageVolume; import android.util.Log; +import com.android.internal.util.Preconditions; + import java.io.IOException; import java.nio.file.DirectoryIteratorException; import java.nio.file.DirectoryStream; @@ -131,6 +133,7 @@ public class MtpStorageManager { /** MtpObject represents either a file or directory in an associated storage. **/ public static class MtpObject { + private MtpStorage mStorage; // null for root objects private MtpObject mParent; @@ -147,9 +150,10 @@ public class MtpStorageManager { // null if not both a directory and visited private FileObserver mObserver; - MtpObject(String name, int id, MtpObject parent, boolean isDir) { + MtpObject(String name, int id, MtpStorage storage, MtpObject parent, boolean isDir) { mId = id; mName = name; + mStorage = Preconditions.checkNotNull(storage); mParent = parent; mObserver = null; mVisited = false; @@ -206,6 +210,10 @@ public class MtpStorageManager { return mParent == null; } + public String getVolumeName() { + return mStorage.getVolumeName(); + } + /** For MtpStorageManager only **/ private void setName(String name) { @@ -278,7 +286,7 @@ public class MtpStorageManager { } private MtpObject copy(boolean recursive) { - MtpObject copy = new MtpObject(mName, mId, mParent, mIsDir); + MtpObject copy = new MtpObject(mName, mId, mStorage, mParent, mIsDir); copy.mIsDir = mIsDir; copy.mVisited = mVisited; copy.mState = mState; @@ -408,7 +416,7 @@ public class MtpStorageManager { public synchronized MtpStorage addMtpStorage(StorageVolume volume) { int storageId = ((getNextStorageId() & 0x0000FFFF) << 16) + 1; MtpStorage storage = new MtpStorage(volume, storageId); - MtpObject root = new MtpObject(storage.getPath(), storageId, null, true); + MtpObject root = new MtpObject(storage.getPath(), storageId, storage, null, true); mRoots.put(storageId, root); return storage; } @@ -608,7 +616,7 @@ public class MtpStorageManager { return null; } - MtpObject obj = new MtpObject(newName, getNextObjectId(), parent, isDir); + MtpObject obj = new MtpObject(newName, getNextObjectId(), parent.mStorage, parent, isDir); mObjects.put(obj.getId(), obj); parent.addChild(obj); return obj; diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java index 55c9361ce6c4..81c5bcd47981 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java @@ -52,6 +52,7 @@ import android.widget.TextView; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.TrafficStatsConstants; import java.io.IOException; import java.lang.reflect.Field; @@ -238,7 +239,8 @@ public class CaptivePortalLoginActivity extends Activity { if (isFinishing() || isDestroyed()) return; HttpURLConnection urlConnection = null; int httpResponseCode = 500; - int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE); + int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_PROBE); try { urlConnection = (HttpURLConnection) mNetwork.openConnection( new URL(mCm.getCaptivePortalServerUrl())); diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index 1544adb23175..7860f36e7d95 100644 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java @@ -238,7 +238,7 @@ public class Assistant extends NotificationAssistantService { } mSingleThreadExecutor.submit(() -> { NotificationEntry entry = - new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + new NotificationEntry(mPackageManager, sbn.cloneLight(), channel, mSmsHelper); SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry); if (DEBUG) { Log.d(TAG, String.format( @@ -272,6 +272,9 @@ public class Assistant extends NotificationAssistantService { final int importance = entry.getImportance() < IMPORTANCE_LOW ? entry.getImportance() : IMPORTANCE_LOW; signals.putInt(KEY_IMPORTANCE, importance); + } else { + // Even if no change is made, send an identity adjustment for metric logging. + signals.putInt(KEY_IMPORTANCE, entry.getImportance()); } } @@ -293,7 +296,7 @@ public class Assistant extends NotificationAssistantService { Ranking ranking = getRanking(sbn.getKey(), rankingMap); if (ranking != null && ranking.getChannel() != null) { NotificationEntry entry = new NotificationEntry(mPackageManager, - sbn, ranking.getChannel(), mSmsHelper); + sbn.cloneLight(), ranking.getChannel(), mSmsHelper); String key = getKey( sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId()); ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java index 0d687d41260d..10360a34546c 100644 --- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java +++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java @@ -338,7 +338,7 @@ public class SmartActionsHelper { createTextClassifierEventBuilder( TextClassifierEvent.TYPE_SMART_ACTION, session.resultId) .setEntityTypes(ConversationAction.TYPE_TEXT_REPLY) - .setScore(session.repliesScores.getOrDefault(reply, 0f)) + .setScores(session.repliesScores.getOrDefault(reply, 0f)) .build(); mTextClassifier.onTextClassifierEvent(textClassifierEvent); } @@ -381,11 +381,9 @@ public class SmartActionsHelper { .build(); } - private TextClassifierEvent.Builder createTextClassifierEventBuilder( + private TextClassifierEvent.ConversationActionsEvent.Builder createTextClassifierEventBuilder( int eventType, String resultId) { - return new TextClassifierEvent.Builder( - TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType) - .setEventTime(System.currentTimeMillis()) + return new TextClassifierEvent.ConversationActionsEvent.Builder(eventType) .setEventContext( new TextClassificationContext.Builder( mContext.getPackageName(), TextClassifier.WIDGET_TYPE_NOTIFICATION) 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 ebcaee87d6d8..dfa1ea0aac90 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java @@ -330,7 +330,9 @@ public class SmartActionsHelperTest { 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); + float[] scores = events.get(1).getScores(); + assertThat(scores).hasLength(1); + assertThat(scores[0]).isEqualTo(SCORE); } @Test diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java index 64adc0d48309..af0e3bb8bfc2 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java @@ -65,6 +65,7 @@ import com.android.internal.util.HexDump; import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import com.android.internal.util.TrafficStatsConstants; import com.android.internal.util.WakeupMessage; import java.io.FileDescriptor; @@ -329,7 +330,8 @@ public class DhcpClient extends StateMachine { } private boolean initUdpSocket() { - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_DHCP); try { mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName); diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java index 8832eaadf6cb..d21b5f7853bb 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java @@ -16,7 +16,6 @@ package android.net.dhcp; -import static android.net.TrafficStats.TAG_SYSTEM_DHCP_SERVER; import static android.net.dhcp.DhcpPacket.DHCP_CLIENT; import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME; import static android.net.dhcp.DhcpPacket.DHCP_SERVER; @@ -33,6 +32,7 @@ import static android.system.OsConstants.SOL_SOCKET; import static android.system.OsConstants.SO_BROADCAST; import static android.system.OsConstants.SO_REUSEADDR; +import static com.android.internal.util.TrafficStatsConstants.TAG_SYSTEM_DHCP_SERVER; import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE; import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL; import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY; diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 2a612503620f..27d420328017 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -98,6 +98,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.RingBufferIndices; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import com.android.internal.util.TrafficStatsConstants; import com.android.networkstack.R; import com.android.networkstack.metrics.DataStallDetectionStats; import com.android.networkstack.metrics.DataStallStatsUtils; @@ -1478,7 +1479,8 @@ public class NetworkMonitor extends StateMachine { int httpResponseCode = CaptivePortalProbeResult.FAILED_CODE; String redirectUrl = null; final Stopwatch probeTimer = new Stopwatch().start(); - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_PROBE); try { urlConnection = (HttpURLConnection) mNetwork.openConnection(url); urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC); diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java index 87b5b57c7d9e..4f4aef0b0fae 100644 --- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java +++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java @@ -76,11 +76,13 @@ public class RestrictedLockUtils { public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { final Intent intent = getShowAdminSupportDetailsIntent(context, admin); int targetUserId = UserHandle.myUserId(); - if (admin != null && admin.user != null - && isCurrentUserOrProfile(context, admin.user.getIdentifier())) { - targetUserId = admin.user.getIdentifier(); + if (admin != null) { + if (admin.user != null + && isCurrentUserOrProfile(context, admin.user.getIdentifier())) { + targetUserId = admin.user.getIdentifier(); + } + intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, admin.enforcedRestriction); } - intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, admin.enforcedRestriction); context.startActivityAsUser(intent, UserHandle.of(targetUserId)); } diff --git a/packages/SettingsLib/res/layout/settings_dialog_title.xml b/packages/SettingsLib/res/layout/settings_dialog_title.xml new file mode 100644 index 000000000000..1e065e0dbb4d --- /dev/null +++ b/packages/SettingsLib/res/layout/settings_dialog_title.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/settings_title_panel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout + android:id="@+id/settings_title_template" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="center" + android:paddingStart="?android:attr/dialogPreferredPadding" + android:paddingEnd="?android:attr/dialogPreferredPadding" + android:paddingTop="@*android:dimen/dialog_padding_top_material"> + + <ImageView + android:id="@+id/settings_icon" + android:layout_width="24dip" + android:layout_height="24dip" + android:layout_marginBottom="12dip" /> + + <TextView + android:id="@+id/settings_title" + android:singleLine="true" + android:ellipsize="end" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="center" + style="?android:attr/windowTitleStyle" /> + </LinearLayout> + + <Space + android:id="@+id/settings_title_divider" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="@*android:dimen/dialog_title_divider_material" /> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt index eff02d24431e..257943e16149 100644 --- a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt +++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt @@ -83,6 +83,8 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) // Dual tone implies that battery level is a clipped overlay over top of the whole shape private var dualTone = false + private var batteryLevel = 0 + private val invalidateRunnable: () -> Unit = { invalidateSelf() } @@ -177,9 +179,9 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) unifiedPath.reset() levelPath.reset() levelRect.set(fillRect) - val fillFraction = level / 100f + val fillFraction = batteryLevel / 100f val fillTop = - if (level >= 95) + if (batteryLevel >= 95) fillRect.top else fillRect.top + (fillRect.height() * (1 - fillFraction)) @@ -223,7 +225,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) fillPaint.color = levelColor // Show colorError below this level - if (level <= Companion.CRITICAL_LEVEL && !charging) { + if (batteryLevel <= Companion.CRITICAL_LEVEL && !charging) { c.save() c.clipPath(scaledFill) c.drawPath(levelPath, fillPaint) @@ -310,13 +312,13 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) */ public open fun setBatteryLevel(l: Int) { invertFillIcon = if (l >= 67) true else if (l <= 33) false else invertFillIcon - level = l - levelColor = batteryColorForLevel(level) + batteryLevel = l + levelColor = batteryColorForLevel(batteryLevel) invalidateSelf() } public fun getBatteryLevel(): Int { - return level + return batteryLevel } override fun onBoundsChange(bounds: Rect?) { @@ -343,7 +345,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) dualToneBackgroundFill.color = bgColor // Also update the level color, since fillColor may have changed - levelColor = batteryColorForLevel(level) + levelColor = batteryColorForLevel(batteryLevel) invalidateSelf() } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 1976ec45bf45..d1e4fdf472c0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -337,6 +337,7 @@ public class AccessPoint implements Comparable<AccessPoint> { mContext = context; networkId = config.networkId; mConfig = config; + mFqdn = config.FQDN; setScanResultsPasspoint(homeScans, roamingScans); updateKey(); } @@ -673,6 +674,13 @@ public class AccessPoint implements Comparable<AccessPoint> { return mKey; } + /** + * Determines if the other AccessPoint represents the same network as this AccessPoint + */ + public boolean matches(AccessPoint other) { + return getKey().equals(other.getKey()); + } + public boolean matches(WifiConfiguration config) { if (config.isPasspoint()) { return (isPasspoint() && config.FQDN.equals(mConfig.FQDN)); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java index 2b5a4e069001..11c799ea9df5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java @@ -16,6 +16,7 @@ package com.android.settingslib; +import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT; @@ -28,11 +29,13 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.UserHandle; @@ -42,6 +45,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -283,6 +287,26 @@ public class RestrictedLockUtilsTest { assertThat(profile).isNull(); } + @Test + public void sendShowAdminSupportDetailsIntent_extraRestrictionProvided() { + EnforcedAdmin enforcedAdmin = new EnforcedAdmin(); + enforcedAdmin.enforcedRestriction = "Dummy"; + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, enforcedAdmin); + + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).startActivityAsUser(intentCaptor.capture(), any()); + assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Dummy"); + } + + @Test + public void sendShowAdminSupportDetailsIntent_noExtraRestriction() { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, null); + + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).startActivityAsUser(intentCaptor.capture(), any()); + assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isNull(); + } + private UserInfo setUpUser(int userId, ComponentName[] admins) { UserInfo userInfo = new UserInfo(userId, "primary", 0); when(mUserManager.getUserInfo(userId)).thenReturn(userInfo); diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png Binary files differindex 83d714bfcb05..83d714bfcb05 100644 --- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png Binary files differdeleted file mode 100644 index 2bfd655e37de..000000000000 --- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png +++ /dev/null diff --git a/packages/SystemUI/res-keyguard/drawable/analog_frame.xml b/packages/SystemUI/res-keyguard/drawable/analog_frame.xml new file mode 100644 index 000000000000..a663ac826127 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/analog_frame.xml @@ -0,0 +1,7 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="250dp" + android:width="250dp" + android:viewportHeight="380" + android:viewportWidth="380"> + <path android:fillColor="#000000" android:pathData="M190,190m0,2a2,2 0,1 1,0 -4a2,2 0,1 1,0 4"/> +</vector> diff --git a/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml b/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml new file mode 100644 index 000000000000..c7b6d60b319f --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml @@ -0,0 +1,7 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="250dp" + android:width="250dp" + android:viewportHeight="380" + android:viewportWidth="380"> + <path android:fillColor="#777777" android:fillType="evenOdd" android:pathData="M203,190C203,185.398 200.608,181.354 197,179.044L197,58C197,54.134 193.866,51 190,51C186.134,51 183,54.134 183,58L183,179.043C179.392,181.354 177,185.397 177,190C177,197.18 182.82,203 190,203C197.18,203 203,197.18 203,190Z"/> +</vector> diff --git a/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml b/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml new file mode 100644 index 000000000000..458275bec23a --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml @@ -0,0 +1,7 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="250dp" + android:width="250dp" + android:viewportHeight="380" + android:viewportWidth="380"> + <path android:fillColor="#FFFFFF" android:pathData="M192,182.252C195.45,183.14 198,186.272 198,190C198,194.418 194.418,198 190,198C185.582,198 182,194.418 182,190C182,186.272 184.55,183.14 188,182.252L188,10C188,8.895 188.895,8 190,8C191.105,8 192,8.895 192,10L192,182.252Z"/> +</vector> diff --git a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml b/packages/SystemUI/res-keyguard/layout/analog_clock.xml index dd25df864733..cf6d35ee27af 100644 --- a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/analog_clock.xml @@ -35,9 +35,27 @@ android:format24Hour="@string/keyguard_widget_24_hours_format" android:elegantTextHeight="false" /> - <com.android.keyguard.clock.StretchAnalogClock + <com.android.keyguard.clock.ImageClock android:id="@+id/analog_clock" - android:layout_width="match_parent" - android:layout_height="match_parent" - /> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + > + <ImageView + android:id="@+id/hour_hand" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/analog_hour_hand" + /> + <ImageView + android:id="@+id/minute_hand" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/analog_minute_hand" + /> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/analog_frame" + /> + </com.android.keyguard.clock.ImageClock> </com.android.keyguard.clock.ClockLayout> diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 4738887d546e..1e98189fa238 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -413,9 +413,6 @@ number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. <string name="clock_title_bubble" translatable="false">Bubble</string> <!-- Title for Stretch clock face that will appear in the picker app next to a preview image of the clock face. [CHAR LIMIT=8] --> - <string name="clock_title_stretch" translatable="false">Stretch</string> - <!-- Title for Typographic clock face that will appear in the picker app next to a preview image of - the clock face. [CHAR LIMIT=8] --> - <string name="clock_title_type" translatable="false">Type</string> + <string name="clock_title_analog" translatable="false">Analog</string> </resources> diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml index e92ec0f75fc1..3cefce071a1e 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_item.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml @@ -20,44 +20,50 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="@dimen/global_actions_grid_item_width" android:layout_height="@dimen/global_actions_grid_item_height" - android:gravity="top|center_horizontal" - android:orientation="vertical" + android:gravity="center" android:layout_marginTop="@dimen/global_actions_grid_item_vertical_margin" android:layout_marginBottom="@dimen/global_actions_grid_item_vertical_margin" android:layout_marginLeft="@dimen/global_actions_grid_item_side_margin" android:layout_marginRight="@dimen/global_actions_grid_item_side_margin" > - <ImageView - android:id="@*android:id/icon" - android:layout_width="@dimen/global_actions_grid_item_icon_width" - android:layout_height="@dimen/global_actions_grid_item_icon_height" - android:layout_marginTop="@dimen/global_actions_grid_item_icon_top_margin" - android:layout_marginBottom="@dimen/global_actions_grid_item_icon_bottom_margin" - android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin" - android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin" - android:scaleType="centerInside" - android:alpha="?android:attr/primaryContentAlpha" - /> + <LinearLayout + android:layout_width="@dimen/global_actions_grid_item_width" + android:layout_height="@dimen/global_actions_grid_item_height" + android:gravity="top|center_horizontal" + android:orientation="vertical" + > + <ImageView + android:id="@*android:id/icon" + android:layout_width="@dimen/global_actions_grid_item_icon_width" + android:layout_height="@dimen/global_actions_grid_item_icon_height" + android:layout_marginTop="@dimen/global_actions_grid_item_icon_top_margin" + android:layout_marginBottom="@dimen/global_actions_grid_item_icon_bottom_margin" + android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin" + android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin" + android:scaleType="centerInside" + android:alpha="?android:attr/primaryContentAlpha" + /> - <TextView - android:id="@*android:id/message" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ellipsize="marquee" - android:marqueeRepeatLimit="marquee_forever" - android:singleLine="true" - android:gravity="center" - android:textSize="12dp" - android:textAppearance="?android:attr/textAppearanceSmall" - /> + <TextView + android:id="@*android:id/message" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:singleLine="true" + android:gravity="center" + android:textSize="12dp" + android:textAppearance="?android:attr/textAppearanceSmall" + /> - <TextView - android:visibility="gone" - android:id="@*android:id/status" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:textColor="?android:attr/textColorTertiary" - android:textAppearance="?android:attr/textAppearanceSmall" - /> + <TextView + android:visibility="gone" + android:id="@*android:id/status" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:textColor="?android:attr/textColorTertiary" + android:textAppearance="?android:attr/textAppearanceSmall" + /> + </LinearLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml index f34161e285da..81d44cfa49dd 100644 --- a/packages/SystemUI/res/layout/qs_tile_label.xml +++ b/packages/SystemUI/res/layout/qs_tile_label.xml @@ -44,8 +44,7 @@ android:padding="0dp" android:gravity="center" android:ellipsize="marquee" - android:textAppearance="@style/TextAppearance.QS.TileLabel" - android:textColor="?android:attr/textColorPrimary"/> + android:textAppearance="@style/TextAppearance.QS.TileLabel"/> <ImageView android:id="@+id/restricted_padlock" android:layout_width="@dimen/qs_tile_text_size" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 444cabfcc50e..f121c8ec5de0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1331,6 +1331,14 @@ <!-- Content description for accessibility: Clear the odi caption tool tip. [CHAR LIMIT=NONE] --> <string name="accessibility_volume_close_odi_captions_tip">Close captions tip</string> + <!-- Content description for accessibility: Captions button. [CHAR LIMIT=NONE] --> + <string name="volume_odi_captions_content_description">Captions overlay</string> + + <!-- Content description for accessibility: Hint if click will enable. [CHAR LIMIT=NONE] --> + <string name="volume_odi_captions_hint_enable">enable</string> + <!-- Content description for accessibility: Hint if click will disable. [CHAR LIMIT=NONE] --> + <string name="volume_odi_captions_hint_disable">disable</string> + <!-- content description for audio output chooser [CHAR LIMIT=NONE]--> <string name="accessibility_output_chooser">Switch output device</string> diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index 64517bae6d6b..eca39262dfb5 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -16,14 +16,22 @@ package com.android.keyguard; +import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; +import static android.telephony.PhoneStateListener.LISTEN_NONE; + +import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM; + import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.Handler; +import android.telephony.CarrierConfigManager; +import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -36,6 +44,7 @@ import com.android.settingslib.WirelessUtils; import com.android.systemui.Dependency; import com.android.systemui.keyguard.WakefulnessLifecycle; +import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -113,6 +122,17 @@ public class CarrierTextController { } }; + private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + @Override + public void onActiveDataSubscriptionIdChanged(int subId) { + mActiveMobileDataSubscription = subId; + if (mKeyguardUpdateMonitor != null) { + updateCarrierText(); + } + } + }; + /** * The status of this lock screen. Primarily used for widgets on LockScreen. */ @@ -200,6 +220,8 @@ public class CarrierTextController { * @param callback Callback to provide text updates */ public void setListening(CarrierTextCallback callback) { + TelephonyManager telephonyManager = ((TelephonyManager) mContext + .getSystemService(Context.TELEPHONY_SERVICE)); if (callback != null) { mCarrierTextCallback = callback; if (ConnectivityManager.from(mContext).isNetworkSupported( @@ -207,6 +229,8 @@ public class CarrierTextController { mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); mKeyguardUpdateMonitor.registerCallback(mCallback); mWakefulnessLifecycle.addObserver(mWakefulnessObserver); + telephonyManager.listen(mPhoneStateListener, + LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); } else { // Don't listen and clear out the text when the device isn't a phone. mKeyguardUpdateMonitor = null; @@ -218,6 +242,35 @@ public class CarrierTextController { mKeyguardUpdateMonitor.removeCallback(mCallback); mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); } + telephonyManager.listen(mPhoneStateListener, LISTEN_NONE); + } + } + + /** + * STOPSHIP(b/130246708) remove when no longer needed for testing purpose. + * @param subscriptions + */ + private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) { + if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) { + SubscriptionInfo info1 = subscriptions.get(0); + SubscriptionInfo info2 = subscriptions.get(1); + if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) { + // If both subscriptions are primary, show both. + if (!info1.isOpportunistic() && !info2.isOpportunistic()) return; + + // If carrier required, always show signal bar of primary subscription. + // Otherwise, show whichever subscription is currently active for Internet. + boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig() + .getBoolean(CarrierConfigManager + .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN); + if (alwaysShowPrimary) { + subscriptions.remove(info1.isOpportunistic() ? info1 : info2); + } else { + subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription + ? info2 : info1); + } + + } } } @@ -226,7 +279,17 @@ public class CarrierTextController { boolean anySimReadyAndInService = false; CharSequence displayText = null; - List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); + // STOPSHIP(b/130246708) revert to mKeyguardUpdateMonitor.getSubscriptionInfo(false). + SubscriptionManager subscriptionManager = ((SubscriptionManager) mContext.getSystemService( + Context.TELEPHONY_SUBSCRIPTION_SERVICE)); + List<SubscriptionInfo> subs = subscriptionManager.getActiveSubscriptionInfoList(false); + + if (subs == null) { + subs = new ArrayList<>(); + } else { + filterMobileSubscriptionInSameGroup(subs); + } + final int numSubs = subs.size(); final int[] subsIds = new int[numSubs]; // This array will contain in position i, the index of subscription in slot ID i. @@ -311,20 +374,23 @@ public class CarrierTextController { displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot, allSimsMissing); + boolean airplaneMode = false; // APM (airplane mode) != no carrier state. There are carrier services // (e.g. WFC = Wi-Fi calling) which may operate in APM. if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) { displayText = getAirplaneModeMessage(); + airplaneMode = true; } - if (TextUtils.isEmpty(displayText)) { + if (TextUtils.isEmpty(displayText) && !airplaneMode) { displayText = TextUtils.join(mSeparator, carrierNames); } final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo( displayText, carrierNames, !allSimsMissing, - subsIds); + subsIds, + airplaneMode); postToCallback(info); } @@ -525,14 +591,22 @@ public class CarrierTextController { public final CharSequence[] listOfCarriers; public final boolean anySimReady; public final int[] subscriptionIds; + public boolean airplaneMode; @VisibleForTesting public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, int[] subscriptionIds) { + this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false); + } + + @VisibleForTesting + public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, + boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) { this.carrierText = carrierText; this.listOfCarriers = listOfCarriers; this.anySimReady = anySimReady; this.subscriptionIds = subscriptionIds; + this.airplaneMode = airplaneMode; } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index fd557083aefd..1d19fecac8c9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -665,9 +665,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING; boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING; mFingerprintRunningState = fingerprintRunningState; - if (DEBUG) Log.v(TAG, "Fingerprint State: " + mFingerprintRunningState); + Log.d(TAG, "fingerprintRunningState: " + mFingerprintRunningState); // Clients of KeyguardUpdateMonitor don't care about the internal state about the - // asynchronousness of the cancel cycle. So only notify them if the actualy running state + // asynchronousness of the cancel cycle. So only notify them if the actually running state // has changed. if (wasRunning != isRunning) { notifyFingerprintRunningStateChanged(); @@ -818,9 +818,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { boolean wasRunning = mFaceRunningState == BIOMETRIC_STATE_RUNNING; boolean isRunning = faceRunningState == BIOMETRIC_STATE_RUNNING; mFaceRunningState = faceRunningState; - if (DEBUG) Log.v(TAG, "Face State: " + mFaceRunningState); + Log.d(TAG, "faceRunningState: " + mFaceRunningState); // Clients of KeyguardUpdateMonitor don't care about the internal state or about the - // asynchronousness of the cancel cycle. So only notify them if the actualy running state + // asynchronousness of the cancel cycle. So only notify them if the actually running state // has changed. if (wasRunning != isRunning) { notifyFaceRunningStateChanged(); @@ -2045,7 +2045,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { */ public void onKeyguardVisibilityChanged(boolean showing) { checkIsHandlerThread(); - if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")"); + Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")"); mKeyguardIsVisible = showing; for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java index 81b6a600f398..1652121f350d 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java @@ -35,7 +35,7 @@ import java.util.TimeZone; /** * Controller for Stretch clock that can appear on lock screen and AOD. */ -public class StretchAnalogClockController implements ClockPlugin { +public class AnalogClockController implements ClockPlugin { /** * Resources used to get title and thumbnail. @@ -60,9 +60,9 @@ public class StretchAnalogClockController implements ClockPlugin { /** * Custom clock shown on AOD screen and behind stack scroller on lock. */ - private View mBigClockView; + private ClockLayout mBigClockView; private TextClock mDigitalClock; - private StretchAnalogClock mAnalogClock; + private ImageClock mAnalogClock; /** * Small clock shown on lock screen above stack scroller. @@ -82,7 +82,7 @@ public class StretchAnalogClockController implements ClockPlugin { * @param inflater Inflater used to inflate custom clock views. * @param colorExtractor Extracts accent color from wallpaper. */ - public StretchAnalogClockController(Resources res, LayoutInflater inflater, + public AnalogClockController(Resources res, LayoutInflater inflater, SysuiColorExtractor colorExtractor) { mResources = res; mLayoutInflater = inflater; @@ -90,7 +90,7 @@ public class StretchAnalogClockController implements ClockPlugin { } private void createViews() { - mBigClockView = mLayoutInflater.inflate(R.layout.stretchanalog_clock, null); + mBigClockView = (ClockLayout) mLayoutInflater.inflate(R.layout.analog_clock, null); mAnalogClock = mBigClockView.findViewById(R.id.analog_clock); mDigitalClock = mBigClockView.findViewById(R.id.digital_clock); @@ -114,17 +114,17 @@ public class StretchAnalogClockController implements ClockPlugin { @Override public String getName() { - return "stretch"; + return "analog"; } @Override public String getTitle() { - return mResources.getString(R.string.clock_title_stretch); + return mResources.getString(R.string.clock_title_analog); } @Override public Bitmap getThumbnail() { - return BitmapFactory.decodeResource(mResources, R.drawable.stretch_thumbnail); + return BitmapFactory.decodeResource(mResources, R.drawable.analog_thumbnail); } @Override @@ -175,13 +175,14 @@ public class StretchAnalogClockController implements ClockPlugin { } final int length = colorPalette.length; mDigitalClock.setTextColor(colorPalette[Math.max(0, length - 5)]); - mAnalogClock.setClockColor(colorPalette[Math.max(0, length - 5)], + mAnalogClock.setClockColors(colorPalette[Math.max(0, length - 5)], colorPalette[Math.max(0, length - 2)]); } @Override public void onTimeTick() { mAnalogClock.onTimeChanged(); + mBigClockView.onTimeChanged(); mDigitalClock.refresh(); mLockClock.refresh(); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java index d30f45f212b9..6069a5e4783d 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java @@ -60,7 +60,7 @@ public class BubbleClockController implements ClockPlugin { /** * Custom clock shown on AOD screen and behind stack scroller on lock. */ - private View mView; + private ClockLayout mView; private TextClock mDigitalClock; private ImageClock mAnalogClock; @@ -90,7 +90,7 @@ public class BubbleClockController implements ClockPlugin { } private void createViews() { - mView = mLayoutInflater.inflate(R.layout.bubble_clock, null); + mView = (ClockLayout) mLayoutInflater.inflate(R.layout.bubble_clock, null); mDigitalClock = (TextClock) mView.findViewById(R.id.digital_clock); mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock); @@ -186,6 +186,7 @@ public class BubbleClockController implements ClockPlugin { @Override public void onTimeTick() { mAnalogClock.onTimeChanged(); + mView.onTimeChanged(); mDigitalClock.refresh(); mLockClock.refresh(); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java index 7ffee5d5232d..55088a800ec8 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java @@ -32,6 +32,7 @@ import com.android.keyguard.R; */ public class ClockLayout extends FrameLayout { + private static final int ANALOG_CLOCK_SHIFT_FACTOR = 3; /** * Clock face views. */ @@ -73,7 +74,14 @@ public class ClockLayout extends FrameLayout { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); + positionChildren(); + } + + void onTimeChanged() { + positionChildren(); + } + private void positionChildren() { final float offsetX = getBurnInOffset(mBurnInPreventionOffsetX * 2, true) - mBurnInPreventionOffsetX; final float offsetY = getBurnInOffset(mBurnInPreventionOffsetY * 2, false) @@ -89,9 +97,9 @@ public class ClockLayout extends FrameLayout { // Put the analog clock in the middle of the screen. if (mAnalogClock != null) { mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth())) - + offsetX); + + ANALOG_CLOCK_SHIFT_FACTOR * offsetX); mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight())) - + offsetY); + + ANALOG_CLOCK_SHIFT_FACTOR * offsetY); } } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index e373ca1d955c..06488b86c59e 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -142,8 +142,7 @@ public final class ClockManager { addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor)); - addBuiltinClock(() -> new StretchAnalogClockController(res, layoutInflater, - colorExtractor)); + addBuiltinClock(() -> new AnalogClockController(res, layoutInflater, colorExtractor)); // Store the size of the display for generation of clock preview. DisplayMetrics dm = res.getDisplayMetrics(); diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java deleted file mode 100644 index 8cdd6325638e..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.view.View; - -import java.util.Calendar; -import java.util.TimeZone; - -/** - * Analog clock where the minute hand extends off of the screen. - */ -public class StretchAnalogClock extends View { - - private static final int DEFAULT_COLOR = Color.parseColor("#F5C983"); - private static final float HOUR_STROKE_WIDTH = 60f; - private static final float MINUTE_STROKE_WIDTH = 20f; - private static final float CENTER_GAP_AND_CIRCLE_RADIUS = 80f; - - private final Paint mHourPaint = new Paint(); - private final Paint mMinutePaint = new Paint(); - private Calendar mTime = Calendar.getInstance(TimeZone.getDefault()); - private TimeZone mTimeZone; - - public StretchAnalogClock(Context context) { - this(context, null); - } - - public StretchAnalogClock(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - - /** - * Call when the time changes to update the clock hands. - */ - public void onTimeChanged() { - mTime.setTimeInMillis(System.currentTimeMillis()); - invalidate(); - } - - /** - * Call when the time zone has changed to update clock hands. - * - * @param timeZone The updated time zone that will be used. - */ - public void onTimeZoneChanged(TimeZone timeZone) { - mTime.setTimeZone(timeZone); - } - - /** - * Set the colors to use on the clock face. - * @param dark Darker color obtained from color palette. - * @param light Lighter color obtained from color palette. - */ - public void setClockColor(int dark, int light) { - mHourPaint.setColor(dark); - invalidate(); - } - - private void init() { - mHourPaint.setColor(DEFAULT_COLOR); - mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH); - mHourPaint.setAntiAlias(true); - mHourPaint.setStrokeCap(Paint.Cap.ROUND); - - mMinutePaint.setColor(Color.WHITE); - mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH); - mMinutePaint.setAntiAlias(true); - mMinutePaint.setStrokeCap(Paint.Cap.ROUND); - } - - @Override - protected void onDraw(Canvas canvas) { - final float centerX = getWidth() / 2f; - final float centerY = getHeight() / 2f; - - final float minutesRotation = mTime.get(Calendar.MINUTE) * 6f; - final float hoursRotation = mTime.get(Calendar.HOUR) * 30 - + mTime.get(Calendar.MINUTE) * 0.5f; - - // Compute length of clock hands. Hour hand is 60% the length from center to edge - // and minute hand is twice the length to make sure it extends past screen edge. - double sMinuteHandLengthFactor = Math.sin(2d * Math.PI * minutesRotation / 360d); - float sMinuteHandLength = (float) (2d * (centerY + (centerX - centerY) - * sMinuteHandLengthFactor * sMinuteHandLengthFactor)); - double sHourHandLengthFactor = Math.sin(2d * Math.PI * hoursRotation / 360d); - float sHourHandLength = (float) (0.6d * (centerY + (centerX - centerY) - * sHourHandLengthFactor * sHourHandLengthFactor)); - - canvas.save(); - - canvas.rotate(minutesRotation, centerX, centerY); - canvas.drawLine( - centerX, - centerY + CENTER_GAP_AND_CIRCLE_RADIUS, - centerX, - centerY - sMinuteHandLength, - mMinutePaint); - - canvas.rotate(hoursRotation - minutesRotation, centerX, centerY); - canvas.drawLine( - centerX, - centerY + CENTER_GAP_AND_CIRCLE_RADIUS, - centerX, - centerY - sHourHandLength, - mHourPaint); - - canvas.restore(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); - onTimeChanged(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 7985dfc75ad4..6c1d1f91830f 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -401,10 +401,10 @@ public class BatteryMeterView extends LinearLayout implements || mShowPercentMode == MODE_ON || mShowPercentMode == MODE_ESTIMATE) { if (!showing) { mBatteryPercentView = loadPercentView(); - if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor); if (mPercentageStyleId != 0) { // Only set if specified as attribute mBatteryPercentView.setTextAppearance(mPercentageStyleId); } + if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor); updatePercentText(); addView(mBatteryPercentView, new ViewGroup.LayoutParams( diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 665df777c1c1..03324777e4ea 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -16,47 +16,65 @@ package com.android.systemui.bubbles; +import android.os.UserHandle; import android.view.LayoutInflater; import com.android.systemui.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import java.util.Objects; + /** * Encapsulates the data and UI elements of a bubble. */ class Bubble { + private static final boolean DEBUG = false; + private static final String TAG = "Bubble"; + private final String mKey; + private final String mGroupId; private final BubbleExpandedView.OnBubbleBlockedListener mListener; private boolean mInflated; - - public BubbleView iconView; - public BubbleExpandedView expandedView; public NotificationEntry entry; + BubbleView iconView; + BubbleExpandedView expandedView; + + private static String groupId(NotificationEntry entry) { + UserHandle user = entry.notification.getUser(); + return user.getIdentifier() + '|' + entry.notification.getPackageName(); + } Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) { entry = e; mKey = e.key; + mGroupId = groupId(e); mListener = listener; } - /** @deprecated use the other constructor to defer View creation. */ - @Deprecated - Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView, - BubbleExpandedView.OnBubbleBlockedListener listener) { - this(e, listener); - inflate(inflater, stackView); - } - public String getKey() { return mKey; } + public String getGroupId() { + return mGroupId; + } + + public String getPackageName() { + return entry.notification.getPackageName(); + } + boolean isInflated() { return mInflated; } + public void updateDotVisibility() { + if (iconView != null) { + iconView.updateDotVisibility(); + } + } + void inflate(LayoutInflater inflater, BubbleStackView stackView) { if (mInflated) { return; @@ -73,10 +91,32 @@ class Bubble { mInflated = true; } + void setDismissed() { + entry.setBubbleDismissed(true); + // TODO: move this somewhere where it can be guaranteed not to run until safe from flicker + if (expandedView != null) { + expandedView.cleanUpExpandedState(); + } + } + void setEntry(NotificationEntry entry) { + this.entry = entry; if (mInflated) { iconView.update(entry); expandedView.update(entry); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Bubble)) return false; + Bubble bubble = (Bubble) o; + return Objects.equals(mKey, bubble.mKey); + } + + @Override + public int hashCode() { + return Objects.hash(mKey); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 0fcc9501e367..acdcfb2ea688 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -77,8 +77,7 @@ import javax.inject.Singleton; * The controller manages addition, removal, and visible state of bubbles on screen. */ @Singleton -public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListener, - ConfigurationController.ConfigurationListener { +public class BubbleController implements ConfigurationController.ConfigurationListener { private static final String TAG = "BubbleController"; @@ -174,6 +173,10 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe @Override public void onStateChanged(int newState) { mState = newState; + boolean shouldCollapse = (mState != SHADE); + if (shouldCollapse) { + collapseStack(); + } updateVisibility(); } } @@ -236,7 +239,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); } - mStackView.setOnBlockedListener(this); } } @@ -284,28 +286,38 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe if (mStackView == null) { return false; } - for (Bubble bubble : mBubbleData.getBubbles()) { - if (!bubble.entry.isBubbleDismissed()) { - return true; - } - } - return false; + return mBubbleData.hasBubbles(); } /** * Whether the stack of bubbles is expanded or not. */ public boolean isStackExpanded() { - return mStackView != null && mStackView.isExpanded(); + return mBubbleData.isExpanded(); + } + + /** + * Tell the stack of bubbles to expand. + */ + public void expandStack() { + mBubbleData.setExpanded(true); } /** * Tell the stack of bubbles to collapse. */ public void collapseStack() { - if (mStackView != null) { - mStackView.collapseStack(); - } + mBubbleData.setExpanded(false /* expanded */); + } + + void selectBubble(Bubble bubble) { + mBubbleData.setSelectedBubble(bubble); + } + + @VisibleForTesting + void selectBubble(String key) { + Bubble bubble = mBubbleData.getBubbleWithKey(key); + selectBubble(bubble); } /** @@ -314,8 +326,10 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * @param notificationKey the notification key for the bubble to be selected */ public void expandStackAndSelectBubble(String notificationKey) { - if (mStackView != null && mBubbleData.getBubble(notificationKey) != null) { - mStackView.setExpandedBubble(notificationKey); + Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey); + if (bubble != null) { + mBubbleData.setSelectedBubble(bubble); + mBubbleData.setExpanded(true); } } @@ -323,13 +337,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack. */ void dismissStack(@DismissReason int reason) { - if (mStackView == null) { - return; - } - mStackView.stackDismissed(reason); - - updateVisibility(); - mNotificationEntryManager.updateNotifications(); + mBubbleData.dismissAll(reason); } /** @@ -348,20 +356,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * @param notif the notification associated with this bubble. */ void updateBubble(NotificationEntry notif) { - if (mStackView != null && mBubbleData.getBubble(notif.key) != null) { - // It's an update - mStackView.updateBubble(notif); - } else { - // It's new - ensureStackViewCreated(); - mStackView.addBubble(notif); - } - Bubble bubble = mBubbleData.getBubble(notif.key); - if (shouldAutoExpand(notif)) { - mStackView.setSelectedBubble(bubble); - mStackView.setExpanded(true); - } - updateVisibility(); + mBubbleData.notificationEntryUpdated(notif); } /** @@ -371,23 +366,10 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe */ @MainThread void removeBubble(String key, int reason) { - if (mStackView != null) { - mStackView.removeBubble(key, reason); - } - mNotificationEntryManager.updateNotifications(); - updateVisibility(); - } - - @Override - public void onBubbleBlocked(NotificationEntry entry) { - Object[] bubbles = mBubbleData.getBubbles().toArray(); - for (int i = 0; i < bubbles.length; i++) { - NotificationEntry e = ((Bubble) bubbles[i]).entry; - boolean samePackage = entry.notification.getPackageName().equals( - e.notification.getPackageName()); - if (samePackage) { - removeBubble(entry.key, DISMISS_BLOCKED); - } + // TEMP: refactor to change this to pass entry + Bubble bubble = mBubbleData.getBubbleWithKey(key); + if (bubble != null) { + mBubbleData.notificationEntryRemoved(bubble.entry, reason); } } @@ -424,7 +406,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe updateShowInShadeForSuppressNotification(entry); entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed updateBubble(entry); - mStackView.updateDotVisibility(entry.key); } } @@ -446,44 +427,57 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } }; + @SuppressWarnings("FieldCanBeLocal") private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() { + @Override public void onBubbleAdded(Bubble bubble) { - + ensureStackViewCreated(); + mStackView.addBubble(bubble); } @Override public void onBubbleRemoved(Bubble bubble, int reason) { - + if (mStackView != null) { + mStackView.removeBubble(bubble); + } } public void onBubbleUpdated(Bubble bubble) { - + if (mStackView != null) { + mStackView.updateBubble(bubble); + } } @Override public void onOrderChanged(List<Bubble> bubbles) { - } @Override public void onSelectionChanged(Bubble selectedBubble) { - + if (mStackView != null) { + mStackView.setSelectedBubble(selectedBubble); + } } @Override public void onExpandedChanged(boolean expanded) { - + if (mStackView != null) { + mStackView.setExpanded(expanded); + } } @Override public void showFlyoutText(Bubble bubble, String text) { - + if (mStackView != null) { + mStackView.animateInFlyoutForBubble(bubble); + } } @Override public void apply() { - + mNotificationEntryManager.updateNotifications(); + updateVisibility(); } }; @@ -514,7 +508,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE); } else if (mStackView != null) { mStackView.setVisibility(INVISIBLE); - collapseStack(); } updateBubblesShowing(); } @@ -621,14 +614,14 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe @Override public void onTaskMovedToFront(RunningTaskInfo taskInfo) { if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) { - mStackView.collapseStack(); + mBubbleData.setExpanded(false); } } @Override public void onActivityLaunchOnSecondaryDisplayRerouted() { if (mStackView != null) { - mStackView.collapseStack(); + mBubbleData.setExpanded(false); } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index fe3f9d192cd5..259665dedf5b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -15,14 +15,24 @@ */ package com.android.systemui.bubbles; -import androidx.annotation.Nullable; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; + +import android.app.ActivityManager; +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; +import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Objects; import javax.inject.Inject; import javax.inject.Singleton; @@ -33,6 +43,8 @@ import javax.inject.Singleton; @Singleton public class BubbleData { + private static final String TAG = "BubbleData"; + /** * This interface reports changes to the state and appearance of bubbles which should be applied * as necessary to the UI. @@ -53,7 +65,7 @@ public class BubbleData { * A Bubble has been removed. A call to {@link #onOrderChanged(List)} will * follow. */ - void onBubbleRemoved(Bubble bubble, @BubbleController.DismissReason int reason); + void onBubbleRemoved(Bubble bubble, @DismissReason int reason); /** * An existing bubble has been updated. @@ -86,46 +98,253 @@ public class BubbleData { void apply(); } - private HashMap<String, Bubble> mBubbles = new HashMap<>(); + private final Context mContext; + private final List<Bubble> mBubbles = new ArrayList<>(); + private Bubble mSelectedBubble; + private boolean mExpanded; private Listener mListener; @VisibleForTesting @Inject - public BubbleData() {} + public BubbleData(Context context) { + mContext = context; + } + + public boolean hasBubbles() { + return !mBubbles.isEmpty(); + } + + public boolean isExpanded() { + return mExpanded; + } + + public boolean hasBubbleWithKey(String key) { + return getBubbleWithKey(key) != null; + } + + public void setExpanded(boolean expanded) { + if (setExpandedInternal(expanded)) { + mListener.apply(); + } + } + + public void setSelectedBubble(Bubble bubble) { + if (setSelectedBubbleInternal(bubble)) { + mListener.apply(); + } + } + + public void notificationEntryUpdated(NotificationEntry entry) { + Bubble bubble = getBubbleWithKey(entry.key); + if (bubble == null) { + // Create a new bubble + bubble = new Bubble(entry, this::onBubbleBlocked); + mBubbles.add(0, bubble); // TODO: reorder/group + mListener.onBubbleAdded(bubble); + } else { + // Updates an existing bubble + bubble.setEntry(entry); + mListener.onBubbleUpdated(bubble); + } + if (shouldAutoExpand(entry)) { + setSelectedBubbleInternal(bubble); + if (!mExpanded) { + setExpandedInternal(true); + } + } else if (mSelectedBubble == null) { + setSelectedBubbleInternal(bubble); + } + // TODO: reorder/group + mListener.apply(); + } + + public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) { + int indexToRemove = indexForKey(entry.key); + if (indexToRemove >= 0) { + Bubble removed = mBubbles.remove(indexToRemove); + removed.setDismissed(); + mListener.onBubbleRemoved(removed, reason); + maybeSendDeleteIntent(reason, removed.entry); + + if (mBubbles.isEmpty()) { + setExpandedInternal(false); + setSelectedBubbleInternal(null); + } else if (removed == mSelectedBubble) { + int newIndex = Math.min(indexToRemove, mBubbles.size() - 1); + Bubble newSelected = mBubbles.get(newIndex); + setSelectedBubbleInternal(newSelected); + } + // TODO: reorder/group + mListener.apply(); + } + } + + public void dismissAll(@DismissReason int reason) { + boolean changed = setExpandedInternal(false); + while (!mBubbles.isEmpty()) { + Bubble bubble = mBubbles.remove(0); + bubble.setDismissed(); + maybeSendDeleteIntent(reason, bubble.entry); + mListener.onBubbleRemoved(bubble, reason); + changed = true; + } + if (setSelectedBubbleInternal(null)) { + changed = true; + } + if (changed) { + // TODO: reorder/group + mListener.apply(); + } + } /** - * The set of bubbles. + * Requests a change to the selected bubble. Calls {@link Listener#onSelectionChanged} if + * the value changes. + * + * @param bubble the new selected bubble + * @return true if the state changed as a result */ - public Collection<Bubble> getBubbles() { - return mBubbles.values(); + private boolean setSelectedBubbleInternal(Bubble bubble) { + if (Objects.equals(bubble, mSelectedBubble)) { + return false; + } + if (bubble != null && !mBubbles.contains(bubble)) { + Log.e(TAG, "Cannot select bubble which doesn't exist!" + + " (" + bubble + ") bubbles=" + mBubbles); + return false; + } + if (mExpanded) { + // TODO: bubble.markAsActive() ? + bubble.entry.setShowInShadeWhenBubble(false); + } + mSelectedBubble = bubble; + mListener.onSelectionChanged(mSelectedBubble); + return true; + } + + + /** + * Requests a change to the expanded state. Calls {@link Listener#onExpandedChanged} if + * the value changes. + * + * @param shouldExpand the new requested state + * @return true if the state changed as a result + */ + private boolean setExpandedInternal(boolean shouldExpand) { + if (mExpanded == shouldExpand) { + return false; + } + if (shouldExpand) { + if (mBubbles.isEmpty()) { + Log.e(TAG, "Attempt to expand stack when empty!"); + return false; + } + if (mSelectedBubble == null) { + Log.e(TAG, "Attempt to expand stack without selected bubble!"); + return false; + } + // TODO: bubble.markAsActive() ? + mSelectedBubble.entry.setShowInShadeWhenBubble(false); + } + // TODO: reorder/regroup + mExpanded = shouldExpand; + mListener.onExpandedChanged(mExpanded); + return true; } - @Nullable - public Bubble getBubble(String key) { - return mBubbles.get(key); + private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) { + if (reason == BubbleController.DISMISS_USER_GESTURE) { + Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata(); + PendingIntent deleteIntent = bubbleMetadata != null + ? bubbleMetadata.getDeleteIntent() + : null; + if (deleteIntent != null) { + try { + deleteIntent.send(); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "Failed to send delete intent for bubble with key: " + entry.key); + } + } + } } - public void addBubble(Bubble b) { - mBubbles.put(b.getKey(), b); + private void onBubbleBlocked(NotificationEntry entry) { + boolean changed = false; + final String blockedPackage = entry.notification.getPackageName(); + for (Iterator<Bubble> i = mBubbles.iterator(); i.hasNext(); ) { + Bubble bubble = i.next(); + if (bubble.getPackageName().equals(blockedPackage)) { + i.remove(); + mListener.onBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED); + changed = true; + } + } + if (changed) { + // TODO: reorder/group + mListener.apply(); + } } - @Nullable - public Bubble removeBubble(String key) { - return mBubbles.remove(key); + private int indexForKey(String key) { + for (int i = 0; i < mBubbles.size(); i++) { + Bubble bubble = mBubbles.get(i); + if (bubble.getKey().equals(key)) { + return i; + } + } + return -1; } - public void updateBubble(String key, NotificationEntry newEntry) { - Bubble oldBubble = mBubbles.get(key); - if (oldBubble != null) { - oldBubble.setEntry(newEntry); + private Bubble removeBubbleWithKey(String key) { + for (int i = 0; i < mBubbles.size(); i++) { + Bubble bubble = mBubbles.get(i); + if (bubble.getKey().equals(key)) { + mBubbles.remove(i); + return bubble; + } } + return null; + } + + /** + * The set of bubbles. + * + * @deprecated + */ + @Deprecated + public Collection<Bubble> getBubbles() { + return Collections.unmodifiableList(mBubbles); } - public void clear() { - mBubbles.clear(); + @VisibleForTesting(visibility = PRIVATE) + Bubble getBubbleWithKey(String key) { + for (int i = 0; i < mBubbles.size(); i++) { + Bubble bubble = mBubbles.get(i); + if (bubble.getKey().equals(key)) { + return bubble; + } + } + return null; } public void setListener(Listener listener) { mListener = listener; } -} + + boolean shouldAutoExpand(NotificationEntry entry) { + Notification.BubbleMetadata metadata = entry.getBubbleMetadata(); + return metadata != null && metadata.getAutoExpandBubble() + && isForegroundApp(entry.notification.getPackageName()); + } + + /** + * Return true if the applications with the package name is running in foreground. + * + * @param pkgName application package name. + */ + boolean isForegroundApp(String pkgName) { + ActivityManager am = mContext.getSystemService(ActivityManager.class); + List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1 /* maxNum */); + return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 424cd5544e50..18b2e37e31ec 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -20,8 +20,6 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.annotation.NonNull; -import android.app.Notification; -import android.app.PendingIntent; import android.content.Context; import android.content.res.Resources; import android.graphics.Outline; @@ -54,9 +52,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ViewClippingUtil; -import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.bubbles.animation.ExpandedAnimationController; import com.android.systemui.bubbles.animation.PhysicsAnimationLayout; import com.android.systemui.bubbles.animation.StackAnimationController; @@ -359,14 +355,13 @@ public class BubbleStackView extends FrameLayout { } switch (action) { case AccessibilityNodeInfo.ACTION_DISMISS: - Dependency.get(BubbleController.class).dismissStack( - BubbleController.DISMISS_ACCESSIBILITY_ACTION); + mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION); return true; case AccessibilityNodeInfo.ACTION_COLLAPSE: - collapseStack(); + mBubbleData.setExpanded(false); return true; case AccessibilityNodeInfo.ACTION_EXPAND: - expandStack(); + mBubbleData.setExpanded(true); return true; } return false; @@ -393,9 +388,9 @@ public class BubbleStackView extends FrameLayout { * @param key the {@link NotificationEntry#key} associated with the bubble. */ public void updateDotVisibility(String key) { - Bubble b = mBubbleData.getBubble(key); + Bubble b = mBubbleData.getBubbleWithKey(key); if (b != null) { - b.iconView.updateDotVisibility(); + b.updateDotVisibility(); } } @@ -407,16 +402,6 @@ public class BubbleStackView extends FrameLayout { } /** - * Sets the listener to notify when a bubble is blocked. - */ - public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) { - mBlockedListener = listener; - for (Bubble b : mBubbleData.getBubbles()) { - b.expandedView.setOnBlockedListener(mBlockedListener); - } - } - - /** * Whether the stack of bubbles is expanded or not. */ public boolean isExpanded() { @@ -445,7 +430,7 @@ public class BubbleStackView extends FrameLayout { */ @Deprecated void setExpandedBubble(String key) { - Bubble bubbleToExpand = mBubbleData.getBubble(key); + Bubble bubbleToExpand = mBubbleData.getBubbleWithKey(key); if (bubbleToExpand != null) { setSelectedBubble(bubbleToExpand); bubbleToExpand.entry.setShowInShadeWhenBubble(false); @@ -466,11 +451,36 @@ public class BubbleStackView extends FrameLayout { } } + // via BubbleData.Listener + void addBubble(Bubble bubble) { + bubble.inflate(mInflater, this); + mBubbleContainer.addView(bubble.iconView, 0, + new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + ViewClippingUtil.setClippingDeactivated(bubble.iconView, true, mClippingParameters); + requestUpdate(); + logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED); + } + + // via BubbleData.Listener + void removeBubble(Bubble bubble) { + // Remove it from the views + int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView); + mBubbleContainer.removeViewAt(removedIndex); + logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); + } + + // via BubbleData.Listener + void updateBubble(Bubble bubble) { + requestUpdate(); + logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED); + } + /** * Changes the currently selected bubble. If the stack is already expanded, the newly selected * bubble will be shown immediately. This does not change the expanded state or change the * position of any bubble. */ + // via BubbleData.Listener public void setSelectedBubble(Bubble bubbleToSelect) { if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) { return; @@ -489,7 +499,8 @@ public class BubbleStackView extends FrameLayout { logBubbleEvent(previouslySelected, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); logBubbleEvent(bubbleToSelect, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED); notifyExpansionChanged(previouslySelected.entry, false /* expanded */); - notifyExpansionChanged(bubbleToSelect.entry, true /* expanded */); + notifyExpansionChanged(bubbleToSelect == null ? null : bubbleToSelect.entry, + true /* expanded */); }); } } @@ -497,13 +508,15 @@ public class BubbleStackView extends FrameLayout { /** * Changes the expanded state of the stack. * - * @param expanded whether the bubble stack should appear expanded + * @param shouldExpand whether the bubble stack should appear expanded */ - public void setExpanded(boolean expanded) { - if (expanded == mIsExpanded) { + // via BubbleData.Listener + public void setExpanded(boolean shouldExpand) { + boolean wasExpanded = mIsExpanded; + if (shouldExpand == wasExpanded) { return; } - if (mIsExpanded) { + if (wasExpanded) { // Collapse the stack animateExpansion(false /* expand */); logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); @@ -518,131 +531,17 @@ public class BubbleStackView extends FrameLayout { } /** - * Adds a bubble to the top of the stack. - * - * @param entry the notification to add to the stack of bubbles. - */ - void addBubble(NotificationEntry entry) { - Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener); - mBubbleData.addBubble(b); - - mBubbleContainer.addView(b.iconView, 0, - new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); - ViewClippingUtil.setClippingDeactivated(b.iconView, true, mClippingParameters); - - requestUpdate(); - logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED); - - animateInFlyoutForBubble(b); - } - - /** - * Remove a bubble from the stack. - */ - void removeBubble(String key, int reason) { - Bubble b = mBubbleData.removeBubble(key); - if (b == null) { - return; - } - setBubbleDismissed(b, reason); - - // Remove it from the views - int removedIndex = mBubbleContainer.indexOfChild(b.iconView); - mBubbleContainer.removeViewAt(removedIndex); - - int bubbleCount = mBubbleContainer.getChildCount(); - if (bubbleCount == 0) { - // If no bubbles remain, collapse the entire stack. - collapseStack(); - return; - } else if (b.equals(mExpandedBubble)) { - // Was the current bubble just removed? - // If we have other bubbles and are expanded go to the next one or previous - // if the bubble removed was last - int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1; - BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex); - if (mIsExpanded) { - setExpandedBubble(expandedBubble.getKey()); - } else { - mExpandedBubble = null; - } - } - // TODO: consider logging reason code - logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); - } - - /** * Dismiss the stack of bubbles. + * @deprecated */ + @Deprecated void stackDismissed(int reason) { - for (Bubble bubble : mBubbleData.getBubbles()) { - setBubbleDismissed(bubble, reason); - } - mBubbleData.clear(); - collapseStack(); - mBubbleContainer.removeAllViews(); - mExpandedViewContainer.removeAllViews(); - // TODO: consider logging reason code + mBubbleData.dismissAll(reason); logBubbleEvent(null /* no bubble associated with bubble stack dismiss */, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED); } /** - * Marks the notification entry as dismissed & calls any delete intents for the bubble. - * - * <p>Note: This does not remove the Bubble from BubbleData. - * - * @param bubble the Bubble being dismissed - * @param reason code for the reason the dismiss was triggered - * @see BubbleController.DismissReason - */ - private void setBubbleDismissed(Bubble bubble, @DismissReason int reason) { - if (DEBUG) { - Log.d(TAG, "dismissBubble: " + bubble + " reason=" + reason); - } - bubble.entry.setBubbleDismissed(true); - bubble.expandedView.cleanUpExpandedState(); - - if (reason == BubbleController.DISMISS_USER_GESTURE) { - Notification.BubbleMetadata bubbleMetadata = bubble.entry.getBubbleMetadata(); - PendingIntent deleteIntent = bubbleMetadata != null - ? bubbleMetadata.getDeleteIntent() - : null; - if (deleteIntent != null) { - try { - deleteIntent.send(); - } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "Failed to send delete intent for bubble with key: " - + (bubble.entry != null ? bubble.entry.key : " null entry")); - } - } - } - } - - /** - * Updates a bubble in the stack. - * @param entry the entry to update in the stack. - */ - public void updateBubble(NotificationEntry entry) { - Bubble b = mBubbleData.getBubble(entry.key); - mBubbleData.updateBubble(entry.key, entry); - - if (!mIsExpanded) { - // If alerting it gets promoted to top of the stack. - if (mBubbleContainer.indexOfChild(b.iconView) != 0) { - mBubbleContainer.moveViewTo(b.iconView, 0); - } - requestUpdate(); - animateInFlyoutForBubble(b /* bubble */); - } - if (mIsExpanded && entry.equals(mExpandedBubble.entry)) { - entry.setShowInShadeWhenBubble(false); - requestUpdate(); - } - logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED); - } - - /** * @return the view the touch event is on */ @Nullable @@ -670,7 +569,7 @@ public class BubbleStackView extends FrameLayout { return this; } - public View getFlyoutView() { + View getFlyoutView() { return mFlyout; } @@ -683,13 +582,8 @@ public class BubbleStackView extends FrameLayout { */ @Deprecated @MainThread - public void collapseStack() { - if (mIsExpanded) { - // TODO: Save opened bubble & move it to top of stack - animateExpansion(false /* shouldExpand */); - notifyExpansionChanged(mExpandedBubble.entry, mIsExpanded); - logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); - } + void collapseStack() { + mBubbleData.setExpanded(false); } /** @@ -712,12 +606,8 @@ public class BubbleStackView extends FrameLayout { */ @Deprecated @MainThread - public void expandStack() { - if (!mIsExpanded) { - String expandedBubbleKey = getBubbleAt(0).getKey(); - setExpandedBubble(expandedBubbleKey); - logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED); - } + void expandStack() { + mBubbleData.setExpanded(true); } /** diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java index f8825690056e..e7878c69873c 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java @@ -119,6 +119,17 @@ public class GlobalActionsGridLayout extends MultiListLayout { } } updateSnapPosition(); + updateSeparatedButtonSize(); + } + + private void updateSeparatedButtonSize() { + ViewGroup separated = getSeparatedView(); + if (separated.getChildCount() == 1) { + View onlyChild = separated.getChildAt(0); + ViewGroup.LayoutParams childParams = onlyChild.getLayoutParams(); + childParams.width = ViewGroup.LayoutParams.MATCH_PARENT; + childParams.height = ViewGroup.LayoutParams.MATCH_PARENT; + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 6f50baa53e38..4590470697ea 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -25,9 +25,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.graphics.Rect; import android.graphics.Typeface; -import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.icu.text.DateFormat; import android.icu.text.DisplayContext; @@ -37,13 +35,8 @@ import android.os.Handler; import android.os.Trace; import android.provider.Settings; import android.service.notification.ZenModeConfig; -import android.text.SpannableStringBuilder; -import android.text.Spanned; import android.text.TextUtils; -import android.text.style.DynamicDrawableSpan; -import android.text.style.ImageSpan; import android.text.style.StyleSpan; -import android.util.MathUtils; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; @@ -205,9 +198,9 @@ public class KeyguardSliceProvider extends SliceProvider implements addMediaLocked(builder); } else { builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText)); - addNextAlarmLocked(builder); - addZenModeLocked(builder); } + addNextAlarmLocked(builder); + addZenModeLocked(builder); addPrimaryActionLocked(builder); slice = builder.build(); } @@ -221,32 +214,25 @@ public class KeyguardSliceProvider extends SliceProvider implements protected void addMediaLocked(ListBuilder listBuilder) { if (mMediaMetaData != null) { - SpannableStringBuilder builder = new SpannableStringBuilder(); - - Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); - if (notificationIcon != null) { - Drawable drawable = notificationIcon.loadDrawable(getContext()); - Rect mediaBounds = new Rect(0 /* left */, 0 /* top */, - drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - int iconHeaderSize = getContext().getResources() - .getDimensionPixelSize(R.dimen.header_icon_size); - MathUtils.fitRect(mediaBounds, iconHeaderSize); - drawable.setBounds(mediaBounds); - builder.append("# "); - builder.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_CENTER), - 0 /* start */, 1 /* end */, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE); if (TextUtils.isEmpty(title)) { title = getContext().getResources().getString(R.string.music_controls_no_title); } - builder.append(title); - listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(builder)); + listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(title)); CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST); if (!TextUtils.isEmpty(album)) { - listBuilder.addRow(new RowBuilder(mMediaUri).setTitle(album)); + RowBuilder albumBuilder = new RowBuilder(mMediaUri); + albumBuilder.setTitle(album); + + Icon mediaIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); + IconCompat mediaIconCompat = mediaIcon == null ? null + : IconCompat.createFromIcon(getContext(), mediaIcon); + if (mediaIconCompat != null) { + albumBuilder.addEndItem(mediaIconCompat, ListBuilder.ICON_IMAGE); + } + + listBuilder.addRow(albumBuilder); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java index 0eeaa9b75382..7de8b74f30cc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java @@ -139,43 +139,49 @@ public class QSCarrierGroup extends LinearLayout implements @Override public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { - if (info.anySimReady) { - boolean[] slotSeen = new boolean[SIM_SLOTS]; - if (info.listOfCarriers.length == info.subscriptionIds.length) { - for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) { - int slot = getSlotIndex(info.subscriptionIds[i]); - if (slot >= SIM_SLOTS) { - Log.w(TAG, "updateInfoCarrier - slot: " + slot); - continue; - } - if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { - Log.e(TAG, - "Invalid SIM slot index for subscription: " - + info.subscriptionIds[i]); - continue; + if (info.airplaneMode) { + setVisibility(View.GONE); + } else { + setVisibility(View.VISIBLE); + if (info.anySimReady) { + boolean[] slotSeen = new boolean[SIM_SLOTS]; + if (info.listOfCarriers.length == info.subscriptionIds.length) { + for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) { + int slot = getSlotIndex(info.subscriptionIds[i]); + if (slot >= SIM_SLOTS) { + Log.w(TAG, "updateInfoCarrier - slot: " + slot); + continue; + } + if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + Log.e(TAG, + "Invalid SIM slot index for subscription: " + + info.subscriptionIds[i]); + continue; + } + mInfos[slot].visible = true; + slotSeen[slot] = true; + mCarrierGroups[slot].setCarrierText( + info.listOfCarriers[i].toString().trim()); + mCarrierGroups[slot].setVisibility(View.VISIBLE); } - mInfos[slot].visible = true; - slotSeen[slot] = true; - mCarrierGroups[slot].setCarrierText(info.listOfCarriers[i].toString().trim()); - mCarrierGroups[slot].setVisibility(View.VISIBLE); - } - for (int i = 0; i < SIM_SLOTS; i++) { - if (!slotSeen[i]) { - mInfos[i].visible = false; - mCarrierGroups[i].setVisibility(View.GONE); + for (int i = 0; i < SIM_SLOTS; i++) { + if (!slotSeen[i]) { + mInfos[i].visible = false; + mCarrierGroups[i].setVisibility(View.GONE); + } } + } else { + Log.e(TAG, "Carrier information arrays not of same length"); } } else { - Log.e(TAG, "Carrier information arrays not of same length"); - } - } else { - mInfos[0].visible = false; - mCarrierGroups[0].setCarrierText(info.carrierText); - mCarrierGroups[0].setVisibility(View.VISIBLE); - for (int i = 1; i < SIM_SLOTS; i++) { - mInfos[i].visible = false; - mCarrierGroups[i].setCarrierText(""); - mCarrierGroups[i].setVisibility(View.GONE); + mInfos[0].visible = false; + mCarrierGroups[0].setCarrierText(info.carrierText); + mCarrierGroups[0].setVisibility(View.VISIBLE); + for (int i = 1; i < SIM_SLOTS; i++) { + mInfos[i].visible = false; + mCarrierGroups[i].setCarrierText(""); + mCarrierGroups[i].setVisibility(View.GONE); + } } } handleUpdateState(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java index 91cd65259d73..8a360ee2e4eb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java @@ -15,11 +15,10 @@ package com.android.systemui.qs.tileimpl; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.service.quicksettings.Tile; -import android.text.SpannableStringBuilder; import android.text.TextUtils; -import android.text.style.ForegroundColorSpan; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -27,6 +26,7 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import com.android.settingslib.Utils; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSIconView; @@ -46,6 +46,8 @@ public class QSTileView extends QSTileBaseView { private ViewGroup mLabelContainer; private View mExpandIndicator; private View mExpandSpace; + private ColorStateList mColorLabelDefault; + private ColorStateList mColorLabelUnavailable; public QSTileView(Context context, QSIconView icon) { this(context, icon, false); @@ -62,6 +64,11 @@ public class QSTileView extends QSTileBaseView { createLabel(); setOrientation(VERTICAL); setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); + mColorLabelDefault = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary); + // The text color for unavailable tiles is textColorSecondary, same as secondaryLabel for + // contrast purposes + mColorLabelUnavailable = Utils.getColorAttr(getContext(), + android.R.attr.textColorSecondary); } TextView getLabel() { @@ -111,12 +118,8 @@ public class QSTileView extends QSTileBaseView { protected void handleStateChanged(QSTile.State state) { super.handleStateChanged(state); if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) { - if (state.state == Tile.STATE_UNAVAILABLE) { - int color = QSTileImpl.getColorForState(getContext(), state.state); - state.label = new SpannableStringBuilder().append(state.label, - new ForegroundColorSpan(color), - SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE); - } + mLabel.setTextColor(state.state == Tile.STATE_UNAVAILABLE ? mColorLabelUnavailable + : mColorLabelDefault); mState = state.state; mLabel.setText(state.label); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 72559f56aaa1..56dbe2b2cd99 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -608,6 +608,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mConnectionCallbacks.add(listener); listener.onConnectionChanged(mOverviewProxy != null); listener.onInteractionFlagsChanged(mInteractionFlags); + listener.onBackButtonAlphaChanged(mBackButtonAlpha, false); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index 907b3ad08c8c..85848cafbfcb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -151,7 +151,7 @@ public class NavigationBarController implements Callbacks { ? Dependency.get(AutoHideController.class) : new AutoHideController(context, mHandler); navBar.setAutoHideController(autoHideController); - navBar.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + navBar.restoreSystemUiVisibilityState(); mNavigationBars.append(displayId, navBar); if (result != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 2e35f0686d55..97f2dd0749f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -22,7 +22,6 @@ import android.os.Trace; import android.util.Log; import android.view.View; import android.view.ViewGroup; -import android.view.ViewParent; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleData; @@ -120,7 +119,7 @@ public class NotificationViewHierarchyManager { for (int i = 0; i < N; i++) { NotificationEntry ent = activeNotifications.get(i); if (ent.isRowDismissed() || ent.isRowRemoved() - || (mBubbleData.getBubble(ent.key) != null && !ent.showInShadeWhenBubble())) { + || (mBubbleData.hasBubbleWithKey(ent.key) && !ent.showInShadeWhenBubble())) { // we don't want to update removed notifications because they could // temporarily become children if they were isolated before. continue; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java index 4ced702f479e..959342b27b56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java @@ -141,6 +141,11 @@ public class ButtonDispatcher { public void setVisibility(int visibility) { if (mVisibility == visibility) return; + if (mFadeAnimator != null) { + mFadeAnimator.cancel(); + mFadeAnimator = null; + } + mVisibility = visibility; final int N = mViews.size(); for (int i = 0; i < N; i++) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 9eecdb924448..4d2b56c5e81a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -123,6 +123,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private static final boolean DEBUG = false; private static final String EXTRA_DISABLE_STATE = "disabled_state"; private static final String EXTRA_DISABLE2_STATE = "disabled2_state"; + private static final String EXTRA_SYSTEM_UI_VISIBILITY = "system_ui_visibility"; /** Allow some time inbetween the long press for back and recents. */ private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; @@ -156,7 +157,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private Locale mLocale; private int mLayoutDirection; - private int mSystemUiVisibility; + private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; private LightBarController mLightBarController; private AutoHideController mAutoHideController; @@ -277,6 +278,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (savedInstanceState != null) { mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0); mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0); + mSystemUiVisibility = savedInstanceState.getInt(EXTRA_SYSTEM_UI_VISIBILITY, 0); } mAccessibilityManagerWrapper.addCallback(mAccessibilityListener); @@ -363,6 +365,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback super.onSaveInstanceState(outState); outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2); + outState.putInt(EXTRA_SYSTEM_UI_VISIBILITY, mSystemUiVisibility); if (mNavigationBarView != null) { mNavigationBarView.getLightTransitionsController().saveState(outState); } @@ -492,13 +495,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } } - /** - * Sets System UI flags to {@link NavigationBarFragment}. - * - * @see View#setSystemUiVisibility(int) - */ - public void setSystemUiVisibility(int systemUiVisibility) { - mSystemUiVisibility = systemUiVisibility; + /** Restores the System UI flags saved state to {@link NavigationBarFragment}. */ + public void restoreSystemUiVisibilityState() { final int barMode = computeBarMode(0, mSystemUiVisibility); if (barMode != -1) { mNavigationBarMode = barMode; diff --git a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java index 45fc7563a5c3..1862ed3a4de8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java +++ b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java @@ -22,6 +22,9 @@ import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; +import androidx.core.view.ViewCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; + import com.android.keyguard.AlphaOptimizedImageButton; import com.android.systemui.R; @@ -31,7 +34,7 @@ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton { private static final int[] OPTED_OUT_STATE = new int[] { R.attr.optedOut }; private ConfirmedTapListener mConfirmedTapListener; - private boolean mComponentEnabled = false; + private boolean mCaptionsEnabled = false; private boolean mOptedOut = false; private GestureDetector mGestureDetector; @@ -39,16 +42,14 @@ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton { new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapConfirmed(MotionEvent e) { - if (mConfirmedTapListener != null) { - mConfirmedTapListener.onConfirmedTap(); - return true; - } - return false; + return tryToSendTapConfirmedEvent(); } }; public CaptionsToggleImageButton(Context context, AttributeSet attrs) { super(context, attrs); + this.setContentDescription( + getContext().getString(R.string.volume_odi_captions_content_description)); } @Override @@ -66,16 +67,32 @@ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton { return state; } - Runnable setComponentEnabled(boolean isComponentEnabled) { - this.mComponentEnabled = isComponentEnabled; + Runnable setCaptionsEnabled(boolean areCaptionsEnabled) { + this.mCaptionsEnabled = areCaptionsEnabled; + + ViewCompat.replaceAccessibilityAction( + this, + AccessibilityActionCompat.ACTION_CLICK, + mCaptionsEnabled + ? getContext().getString(R.string.volume_odi_captions_hint_disable) + : getContext().getString(R.string.volume_odi_captions_hint_enable), + (view, commandArguments) -> tryToSendTapConfirmedEvent()); - return this.setImageResourceAsync(this.mComponentEnabled + return this.setImageResourceAsync(mCaptionsEnabled ? R.drawable.ic_volume_odi_captions : R.drawable.ic_volume_odi_captions_disabled); } - boolean getComponentEnabled() { - return this.mComponentEnabled; + private boolean tryToSendTapConfirmedEvent() { + if (mConfirmedTapListener != null) { + mConfirmedTapListener.onConfirmedTap(); + return true; + } + return false; + } + + boolean getCaptionsEnabled() { + return this.mCaptionsEnabled; } /** Sets whether or not the current stream has opted out of captions */ diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 509537089bf8..8d9c5a3740b2 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -596,9 +596,9 @@ public class VolumeDialogImpl implements VolumeDialog, } private void updateCaptionsIcon() { - boolean componentEnabled = mController.areCaptionsEnabled(); - if (mODICaptionsIcon.getComponentEnabled() != componentEnabled) { - mHandler.post(mODICaptionsIcon.setComponentEnabled(componentEnabled)); + boolean captionsEnabled = mController.areCaptionsEnabled(); + if (mODICaptionsIcon.getCaptionsEnabled() != captionsEnabled) { + mHandler.post(mODICaptionsIcon.setCaptionsEnabled(captionsEnabled)); } boolean isOptedOut = mController.isCaptionStreamOptedOut(); @@ -878,7 +878,6 @@ public class VolumeDialogImpl implements VolumeDialog, } view.setContentDescription(mContext.getString(currStateResId)); - view.setAccessibilityDelegate(new AccessibilityDelegate() { public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java index 8e061cc84396..df534d79f730 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java @@ -37,6 +37,7 @@ import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.Handler; import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -81,6 +82,8 @@ public class CarrierTextControllerTest extends SysuiTestCase { private ConnectivityManager mConnectivityManager; @Mock private TelephonyManager mTelephonyManager; + @Mock + private SubscriptionManager mSubscriptionManager; private CarrierTextController.CarrierTextCallbackInfo mCarrierTextCallbackInfo; private CarrierTextController mCarrierTextController; @@ -94,6 +97,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { mContext.addMockSystemService(WifiManager.class, mWifiManager); mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager); mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager); + mContext.addMockSystemService(SubscriptionManager.class, mSubscriptionManager); mDependency.injectMockDependency(WakefulnessLifecycle.class); mDependency.injectTestDependency(Dependency.MAIN_HANDLER, new Handler(mTestableLooper.getLooper())); @@ -169,6 +173,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { list.add(TEST_SUBSCRIPTION); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY); when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = @@ -192,6 +197,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { list.add(TEST_SUBSCRIPTION_ROAMING); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY); when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java index 26fa62b77d9a..4bb2395cc92d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java @@ -38,9 +38,9 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper -public final class StretchAnalogClockControllerTest extends SysuiTestCase { +public final class AnalogClockControllerTest extends SysuiTestCase { - private StretchAnalogClockController mClockController; + private AnalogClockController mClockController; @Mock SysuiColorExtractor mMockColorExtractor; @Before @@ -49,7 +49,7 @@ public final class StretchAnalogClockControllerTest extends SysuiTestCase { Resources res = getContext().getResources(); LayoutInflater layoutInflater = LayoutInflater.from(getContext()); - mClockController = new StretchAnalogClockController(res, layoutInflater, + mClockController = new AnalogClockController(res, layoutInflater, mMockColorExtractor); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 20f539bbebe5..40a53571ddf0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -146,7 +146,7 @@ public class BubbleControllerTest extends SysuiTestCase { when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData); when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel); - mBubbleData = new BubbleData(); + mBubbleData = new BubbleData(mContext); mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController, mBubbleData, mConfigurationController); mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener); @@ -177,25 +177,27 @@ public class BubbleControllerTest extends SysuiTestCase { public void testRemoveBubble() { mBubbleController.updateBubble(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); - + verify(mNotificationEntryManager).updateNotifications(); verify(mBubbleStateChangeListener).onHasBubblesChanged(true); mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE); assertFalse(mStatusBarWindowController.getBubblesShowing()); assertTrue(mRow.getEntry().isBubbleDismissed()); - verify(mNotificationEntryManager).updateNotifications(); + verify(mNotificationEntryManager, times(2)).updateNotifications(); verify(mBubbleStateChangeListener).onHasBubblesChanged(false); } @Test public void testDismissStack() { mBubbleController.updateBubble(mRow.getEntry()); + verify(mNotificationEntryManager, times(1)).updateNotifications(); mBubbleController.updateBubble(mRow2.getEntry()); + verify(mNotificationEntryManager, times(2)).updateNotifications(); assertTrue(mBubbleController.hasBubbles()); mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE); assertFalse(mStatusBarWindowController.getBubblesShowing()); - verify(mNotificationEntryManager).updateNotifications(); + verify(mNotificationEntryManager, times(3)).updateNotifications(); assertTrue(mRow.getEntry().isBubbleDismissed()); assertTrue(mRow2.getEntry().isBubbleDismissed()); } @@ -215,7 +217,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand the stack BubbleStackView stackView = mBubbleController.getStackView(); - stackView.expandStack(); + mBubbleController.expandStack(); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); assertTrue(mStatusBarWindowController.getBubbleExpanded()); @@ -224,7 +226,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertFalse(mRow.getEntry().showInShadeWhenBubble()); // Collapse - stackView.collapseStack(); + mBubbleController.collapseStack(); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); assertFalse(mBubbleController.isStackExpanded()); assertFalse(mStatusBarWindowController.getBubbleExpanded()); @@ -245,23 +247,24 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand BubbleStackView stackView = mBubbleController.getStackView(); - stackView.expandStack(); + mBubbleController.expandStack(); assertTrue(mBubbleController.isStackExpanded()); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); - - // Last added is the one that is expanded - assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); - assertFalse(mRow2.getEntry().showInShadeWhenBubble()); + verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); - // Switch which bubble is expanded - stackView.setExpandedBubble(mRow.getEntry()); + // First added is the one that is expanded assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry()); assertFalse(mRow.getEntry().showInShadeWhenBubble()); + // Switch which bubble is expanded + mBubbleController.selectBubble(mRow2.getEntry().key); + stackView.setExpandedBubble(mRow2.getEntry()); + assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); + assertFalse(mRow2.getEntry().showInShadeWhenBubble()); + // collapse for previous bubble - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key); + verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); // expand for selected bubble - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); + verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); // Collapse mBubbleController.collapseStack(); @@ -280,7 +283,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand BubbleStackView stackView = mBubbleController.getStackView(); - stackView.expandStack(); + mBubbleController.expandStack(); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); @@ -299,30 +302,30 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand BubbleStackView stackView = mBubbleController.getStackView(); - stackView.expandStack(); + mBubbleController.expandStack(); assertTrue(mBubbleController.isStackExpanded()); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); + verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); - // Last added is the one that is expanded - assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); - assertFalse(mRow2.getEntry().showInShadeWhenBubble()); + // First added is the one that is expanded + assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry()); + assertFalse(mRow.getEntry().showInShadeWhenBubble()); // Dismiss currently expanded mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(), BubbleController.DISMISS_USER_GESTURE); - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key); + verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); // Make sure next bubble is selected - assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry()); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); + assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); + verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); // Dismiss that one mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(), BubbleController.DISMISS_USER_GESTURE); // Make sure state changes and collapse happens - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); + verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key); verify(mBubbleStateChangeListener).onHasBubblesChanged(false); assertFalse(mBubbleController.hasBubbles()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java index 801308fc77da..da31134d13b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java @@ -48,7 +48,7 @@ public class BubbleStackViewTest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mStackView = new BubbleStackView(mContext, new BubbleData(), null); + mStackView = new BubbleStackView(mContext, new BubbleData(getContext()), null); mBubble.entry = mNotifEntry; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index 63d42a70aa98..c534de7e24a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.content.ContentResolver; @@ -102,6 +103,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void onBindSlice_readsMedia() { MediaMetadata metadata = mock(MediaMetadata.class); + when(metadata.getText(any())).thenReturn("metadata"); mProvider.onDozingChanged(true); mProvider.onMetadataChanged(metadata); mProvider.onBindSlice(mProvider.getUri()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 2172a125ba85..3662c3860177 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -98,7 +98,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mViewHierarchyManager = new NotificationViewHierarchyManager(mContext, mLockscreenUserManager, mGroupManager, mVisualStabilityManager, mock(StatusBarStateControllerImpl.class), mEntryManager, - () -> mShadeController, new BubbleData()); + () -> mShadeController, new BubbleData(mContext)); Dependency.get(InitController.class).executePostInitTasks(); mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer); } diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java index 46e3226bb751..7b6a12822faa 100644 --- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java +++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java @@ -111,6 +111,15 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement @Override public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + // MotionEventInjector would cancel any injected gesture when any MotionEvent arrives. + // For user using an external device to control the pointer movement, it's almost + // impossible to perform the gestures. Any slightly unintended movement results in the + // cancellation of the gesture. + if ((event.isFromSource(InputDevice.SOURCE_MOUSE) + && event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) + && mOpenGesturesInProgress.get(EVENT_SOURCE, false)) { + return; + } cancelAnyPendingInjectedEvents(); sendMotionEventToNext(event, rawEvent, policyFlags); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index fdc3567aa002..a94d1dc7f243 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -806,6 +806,17 @@ public final class AutofillManagerService mAugmentedAutofillState.injectAugmentedAutofillInfo(options, userId, packageName); return options; } + + @Override + public boolean isAugmentedAutofillServiceForUser(int callingUid, int userId) { + synchronized (mLock) { + final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + if (service != null) { + return service.isAugmentedAutofillServiceForUserLocked(callingUid); + } + } + return false; + } } /** diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 4bd6fbd39de4..f1963b37a6cf 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -1165,6 +1165,11 @@ final class AutofillManagerServiceImpl return true; } + boolean isAugmentedAutofillServiceForUserLocked(int callingUid) { + return mRemoteAugmentedAutofillServiceInfo != null + && mRemoteAugmentedAutofillServiceInfo.applicationInfo.uid == callingUid; + } + /** * Sets which packages and activities can trigger augmented autofill. * diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 447bd8c237dd..03f21498766d 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -566,10 +566,6 @@ public class UserBackupManagerService { // require frequent starting and stopping. mConstants.start(); - // Set up the various sorts of package tracking we do - mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule"); - initPackageTracking(); - // Build our mapping of uid to backup client services. This implicitly // schedules a backup pass on the Package Manager metadata the first // time anything needs to be backed up. @@ -589,6 +585,10 @@ public class UserBackupManagerService { // Power management mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*-" + userId); + + // Set up the various sorts of package tracking we do + mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule"); + initPackageTracking(); } void initializeBackupEnableState() { @@ -744,6 +744,11 @@ public class UserBackupManagerService { return mDataDir; } + @VisibleForTesting + BroadcastReceiver getPackageTrackingReceiver() { + return mBroadcastReceiver; + } + @Nullable public DataChangedJournal getJournal() { return mJournal; diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java index 593bb2c41587..35dfccf32924 100644 --- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java +++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java @@ -23,22 +23,20 @@ import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_A import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.annotation.Nullable; -import android.app.AppGlobals; import android.app.backup.BackupTransport; import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.content.pm.SigningInfo; -import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.backup.IBackupTransport; import com.android.internal.util.ArrayUtils; +import com.android.server.LocalServices; import com.android.server.backup.transport.TransportClient; import com.google.android.collect.Sets; @@ -67,12 +65,13 @@ public class AppBackupUtils { * </ol> */ public static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) { - return appIsEligibleForBackup(app, AppGlobals.getPackageManager(), userId); + return appIsEligibleForBackup( + app, LocalServices.getService(PackageManagerInternal.class), userId); } @VisibleForTesting - static boolean appIsEligibleForBackup(ApplicationInfo app, - IPackageManager packageManager, int userId) { + static boolean appIsEligibleForBackup( + ApplicationInfo app, PackageManagerInternal packageManager, int userId) { // 1. their manifest states android:allowBackup="false" if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { return false; @@ -108,9 +107,9 @@ public class AppBackupUtils { /** * Returns whether an app is eligible for backup at runtime. That is, the app has to: * <ol> - * <li>Return true for {@link #appIsEligibleForBackup(ApplicationInfo, PackageManager)} + * <li>Return true for {@link #appIsEligibleForBackup(ApplicationInfo, int)} * <li>Return false for {@link #appIsStopped(ApplicationInfo)} - * <li>Return false for {@link #appIsDisabled(ApplicationInfo, PackageManager)} + * <li>Return false for {@link #appIsDisabled(ApplicationInfo, int)} * <li>Be eligible for the transport via * {@link BackupTransport#isAppEligibleForBackup(PackageInfo, boolean)} * </ol> @@ -149,19 +148,13 @@ public class AppBackupUtils { /** Avoid backups of 'disabled' apps. */ static boolean appIsDisabled(ApplicationInfo app, int userId) { - return appIsDisabled(app, AppGlobals.getPackageManager(), userId); + return appIsDisabled(app, LocalServices.getService(PackageManagerInternal.class), userId); } @VisibleForTesting - static boolean appIsDisabled(ApplicationInfo app, - IPackageManager packageManager, int userId) { - int enabledSetting; - try { - enabledSetting = packageManager.getApplicationEnabledSetting(app.packageName, userId); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to get application enabled setting: " + e); - return false; - } + static boolean appIsDisabled( + ApplicationInfo app, PackageManagerInternal packageManager, int userId) { + int enabledSetting = packageManager.getApplicationEnabledState(app.packageName, userId); switch (enabledSetting) { case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 757c2dc7f6f1..7f411d83b34b 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -60,8 +60,8 @@ import android.util.SparseBooleanArray; import android.view.contentcapture.ContentCaptureCondition; import android.view.contentcapture.ContentCaptureHelper; import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.DataRemovalRequest; import android.view.contentcapture.IContentCaptureManager; -import android.view.contentcapture.UserDataRemovalRequest; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.AbstractRemoteService; @@ -583,14 +583,14 @@ public final class ContentCaptureManagerService extends } @Override - public void removeUserData(@NonNull UserDataRemovalRequest request) { + public void removeData(@NonNull DataRemovalRequest request) { Preconditions.checkNotNull(request); assertCalledByPackageOwner(request.getPackageName()); final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { final ContentCapturePerUserService service = getServiceForUserLocked(userId); - service.removeUserDataLocked(request); + service.removeDataLocked(request); } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 564952697250..b4a1f381f7ff 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -57,7 +57,7 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.view.contentcapture.ContentCaptureCondition; -import android.view.contentcapture.UserDataRemovalRequest; +import android.view.contentcapture.DataRemovalRequest; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.WhitelistHelper; @@ -343,12 +343,12 @@ final class ContentCapturePerUserService } @GuardedBy("mLock") - public void removeUserDataLocked(@NonNull UserDataRemovalRequest request) { + public void removeDataLocked(@NonNull DataRemovalRequest request) { if (!isEnabledLocked()) { return; } assertCallerLocked(request.getPackageName()); - mRemoteService.onUserDataRemovalRequest(request); + mRemoteService.onDataRemovalRequest(request); } @GuardedBy("mLock") diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index 3fa3fdf6d36e..2171033c5a28 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -29,7 +29,7 @@ import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.SnapshotData; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; -import android.view.contentcapture.UserDataRemovalRequest; +import android.view.contentcapture.DataRemovalRequest; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.internal.os.IResultReceiver; @@ -120,10 +120,10 @@ final class RemoteContentCaptureService } /** - * Called by {@link ContentCaptureServerSession} to request removal of user data. + * Called by {@link ContentCaptureServerSession} to request removal of content capture data. */ - public void onUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) { - scheduleAsyncRequest((s) -> s.onUserDataRemovalRequest(request)); + public void onDataRemovalRequest(@NonNull DataRemovalRequest request) { + scheduleAsyncRequest((s) -> s.onDataRemovalRequest(request)); } /** diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java index 6018f008054c..5c5b477352b1 100644 --- a/services/core/java/com/android/server/BluetoothService.java +++ b/services/core/java/com/android/server/BluetoothService.java @@ -18,11 +18,10 @@ package com.android.server; import android.bluetooth.BluetoothAdapter; import android.content.Context; -import android.os.SystemProperties; -class BluetoothService extends SystemService { - private static final String HEADLESS_SYSTEM_USER = "android.car.systemuser.headless"; +import com.android.internal.os.RoSystemProperties; +class BluetoothService extends SystemService { private BluetoothManagerService mBluetoothManagerService; private boolean mInitialized = false; @@ -48,7 +47,7 @@ class BluetoothService extends SystemService { publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService); } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY && - !SystemProperties.getBoolean(HEADLESS_SYSTEM_USER, false)) { + !RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER) { initialize(); } } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 382fdec94f08..053e68625a47 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -293,7 +293,6 @@ class StorageManagerService extends IStorageManager.Stub private static final String TAG_VOLUMES = "volumes"; private static final String ATTR_VERSION = "version"; private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid"; - private static final String ATTR_ISOLATED_STORAGE = "isolatedStorage"; private static final String TAG_VOLUME = "volume"; private static final String ATTR_TYPE = "type"; private static final String ATTR_FS_UUID = "fsUuid"; @@ -349,10 +348,6 @@ class StorageManagerService extends IStorageManager.Stub @GuardedBy("mLock") private String mPrimaryStorageUuid; - /** Flag indicating isolated storage state of last boot */ - @GuardedBy("mLock") - private boolean mLastIsolatedStorage = false; - /** Map from disk ID to latches */ @GuardedBy("mLock") private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>(); @@ -1074,6 +1069,15 @@ class StorageManagerService extends IStorageManager.Stub mVold.onUserStarted(userId, packages, appIds, sandboxIds); } + private boolean supportsBlockCheckpoint() throws RemoteException { + // Only the system process is permitted to start checkpoints + if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { + throw new SecurityException("no permission to check block based checkpoint support"); + } + + return mVold.supportsBlockCheckpoint(); + } + @Override public void onAwakeStateChanged(boolean isAwake) { // Ignored @@ -1681,63 +1685,6 @@ class StorageManagerService extends IStorageManager.Stub mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback); } catch (RemoteException e) { } - - synchronized (mLock) { - final boolean thisIsolatedStorage = StorageManager.hasIsolatedStorage(); - if (mLastIsolatedStorage != thisIsolatedStorage) { - if (thisIsolatedStorage) { - // This boot enables isolated storage; apply legacy behavior - applyLegacyStorage(); - } - - // Always remember the new state we just booted with - writeSettingsLocked(); - } - } - } - - /** - * If we're enabling isolated storage, we need to remember which existing - * apps have already been using shared storage, and grant them legacy access - * to keep them running smoothly. - * - * @see com.android.server.pm.permission.PermissionManagerService - * #applyLegacyStoragePermissionModel - */ - private void applyLegacyStorage() { - final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); - final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class); - for (int userId : um.getUserIds()) { - final UserHandle user = UserHandle.of(userId); - final PackageManager pm; - try { - pm = mContext.createPackageContextAsUser(mContext.getPackageName(), 0, - user).getPackageManager(); - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException(e); - } - - final List<PackageInfo> pkgs = pm.getPackagesHoldingPermissions( - ALL_STORAGE_PERMISSIONS, - MATCH_UNINSTALLED_PACKAGES | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE - | GET_PERMISSIONS); - for (PackageInfo pkg : pkgs) { - final int uid = pkg.applicationInfo.uid; - final String packageName = pkg.applicationInfo.packageName; - - final long lastAccess = getLastAccessTime(appOps, uid, packageName, new int[] { - AppOpsManager.OP_READ_EXTERNAL_STORAGE, - AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, - }); - - Log.d(TAG, "Found " + uid + " " + packageName - + " with granted storage access, last accessed " + lastAccess); - if (lastAccess > 0) { - appOps.setUidMode(AppOpsManager.OP_LEGACY_STORAGE, uid, - AppOpsManager.MODE_ALLOWED); - } - } - } } private static long getLastAccessTime(AppOpsManager manager, @@ -1783,7 +1730,6 @@ class StorageManagerService extends IStorageManager.Stub private void readSettingsLocked() { mRecords.clear(); mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); - mLastIsolatedStorage = false; FileInputStream fis = null; try { @@ -1805,9 +1751,6 @@ class StorageManagerService extends IStorageManager.Stub mPrimaryStorageUuid = readStringAttribute(in, ATTR_PRIMARY_STORAGE_UUID); } - mLastIsolatedStorage = readBooleanAttribute(in, - ATTR_ISOLATED_STORAGE, false); - } else if (TAG_VOLUME.equals(tag)) { final VolumeRecord rec = readVolumeRecord(in); mRecords.put(rec.fsUuid, rec); @@ -1837,7 +1780,6 @@ class StorageManagerService extends IStorageManager.Stub out.startTag(null, TAG_VOLUMES); writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY); writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); - writeBooleanAttribute(out, ATTR_ISOLATED_STORAGE, StorageManager.hasIsolatedStorage()); final int size = mRecords.size(); for (int i = 0; i < size; i++) { final VolumeRecord rec = mRecords.valueAt(i); @@ -2116,37 +2058,45 @@ class StorageManagerService extends IStorageManager.Stub enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); try { - mVold.fstrim(flags, new IVoldTaskListener.Stub() { - @Override - public void onStatus(int status, PersistableBundle extras) { - dispatchOnStatus(listener, status, extras); + // Block based checkpoint process runs fstrim. So, if checkpoint is in progress + // (first boot after OTA), We skip idle maintenance and make sure the last + // fstrim time is still updated. If file based checkpoints are used, we run + // idle maintenance (GC + fstrim) regardless of checkpoint status. + if (!needsCheckpoint() || !supportsBlockCheckpoint()) { + mVold.fstrim(flags, new IVoldTaskListener.Stub() { + @Override + public void onStatus(int status, PersistableBundle extras) { + dispatchOnStatus(listener, status, extras); - // Ignore trim failures - if (status != 0) return; + // Ignore trim failures + if (status != 0) return; - final String path = extras.getString("path"); - final long bytes = extras.getLong("bytes"); - final long time = extras.getLong("time"); + final String path = extras.getString("path"); + final long bytes = extras.getLong("bytes"); + final long time = extras.getLong("time"); - final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); - dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) + " " + bytes + " " + time); + final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); + dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) + " " + bytes + " " + time); - synchronized (mLock) { - final VolumeRecord rec = findRecordForPath(path); - if (rec != null) { - rec.lastTrimMillis = System.currentTimeMillis(); - writeSettingsLocked(); + synchronized (mLock) { + final VolumeRecord rec = findRecordForPath(path); + if (rec != null) { + rec.lastTrimMillis = System.currentTimeMillis(); + writeSettingsLocked(); + } } } - } - @Override - public void onFinished(int status, PersistableBundle extras) { - dispatchOnFinished(listener, status, extras); + @Override + public void onFinished(int status, PersistableBundle extras) { + dispatchOnFinished(listener, status, extras); - // TODO: benchmark when desired - } - }); + // TODO: benchmark when desired + } + }); + } else { + Slog.i(TAG, "Skipping fstrim - block based checkpoint in progress"); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -2156,18 +2106,26 @@ class StorageManagerService extends IStorageManager.Stub enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); try { - mVold.runIdleMaint(new IVoldTaskListener.Stub() { - @Override - public void onStatus(int status, PersistableBundle extras) { - // Not currently used - } - @Override - public void onFinished(int status, PersistableBundle extras) { - if (callback != null) { - BackgroundThread.getHandler().post(callback); + // Block based checkpoint process runs fstrim. So, if checkpoint is in progress + // (first boot after OTA), We skip idle maintenance and make sure the last + // fstrim time is still updated. If file based checkpoints are used, we run + // idle maintenance (GC + fstrim) regardless of checkpoint status. + if (!needsCheckpoint() || !supportsBlockCheckpoint()) { + mVold.runIdleMaint(new IVoldTaskListener.Stub() { + @Override + public void onStatus(int status, PersistableBundle extras) { + // Not currently used } - } - }); + @Override + public void onFinished(int status, PersistableBundle extras) { + if (callback != null) { + BackgroundThread.getHandler().post(callback); + } + } + }); + } else { + Slog.i(TAG, "Skipping idle maintenance - block based checkpoint in progress"); + } } catch (Exception e) { Slog.wtf(TAG, e); } @@ -3766,7 +3724,7 @@ class StorageManagerService extends IStorageManager.Stub return Zygote.MOUNT_EXTERNAL_NONE; } if (mPmInternal.isInstantApp(packageName, UserHandle.getUserId(uid))) { - return Zygote.MOUNT_EXTERNAL_NONE; + return Zygote.MOUNT_EXTERNAL_DEFAULT; } // Determine if caller is holding runtime permission @@ -3800,11 +3758,7 @@ class StorageManagerService extends IStorageManager.Stub // they hold the runtime permission final boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, uid, packageName) == MODE_ALLOWED; - // STOPSHIP: only use app-op once permission model has fully landed - final boolean requestedLegacy = !mIPackageManager - .getApplicationInfo(packageName, 0, UserHandle.getUserId(uid)) - .isExternalStorageSandboxAllowed(); - if ((hasLegacy || requestedLegacy) && hasStorage) { + if (hasLegacy && hasStorage) { return Zygote.MOUNT_EXTERNAL_LEGACY; } else { return Zygote.MOUNT_EXTERNAL_WRITE; diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java index 6bb3200f7cc1..1ce655618028 100644 --- a/services/core/java/com/android/server/SystemServerInitThreadPool.java +++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java @@ -120,7 +120,7 @@ public class SystemServerInitThreadPool { private static void dumpStackTraces() { final ArrayList<Integer> pids = new ArrayList<>(); pids.add(Process.myPid()); - ActivityManagerService.dumpStackTraces( - pids, null, null, null); + ActivityManagerService.dumpStackTraces(pids, null, null, + Watchdog.getInterestingNativePids()); } } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index afcf954371cc..cc3b43adcacd 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -101,17 +101,18 @@ public class Watchdog extends Thread { }; public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList( - "android.hardware.audio@2.0::IDevicesFactory", - "android.hardware.audio@4.0::IDevicesFactory", - "android.hardware.bluetooth@1.0::IBluetoothHci", - "android.hardware.camera.provider@2.4::ICameraProvider", - "android.hardware.graphics.allocator@2.0::IAllocator", - "android.hardware.graphics.composer@2.1::IComposer", - "android.hardware.health@2.0::IHealth", - "android.hardware.media.omx@1.0::IOmx", - "android.hardware.media.omx@1.0::IOmxStore", - "android.hardware.sensors@1.0::ISensors", - "android.hardware.vr@1.0::IVr" + "android.hardware.audio@2.0::IDevicesFactory", + "android.hardware.audio@4.0::IDevicesFactory", + "android.hardware.bluetooth@1.0::IBluetoothHci", + "android.hardware.camera.provider@2.4::ICameraProvider", + "android.hardware.graphics.allocator@2.0::IAllocator", + "android.hardware.graphics.composer@2.1::IComposer", + "android.hardware.health@2.0::IHealth", + "android.hardware.media.omx@1.0::IOmx", + "android.hardware.media.omx@1.0::IOmxStore", + "android.hardware.sensors@1.0::ISensors", + "android.hardware.vr@1.0::IVr", + "android.hardware.biometrics.face@1.0::IBiometricsFace" ); static Watchdog sWatchdog; @@ -396,7 +397,7 @@ public class Watchdog extends Thread { return builder.toString(); } - private ArrayList<Integer> getInterestingHalPids() { + private static ArrayList<Integer> getInterestingHalPids() { try { IServiceManager serviceManager = IServiceManager.getService(); ArrayList<IServiceManager.InstanceDebugInfo> dump = @@ -419,7 +420,7 @@ public class Watchdog extends Thread { } } - private ArrayList<Integer> getInterestingNativePids() { + static ArrayList<Integer> getInterestingNativePids() { ArrayList<Integer> pids = getInterestingHalPids(); int[] nativePids = Process.getPidsForCommands(NATIVE_STACKS_OF_INTEREST); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 4ec90ba5b803..0271354b3fff 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1806,7 +1806,7 @@ public final class ActiveServices { || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0), b.client); - mAm.updateOomAdjLocked(); + mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE); } if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b @@ -1957,11 +1957,12 @@ public final class ActiveServices { r.binding.service.app.hasClientActivities() || r.binding.service.app.treatLikeActivity, null); } - mAm.updateOomAdjLocked(r.binding.service.app, false); + mAm.updateOomAdjLocked(r.binding.service.app, false, + OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); } } - mAm.updateOomAdjLocked(); + mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); } finally { Binder.restoreCallingIdentity(origId); @@ -2676,7 +2677,7 @@ public final class ActiveServices { bumpServiceExecutingLocked(r, execInFg, "create"); mAm.updateLruProcessLocked(app, false, null); updateServiceForegroundLocked(r.app, /* oomAdj= */ false); - mAm.updateOomAdjLocked(); + mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE); boolean created = false; try { @@ -2797,7 +2798,7 @@ public final class ActiveServices { bumpServiceExecutingLocked(r, execInFg, "start"); if (!oomAdjusted) { oomAdjusted = true; - mAm.updateOomAdjLocked(r.app, true); + mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_START_SERVICE); } if (r.fgRequired && !r.fgWaiting) { if (!r.isForeground) { @@ -2923,7 +2924,8 @@ public final class ActiveServices { if (ibr.hasBound) { try { bumpServiceExecutingLocked(r, false, "bring down unbind"); - mAm.updateOomAdjLocked(r.app, true); + mAm.updateOomAdjLocked(r.app, true, + OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); ibr.hasBound = false; ibr.requested = false; r.app.thread.scheduleUnbindService(r, @@ -3038,7 +3040,8 @@ public final class ActiveServices { bumpServiceExecutingLocked(r, false, "destroy"); mDestroyingServices.add(r); r.destroying = true; - mAm.updateOomAdjLocked(r.app, true); + mAm.updateOomAdjLocked(r.app, true, + OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); r.app.thread.scheduleStopService(r); } catch (Exception e) { Slog.w(TAG, "Exception when destroying service " @@ -3143,7 +3146,8 @@ public final class ActiveServices { // it to go down there and we want it to start out near the top. mAm.updateLruProcessLocked(s.app, false, null); } - mAm.updateOomAdjLocked(s.app, true); + mAm.updateOomAdjLocked(s.app, true, + OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); b.intent.hasBound = false; // Assume the client doesn't want to know about a rebind; // we will deal with that later if it asks for one. @@ -3296,7 +3300,7 @@ public final class ActiveServices { mDestroyingServices.remove(r); r.bindings.clear(); } - mAm.updateOomAdjLocked(r.app, true); + mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); } r.executeFg = false; if (r.tracker != null) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3b6b404170c5..cdcd9e1ad58c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1972,7 +1972,7 @@ public class ActivityManagerService extends IActivityManager.Stub app.makeActive(mSystemThread.getApplicationThread(), mProcessStats); mPidsSelfLocked.put(app.pid, app); mProcessList.updateLruProcessLocked(app, false, null); - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); } } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException( @@ -2456,7 +2456,7 @@ public class ActivityManagerService extends IActivityManager.Stub // bind background threads to little cores // this is expected to fail inside of framework tests because apps can't touch cpusets directly // make sure we've already adjusted system_server's internal view of itself first - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); try { Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(), Process.THREAD_GROUP_SYSTEM); @@ -3634,7 +3634,7 @@ public class ActivityManagerService extends IActivityManager.Stub handleAppDiedLocked(app, false, true); if (doOomAdj) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END); } if (doLowMem) { doLowMemReportIfNeededLocked(app); @@ -5002,7 +5002,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (!didSomething) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN); checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked"); } @@ -5485,7 +5485,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { mConstants.setOverrideMaxCachedProcesses(max); } - trimApplications(); + trimApplications(OomAdjuster.OOM_ADJ_REASON_PROCESS_END); } @Override @@ -5511,7 +5511,7 @@ public class ActivityManagerService extends IActivityManager.Stub pr.forcingToImportant = null; updateProcessForegroundLocked(pr, false, 0, false); } - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } @@ -5557,7 +5557,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (changed) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } } @@ -6714,7 +6714,8 @@ public class ActivityManagerService extends IActivityManager.Stub checkTime(startTime, "getContentProviderImpl: before updateOomAdj"); final int verifiedAdj = cpr.proc.verifiedAdj; - boolean success = updateOomAdjLocked(cpr.proc, true); + boolean success = updateOomAdjLocked(cpr.proc, true, + OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); // XXX things have changed so updateOomAdjLocked doesn't actually tell us // if the process has been successfully adjusted. So to reduce races with // it, we will check whether the process still exists. Note that this doesn't @@ -7147,7 +7148,7 @@ public class ActivityManagerService extends IActivityManager.Stub throw new NullPointerException("connection is null"); } if (decProviderCountLocked(conn, null, null, stable)) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER); } } } finally { @@ -7188,7 +7189,7 @@ public class ActivityManagerService extends IActivityManager.Stub ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId); if (localCpr.hasExternalProcessHandles()) { if (localCpr.removeExternalProcessHandleLocked(token)) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER); } else { Slog.e(TAG, "Attmpt to remove content provider " + localCpr + " with no external reference for token: " @@ -7255,7 +7256,7 @@ public class ActivityManagerService extends IActivityManager.Stub dst.setProcess(r); dst.notifyAll(); } - updateOomAdjLocked(r, true); + updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } @@ -7645,7 +7646,7 @@ public class ActivityManagerService extends IActivityManager.Stub new HostingRecord("added application", customProcess != null ? customProcess : info.processName)); mProcessList.updateLruProcessLocked(app, false, null); - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN); } // This package really, really can not be stopped. @@ -7740,7 +7741,7 @@ public class ActivityManagerService extends IActivityManager.Stub mActivityTaskManager.onScreenAwakeChanged(isAwake); mOomAdjProfiler.onWakefulnessChanged(wakefulness); } - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } @@ -8320,7 +8321,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } if (changed) { - updateOomAdjLocked(pr, true); + updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } } finally { @@ -8350,7 +8351,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.i(TAG, "Setting runningRemoteAnimation=" + pr.runningRemoteAnimation + " for pid=" + pid); } - updateOomAdjLocked(pr, true); + updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } @@ -13984,7 +13985,7 @@ public class ActivityManagerService extends IActivityManager.Stub mBackupTargets.put(targetUserId, r); // Try not to kill the process during backup - updateOomAdjLocked(proc, true); + updateOomAdjLocked(proc, true, OomAdjuster.OOM_ADJ_REASON_NONE); // If the process is already attached, schedule the creation of the backup agent now. // If it is not yet live, this will be done when it attaches to the framework. @@ -14099,7 +14100,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Not backing this app up any more; reset its OOM adjustment final ProcessRecord proc = backupTarget.app; - updateOomAdjLocked(proc, true); + updateOomAdjLocked(proc, true, OomAdjuster.OOM_ADJ_REASON_NONE); proc.inFullBackup = false; oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1; @@ -14388,7 +14389,7 @@ public class ActivityManagerService extends IActivityManager.Stub // If we actually concluded any broadcasts, we might now be able // to trim the recipients' apps from our working set if (doTrim) { - trimApplications(); + trimApplications(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER); return; } @@ -15463,7 +15464,7 @@ public class ActivityManagerService extends IActivityManager.Stub r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true); } // updateOomAdjLocked() will be done here - trimApplicationsLocked(); + trimApplicationsLocked(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER); } } finally { @@ -16448,7 +16449,7 @@ public class ActivityManagerService extends IActivityManager.Stub item.foregroundServiceTypes = fgServiceTypes; if (oomAdj) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } } @@ -16496,11 +16497,13 @@ public class ActivityManagerService extends IActivityManager.Stub * @param app The process to update * @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps * if necessary, or skip. + * @param oomAdjReason * @return whether updateOomAdjLocked(app) was successful. */ @GuardedBy("this") - final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) { - return mOomAdjuster.updateOomAdjLocked(app, oomAdjAll); + final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll, + String oomAdjReason) { + return mOomAdjuster.updateOomAdjLocked(app, oomAdjAll, oomAdjReason); } static final class ProcStatsRunnable implements Runnable { @@ -16693,8 +16696,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @GuardedBy("this") - final void updateOomAdjLocked() { - mOomAdjuster.updateOomAdjLocked(); + final void updateOomAdjLocked(String oomAdjReason) { + mOomAdjuster.updateOomAdjLocked(oomAdjReason); } @Override @@ -16979,14 +16982,14 @@ public class ActivityManagerService extends IActivityManager.Stub mOomAdjuster.setUidTempWhitelistStateLocked(uid, onWhitelist); } - final void trimApplications() { + final void trimApplications(String oomAdjReason) { synchronized (this) { - trimApplicationsLocked(); + trimApplicationsLocked(oomAdjReason); } } @GuardedBy("this") - final void trimApplicationsLocked() { + final void trimApplicationsLocked(String oomAdjReason) { // First remove any unused application processes whose package // has been removed. for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) { @@ -17018,7 +17021,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Now update the oom adj for all processes. Don't skip this, since other callers // might be depending on it. - updateOomAdjLocked(); + updateOomAdjLocked(oomAdjReason); } /** This method sends the specified signal to each of the persistent apps */ @@ -17627,7 +17630,7 @@ public class ActivityManagerService extends IActivityManager.Stub } pr.setHasOverlayUi(hasOverlayUi); //Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid); - updateOomAdjLocked(pr, true); + updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } @@ -17784,7 +17787,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void trimApplications() { - ActivityManagerService.this.trimApplications(); + ActivityManagerService.this.trimApplications(OomAdjuster.OOM_ADJ_REASON_ACTIVITY); } public void killProcessesForRemovedTask(ArrayList<Object> procsToKill) { @@ -17836,7 +17839,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void updateOomAdj() { synchronized (ActivityManagerService.this) { - ActivityManagerService.this.updateOomAdjLocked(); + ActivityManagerService.this.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 3c57c3bcb7d6..ee9b5614584f 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -309,7 +309,7 @@ public final class BroadcastQueue { app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER); mService.mProcessList.updateLruProcessLocked(app, false, null); if (!skipOomAdj) { - mService.updateOomAdjLocked(); + mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); } // Tell the application to launch this receiver. @@ -791,7 +791,8 @@ public final class BroadcastQueue { // are already core system stuff so don't matter for this. r.curApp = filter.receiverList.app; filter.receiverList.app.curReceivers.add(r); - mService.updateOomAdjLocked(r.curApp, true); + mService.updateOomAdjLocked(r.curApp, true, + OomAdjuster.OOM_ADJ_REASON_START_RECEIVER); } } try { @@ -1028,7 +1029,7 @@ public final class BroadcastQueue { // If we had finished the last ordered broadcast, then // make sure all processes have correct oom and sched // adjustments. - mService.updateOomAdjLocked(); + mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER); } // when we have no more ordered broadcast on this queue, stop logging diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 44d435f2b539..8ae7c7d3e0c8 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -90,6 +90,21 @@ import java.util.Arrays; public final class OomAdjuster { private static final String TAG = "OomAdjuster"; + static final String OOM_ADJ_REASON_METHOD = "updateOomAdj"; + static final String OOM_ADJ_REASON_NONE = OOM_ADJ_REASON_METHOD + "_meh"; + static final String OOM_ADJ_REASON_ACTIVITY = OOM_ADJ_REASON_METHOD + "_activityChange"; + static final String OOM_ADJ_REASON_FINISH_RECEIVER = OOM_ADJ_REASON_METHOD + "_finishReceiver"; + static final String OOM_ADJ_REASON_START_RECEIVER = OOM_ADJ_REASON_METHOD + "_startReceiver"; + static final String OOM_ADJ_REASON_BIND_SERVICE = OOM_ADJ_REASON_METHOD + "_bindService"; + static final String OOM_ADJ_REASON_UNBIND_SERVICE = OOM_ADJ_REASON_METHOD + "_unbindService"; + static final String OOM_ADJ_REASON_START_SERVICE = OOM_ADJ_REASON_METHOD + "_startService"; + static final String OOM_ADJ_REASON_GET_PROVIDER = OOM_ADJ_REASON_METHOD + "_getProvider"; + static final String OOM_ADJ_REASON_REMOVE_PROVIDER = OOM_ADJ_REASON_METHOD + "_removeProvider"; + static final String OOM_ADJ_REASON_UI_VISIBILITY = OOM_ADJ_REASON_METHOD + "_uiVisibility"; + static final String OOM_ADJ_REASON_WHITELIST = OOM_ADJ_REASON_METHOD + "_whitelistChange"; + static final String OOM_ADJ_REASON_PROCESS_BEGIN = OOM_ADJ_REASON_METHOD + "_processBegin"; + static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd"; + /** * For some direct access we need to power manager. */ @@ -156,10 +171,12 @@ public final class OomAdjuster { * @param app The process to update * @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps * if necessary, or skip. + * @param oomAdjReason * @return whether updateOomAdjLocked(app) was successful. */ @GuardedBy("mService") - final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) { + boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll, + String oomAdjReason) { final ProcessRecord TOP_APP = mService.getTopAppLocked(); final boolean wasCached = app.cached; @@ -177,7 +194,7 @@ public final class OomAdjuster { && (wasCached != app.cached || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) { // Changed to/from cached state, so apps after it in the LRU // list may also be changed. - updateOomAdjLocked(); + updateOomAdjLocked(oomAdjReason); } return success; } @@ -195,8 +212,8 @@ public final class OomAdjuster { } @GuardedBy("mService") - final void updateOomAdjLocked() { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "updateOomAdj"); + void updateOomAdjLocked(String oomAdjReason) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason); mService.mOomAdjProfiler.oomAdjStarted(); final ProcessRecord TOP_APP = mService.getTopAppLocked(); final long now = SystemClock.uptimeMillis(); @@ -2009,7 +2026,7 @@ public final class OomAdjuster { } } if (changed) { - updateOomAdjLocked(); + updateOomAdjLocked(OOM_ADJ_REASON_WHITELIST); } } @@ -2019,7 +2036,7 @@ public final class OomAdjuster { final UidRecord uidRec = mActiveUids.get(uid); if (uidRec != null && uidRec.curWhitelist != onWhitelist) { uidRec.curWhitelist = onWhitelist; - updateOomAdjLocked(); + updateOomAdjLocked(OOM_ADJ_REASON_WHITELIST); } } diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index a08c829f0576..588e05d7a060 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -29,6 +29,7 @@ import android.content.Intent; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.TransactionTooLargeException; @@ -380,8 +381,9 @@ public final class PendingIntentRecord extends IIntentSender.Stub { userId = controller.mUserController.getCurrentOrTargetUserId(); } // temporarily allow receivers and services to open activities from background if the - // PendingIntent.send() caller was foreground at the time of sendInner() call - final boolean allowTrampoline = uid != callingUid + // PendingIntent.send() caller was foreground at the time of sendInner() call, unless + // caller is SYSTEM_UID + final boolean allowTrampoline = uid != callingUid && callingUid != Process.SYSTEM_UID && controller.mAtmInternal.isUidForeground(callingUid); // note: we on purpose don't pass in the information about the PendingIntent's creator, diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 0a926f9a801a..316368a52ce2 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2227,9 +2227,10 @@ public final class ProcessList { for (AppZygote appZygote : zygotesToKill) { killAppZygoteIfNeededLocked(appZygote); } - mService.updateOomAdjLocked(); + mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END); return N > 0; } + @GuardedBy("mService") boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart, String reason) { diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 933f41c47167..49930c1f4d62 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1301,7 +1301,7 @@ class ProcessRecord implements WindowProcessListener { } mService.mProcessList.updateLruProcessLocked(this, activityChange, null /* client */); if (updateOomAdj) { - mService.updateOomAdjLocked(); + mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_ACTIVITY); } } } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 873cadb5a9d9..cd8167ae4653 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -3097,6 +3097,7 @@ public class AppOpsService extends IAppOpsService.Stub { int nonpackageUid; final static Binder sBinder = new Binder(); IBinder mToken; + boolean targetsUid; Shell(IAppOpsService iface, AppOpsService internal) { mInterface = iface; @@ -3190,6 +3191,8 @@ public class AppOpsService extends IAppOpsService.Stub { for (String argument; (argument = getNextArg()) != null;) { if ("--user".equals(argument)) { userId = UserHandle.parseUserArg(getNextArgRequired()); + } else if ("--uid".equals(argument)) { + targetsUid = true; } else { if (packageName == null) { packageName = argument; @@ -3288,7 +3291,7 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" Starts a given operation for a particular application."); pw.println(" stop [--user <USER_ID>] <PACKAGE | UID> <OP> "); pw.println(" Stops a given operation for a particular application."); - pw.println(" set [--user <USER_ID>] <PACKAGE | UID> <OP> <MODE>"); + pw.println(" set [--user <USER_ID>] <--uid PACKAGE | PACKAGE | UID> <OP> <MODE>"); pw.println(" Set the mode for a particular application and operation."); pw.println(" get [--user <USER_ID>] <PACKAGE | UID> [<OP>]"); pw.println(" Return the mode for a particular application and optional operation."); @@ -3306,6 +3309,7 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" <MODE> one of allow, ignore, deny, or default"); pw.println(" <USER_ID> the user id under which the package is installed. If --user is not"); pw.println(" specified, the current user is assumed."); + pw.println(" --uid PACKAGE refer to the UID of the package"); } static int onShellCommand(Shell shell, String cmd) { @@ -3332,9 +3336,17 @@ public class AppOpsService extends IAppOpsService.Stub { return -1; } - if (shell.packageName != null) { + if (!shell.targetsUid && shell.packageName != null) { shell.mInterface.setMode(shell.op, shell.packageUid, shell.packageName, mode); + } else if (shell.targetsUid && shell.packageName != null) { + try { + final int uid = shell.mInternal.mContext.getPackageManager() + .getPackageUid(shell.packageName, shell.userId); + shell.mInterface.setUidMode(shell.op, uid, mode); + } catch (PackageManager.NameNotFoundException e) { + return -1; + } } else { shell.mInterface.setUidMode(shell.op, shell.nonpackageUid, mode); } diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java index 3dbea0d1978e..d7d4851bf33f 100644 --- a/services/core/java/com/android/server/attention/AttentionManagerService.java +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -17,7 +17,6 @@ package com.android.server.attention; import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE; -import static android.provider.Settings.System.ADAPTIVE_SLEEP; import static android.service.attention.AttentionService.ATTENTION_FAILURE_CANCELLED; import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNOWN; @@ -47,7 +46,6 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.DeviceConfig; -import android.provider.Settings; import android.service.attention.AttentionService; import android.service.attention.AttentionService.AttentionFailureCodes; import android.service.attention.AttentionService.AttentionSuccessCodes; @@ -60,6 +58,7 @@ import android.util.StatsLog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -102,16 +101,25 @@ public class AttentionManagerService extends SystemService { private final Object mLock; @GuardedBy("mLock") private final SparseArray<UserState> mUserStates = new SparseArray<>(); - private final AttentionHandler mAttentionHandler; + private AttentionHandler mAttentionHandler; - private ComponentName mComponentName; + @VisibleForTesting + ComponentName mComponentName; public AttentionManagerService(Context context) { + this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE), + new Object(), null); + mAttentionHandler = new AttentionHandler(); + } + + @VisibleForTesting + AttentionManagerService(Context context, PowerManager powerManager, Object lock, + AttentionHandler handler) { super(context); mContext = Preconditions.checkNotNull(context); - mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mLock = new Object(); - mAttentionHandler = new AttentionHandler(); + mPowerManager = powerManager; + mLock = lock; + mAttentionHandler = handler; } @Override @@ -149,7 +157,8 @@ public class AttentionManagerService extends SystemService { return isServiceEnabled() && isServiceAvailable(); } - private boolean isServiceEnabled() { + @VisibleForTesting + protected boolean isServiceEnabled() { return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED); } @@ -163,7 +172,8 @@ public class AttentionManagerService extends SystemService { * * @return {@code true} if the framework was able to dispatch the request */ - private boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { + @VisibleForTesting + boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { Preconditions.checkNotNull(callbackInternal); if (!isAttentionServiceSupported()) { @@ -259,37 +269,24 @@ public class AttentionManagerService extends SystemService { } /** Cancels the specified attention check. */ - private void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { + @VisibleForTesting + void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { synchronized (mLock) { final UserState userState = peekCurrentUserStateLocked(); if (userState == null) { return; } - if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) { Slog.e(LOG_TAG, "Cannot cancel a non-current request"); return; } - cancel(userState); } } - /** Disables service dependants. */ - private void disableSelf() { - final long identity = Binder.clearCallingIdentity(); - try { - if (DEBUG) { - Slog.d(LOG_TAG, "Disabling self."); - } - Settings.System.putInt(mContext.getContentResolver(), ADAPTIVE_SLEEP, 0); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - @GuardedBy("mLock") - private void freeIfInactiveLocked() { + @VisibleForTesting + protected void freeIfInactiveLocked() { // If we are called here, it means someone used the API again - reset the timer then. mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION); @@ -306,7 +303,8 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") - private UserState getOrCreateCurrentUserStateLocked() { + @VisibleForTesting + protected UserState getOrCreateCurrentUserStateLocked() { return getOrCreateUserStateLocked(ActivityManager.getCurrentUser()); } @@ -322,7 +320,8 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") @Nullable - private UserState peekCurrentUserStateLocked() { + @VisibleForTesting + protected UserState peekCurrentUserStateLocked() { return peekUserStateLocked(ActivityManager.getCurrentUser()); } @@ -418,11 +417,6 @@ public class AttentionManagerService extends SystemService { public void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { AttentionManagerService.this.cancelAttentionCheck(callbackInternal); } - - @Override - public void disableSelf() { - AttentionManagerService.this.disableSelf(); - } } private static final class AttentionCheckCache { @@ -438,7 +432,8 @@ public class AttentionManagerService extends SystemService { } } - private static final class AttentionCheck { + @VisibleForTesting + static final class AttentionCheck { private final AttentionCallbackInternal mCallbackInternal; private final IAttentionCallback mIAttentionCallback; private boolean mIsDispatched; @@ -455,7 +450,8 @@ public class AttentionManagerService extends SystemService { } } - private static final class UserState { + @VisibleForTesting + protected static class UserState { final ComponentName mComponentName; final AttentionServiceConnection mConnection = new AttentionServiceConnection(); @@ -473,7 +469,7 @@ public class AttentionManagerService extends SystemService { final Context mContext; final Object mLock; - private UserState(int userId, Context context, Object lock, ComponentName componentName) { + UserState(int userId, Context context, Object lock, ComponentName componentName) { mUserId = userId; mContext = Preconditions.checkNotNull(context); mLock = Preconditions.checkNotNull(lock); @@ -529,7 +525,7 @@ public class AttentionManagerService extends SystemService { } } - private final class AttentionServiceConnection implements ServiceConnection { + private class AttentionServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { init(IAttentionService.Stub.asInterface(service)); @@ -564,7 +560,8 @@ public class AttentionManagerService extends SystemService { } } - private class AttentionHandler extends Handler { + @VisibleForTesting + protected class AttentionHandler extends Handler { private static final int CHECK_CONNECTION_EXPIRATION = 1; private static final int ATTENTION_CHECK_TIMEOUT = 2; @@ -596,7 +593,8 @@ public class AttentionManagerService extends SystemService { } } - private void cancel(UserState userState) { + @VisibleForTesting + void cancel(UserState userState) { if (userState == null || userState.mCurrentAttentionCheck == null) { return; } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index b7746477f0f8..c573332235d8 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -114,8 +114,10 @@ import java.util.ArrayList; // All post* methods are asynchronous /*package*/ void onSystemReady() { - synchronized (mDeviceStateLock) { - mBtHelper.onSystemReady(); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.onSystemReady(); + } } } @@ -151,8 +153,10 @@ import java.util.ArrayList; * @param intent */ /*package*/ void receiveBtEvent(@NonNull Intent intent) { - synchronized (mDeviceStateLock) { - mBtHelper.receiveBtEvent(intent); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.receiveBtEvent(intent); + } } } @@ -350,13 +354,19 @@ import java.util.ArrayList; sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device); } + @GuardedBy("mSetModeLock") /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode, @NonNull String eventSource) { - mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); + synchronized (mDeviceStateLock) { + mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); + } } + @GuardedBy("mSetModeLock") /*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) { - mBtHelper.stopBluetoothScoForClient(cb, eventSource); + synchronized (mDeviceStateLock) { + mBtHelper.stopBluetoothScoForClient(cb, eventSource); + } } //--------------------------------------------------------------------- @@ -406,6 +416,10 @@ import java.util.ArrayList; mAudioService.checkVolumeCecOnHdmiConnection(state, caller); } + /*package*/ boolean hasAudioFocusUsers() { + return mAudioService.hasAudioFocusUsers(); + } + //--------------------------------------------------------------------- // Message handling on behalf of helper classes /*package*/ void postBroadcastScoConnectionState(int state) { @@ -475,6 +489,10 @@ import java.util.ArrayList; hearingAidProfile); } + /*package*/ void postScoClientDied(Object obj) { + sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj); + } + //--------------------------------------------------------------------- // Method forwarding between the helper classes (BtHelper, AudioDeviceInventory) // only call from a "handle"* method or "on"* method @@ -704,8 +722,10 @@ import java.util.ArrayList; } break; case MSG_BT_HEADSET_CNCT_FAILED: - synchronized (mDeviceStateLock) { - mBtHelper.resetBluetoothSco(); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.resetBluetoothSco(); + } } break; case MSG_IL_BTA2DP_DOCK_TIMEOUT: @@ -738,8 +758,17 @@ import java.util.ArrayList; } break; case MSG_I_DISCONNECT_BT_SCO: - synchronized (mDeviceStateLock) { - mBtHelper.disconnectBluetoothSco(msg.arg1); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.disconnectBluetoothSco(msg.arg1); + } + } + break; + case MSG_L_SCOCLIENT_DIED: + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.scoClientDied(msg.arg1); + } } break; case MSG_TOGGLE_HDMI: @@ -770,8 +799,10 @@ import java.util.ArrayList; } break; case MSG_DISCONNECT_BT_HEADSET: - synchronized (mDeviceStateLock) { - mBtHelper.disconnectHeadset(); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.disconnectHeadset(); + } } break; case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP: @@ -790,8 +821,10 @@ import java.util.ArrayList; } break; case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET: - synchronized (mDeviceStateLock) { - mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + } } break; case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: { @@ -888,6 +921,8 @@ import java.util.ArrayList; private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 28; // process external command to (dis)connect or change active A2DP device private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT = 29; + // a ScoClient died in BtHelper + private static final int MSG_L_SCOCLIENT_DIED = 30; private static boolean isMessageHandledUnderWakelock(int msgId) { diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 7750bfefae05..91b51b4989d8 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -819,11 +819,12 @@ public final class AudioDeviceInventory { if (((device == musicDevice) || mDeviceBroker.isInCommunication()) && (device == devices) && !mDeviceBroker.hasMediaDynamicPolicy() && ((musicDevice & AudioSystem.DEVICE_OUT_REMOTE_SUBMIX) == 0)) { - if (!AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0 /*not looking in past*/)) { + if (!AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0 /*not looking in past*/) + && !mDeviceBroker.hasAudioFocusUsers()) { // no media playback, not a "becoming noisy" situation, otherwise it could cause // the pausing of some apps that are playing remotely AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "dropping ACTION_AUDIO_BECOMING_NOISY, no media playback")).printLog(TAG)); + "dropping ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG)); return 0; } mDeviceBroker.postBroadcastBecomingNoisy(); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 77472ed288c3..d30a9d2b158e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1523,9 +1523,11 @@ public class AudioService extends IAudioService.Stub + ", flags=" + flags + ", caller=" + caller + ", volControlStream=" + mVolumeControlStream + ", userSelect=" + mUserSelectedVolumeControlStream); - sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_SUGG_VOL, suggestedStreamType, - direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage) - .append("/").append(caller).append(" uid:").append(uid).toString())); + if (direction != AudioManager.ADJUST_SAME) { + sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_SUGG_VOL, suggestedStreamType, + direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage) + .append("/").append(caller).append(" uid:").append(uid).toString())); + } final int streamType; synchronized (mForceControlStreamLock) { // Request lock in case mVolumeControlStream is changed by other thread. @@ -3482,7 +3484,9 @@ public class AudioService extends IAudioService.Stub !mSystemReady) { return; } - mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); + synchronized (mDeviceBroker.mSetModeLock) { + mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); + } } /** @see AudioManager#stopBluetoothSco() */ @@ -3494,7 +3498,9 @@ public class AudioService extends IAudioService.Stub final String eventSource = new StringBuilder("stopBluetoothSco()") .append(") from u/pid:").append(Binder.getCallingUid()).append("/") .append(Binder.getCallingPid()).toString(); - mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource); + synchronized (mDeviceBroker.mSetModeLock) { + mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource); + } } @@ -5578,6 +5584,10 @@ public class AudioService extends IAudioService.Stub return mMediaFocusControl.getFocusRampTimeMs(focusGain, attr); } + /*package*/ boolean hasAudioFocusUsers() { + return mMediaFocusControl.hasAudioFocusUsers(); + } + //========================================================================================== private boolean readCameraSoundForced() { return SystemProperties.getBoolean("audio.camerasound.force", false) || @@ -6320,6 +6330,11 @@ public class AudioService extends IAudioService.Stub @Override public void adjustStreamVolumeForUid(int streamType, int direction, int flags, String callingPackage, int uid) { + if (direction != AudioManager.ADJUST_SAME) { + sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_VOL_UID, streamType, + direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage) + .append(" uid:").append(uid).toString())); + } adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage, uid); } @@ -6660,6 +6675,13 @@ public class AudioService extends IAudioService.Stub return AudioManager.SUCCESS; } + /** see AudioManager.hasRegisteredDynamicPolicy */ + public boolean hasRegisteredDynamicPolicy() { + synchronized (mAudioPolicies) { + return !mAudioPolicies.isEmpty(); + } + } + private final Object mExtVolumeControllerLock = new Object(); private IAudioPolicyCallback mExtVolumeController; private void setExtVolumeController(IAudioPolicyCallback apc) { diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index 7ccb45e97912..d999217bf29b 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -94,6 +94,7 @@ public class AudioServiceEvents { static final int VOL_SET_STREAM_VOL = 2; static final int VOL_SET_HEARING_AID_VOL = 3; static final int VOL_SET_AVRCP_VOL = 4; + static final int VOL_ADJUST_VOL_UID = 5; final int mOp; final int mStream; @@ -160,6 +161,13 @@ public class AudioServiceEvents { return new StringBuilder("setAvrcpVolume:") .append(" index:").append(mVal1) .toString(); + case VOL_ADJUST_VOL_UID: + return new StringBuilder("adjustStreamVolumeForUid(stream:") + .append(AudioSystem.streamToString(mStream)) + .append(" dir:").append(AudioManager.adjustToString(mVal1)) + .append(" flags:0x").append(Integer.toHexString(mVal2)) + .append(") from ").append(mCaller) + .toString(); default: return new StringBuilder("FIXME invalid op:").append(mOp).toString(); } } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 2d9156b8782e..332ff362392a 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -36,6 +36,8 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Log; +import com.android.internal.annotations.GuardedBy; + import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; @@ -163,6 +165,8 @@ public class BtHelper { //---------------------------------------------------------------------- // Interface for AudioDeviceBroker + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void onSystemReady() { mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR; resetBluetoothSco(); @@ -231,6 +235,8 @@ public class BtHelper { return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType()); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void receiveBtEvent(Intent intent) { final String action = intent.getAction(); if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { @@ -317,6 +323,8 @@ public class BtHelper { * * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept */ + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) { checkScoAudioState(); if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { @@ -325,6 +333,8 @@ public class BtHelper { clearAllScoClients(exceptPid, true); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode, @NonNull String eventSource) { ScoClient client = getScoClient(cb, true); @@ -344,6 +354,8 @@ public class BtHelper { Binder.restoreCallingIdentity(ident); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb, @NonNull String eventSource) { ScoClient client = getScoClient(cb, false); @@ -401,6 +413,8 @@ public class BtHelper { mDeviceBroker.postDisconnectHearingAid(); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void resetBluetoothSco() { clearAllScoClients(0, false); mScoAudioState = SCO_STATE_INACTIVE; @@ -409,6 +423,8 @@ public class BtHelper { mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void disconnectHeadset() { setBtScoActiveDevice(null); mBluetoothHeadset = null; @@ -454,6 +470,8 @@ public class BtHelper { /*eventSource*/ "mBluetoothProfileServiceListener"); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) { // Discard timeout message mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); @@ -540,6 +558,9 @@ public class BtHelper { return result; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") private void setBtScoActiveDevice(BluetoothDevice btDevice) { Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice); final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice; @@ -621,6 +642,20 @@ public class BtHelper { }; //---------------------------------------------------------------------- + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + /*package*/ synchronized void scoClientDied(Object obj) { + final ScoClient client = (ScoClient) obj; + Log.w(TAG, "SCO client died"); + int index = mScoClients.indexOf(client); + if (index < 0) { + Log.w(TAG, "unregistered SCO client died"); + } else { + client.clearCount(true); + mScoClients.remove(client); + } + } + private class ScoClient implements IBinder.DeathRecipient { private IBinder mCb; // To be notified of client's death private int mCreatorPid; @@ -634,21 +669,14 @@ public class BtHelper { @Override public void binderDied() { - // this is the only place the implementation of ScoClient needs to be synchronized - // on the instance, as all other methods are directly or indirectly called from - // package-private methods, which are synchronized - synchronized (BtHelper.this) { - Log.w(TAG, "SCO client died"); - int index = mScoClients.indexOf(this); - if (index < 0) { - Log.w(TAG, "unregistered SCO client died"); - } else { - clearCount(true); - mScoClients.remove(this); - } - } + // process this from DeviceBroker's message queue to take the right locks since + // this event can impact SCO mode and requires querying audio mode stack + mDeviceBroker.postScoClientDied(this); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") void incCount(int scoAudioMode) { requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); if (mStartcount == 0) { @@ -663,6 +691,9 @@ public class BtHelper { mStartcount++; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") void decCount() { if (mStartcount == 0) { Log.w(TAG, "ScoClient.decCount() already 0"); @@ -679,6 +710,9 @@ public class BtHelper { } } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") void clearCount(boolean stopSco) { if (mStartcount != 0) { try { @@ -714,6 +748,9 @@ public class BtHelper { return count; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") private void requestScoState(int state, int scoAudioMode) { checkScoAudioState(); int clientCount = totalCount(); @@ -728,74 +765,71 @@ public class BtHelper { broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); // Accept SCO audio activation only in NORMAL audio mode or if the mode is // currently controlled by the same client process. - // TODO do not sync that way, see b/123769055 - synchronized (mDeviceBroker.mSetModeLock) { - int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty() - ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid(); - if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) { - Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid " - + modeOwnerPid + " != creatorPid " + mCreatorPid); - broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - return; - } - switch (mScoAudioState) { - case SCO_STATE_INACTIVE: - mScoAudioMode = scoAudioMode; - if (scoAudioMode == SCO_MODE_UNDEFINED) { - mScoAudioMode = SCO_MODE_VIRTUAL_CALL; - if (mBluetoothHeadsetDevice != null) { - mScoAudioMode = Settings.Global.getInt( - mDeviceBroker.getContentResolver(), - "bluetooth_sco_channel_" - + mBluetoothHeadsetDevice.getAddress(), - SCO_MODE_VIRTUAL_CALL); - if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) { - mScoAudioMode = SCO_MODE_VIRTUAL_CALL; - } - } - } - if (mBluetoothHeadset == null) { - if (getBluetoothHeadset()) { - mScoAudioState = SCO_STATE_ACTIVATE_REQ; - } else { - Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" - + " connection, mScoAudioMode=" + mScoAudioMode); - broadcastScoConnectionState( - AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty() + ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid(); + if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) { + Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid " + + modeOwnerPid + " != creatorPid " + mCreatorPid); + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + return; + } + switch (mScoAudioState) { + case SCO_STATE_INACTIVE: + mScoAudioMode = scoAudioMode; + if (scoAudioMode == SCO_MODE_UNDEFINED) { + mScoAudioMode = SCO_MODE_VIRTUAL_CALL; + if (mBluetoothHeadsetDevice != null) { + mScoAudioMode = Settings.Global.getInt( + mDeviceBroker.getContentResolver(), + "bluetooth_sco_channel_" + + mBluetoothHeadsetDevice.getAddress(), + SCO_MODE_VIRTUAL_CALL); + if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) { + mScoAudioMode = SCO_MODE_VIRTUAL_CALL; } - break; - } - if (mBluetoothHeadsetDevice == null) { - Log.w(TAG, "requestScoState: no active device while connecting," - + " mScoAudioMode=" + mScoAudioMode); - broadcastScoConnectionState( - AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - break; } - if (connectBluetoothScoAudioHelper(mBluetoothHeadset, - mBluetoothHeadsetDevice, mScoAudioMode)) { - mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + } + if (mBluetoothHeadset == null) { + if (getBluetoothHeadset()) { + mScoAudioState = SCO_STATE_ACTIVATE_REQ; } else { - Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice - + " failed, mScoAudioMode=" + mScoAudioMode); + Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" + + " connection, mScoAudioMode=" + mScoAudioMode); broadcastScoConnectionState( AudioManager.SCO_AUDIO_STATE_DISCONNECTED); } break; - case SCO_STATE_DEACTIVATING: - mScoAudioState = SCO_STATE_ACTIVATE_REQ; + } + if (mBluetoothHeadsetDevice == null) { + Log.w(TAG, "requestScoState: no active device while connecting," + + " mScoAudioMode=" + mScoAudioMode); + broadcastScoConnectionState( + AudioManager.SCO_AUDIO_STATE_DISCONNECTED); break; - case SCO_STATE_DEACTIVATE_REQ: + } + if (connectBluetoothScoAudioHelper(mBluetoothHeadset, + mBluetoothHeadsetDevice, mScoAudioMode)) { mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; - broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); - break; - default: - Log.w(TAG, "requestScoState: failed to connect in state " - + mScoAudioState + ", scoAudioMode=" + scoAudioMode); - broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - break; + } else { + Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice + + " failed, mScoAudioMode=" + mScoAudioMode); + broadcastScoConnectionState( + AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + } + break; + case SCO_STATE_DEACTIVATING: + mScoAudioState = SCO_STATE_ACTIVATE_REQ; + break; + case SCO_STATE_DEACTIVATE_REQ: + mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); + break; + default: + Log.w(TAG, "requestScoState: failed to connect in state " + + mScoAudioState + ", scoAudioMode=" + scoAudioMode); + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + break; - } } } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { switch (mScoAudioState) { @@ -906,6 +940,9 @@ public class BtHelper { return null; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") private void clearAllScoClients(int exceptPid, boolean stopSco) { ScoClient savedClient = null; for (ScoClient cl : mScoClients) { diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index 1e58b454a15b..5c93071fd551 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -162,6 +162,12 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } } + /*package*/ boolean hasAudioFocusUsers() { + synchronized (mAudioFocusLock) { + return !mFocusStack.empty(); + } + } + /** * Discard the current audio focus owner. * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 258c325716f2..d28482ef82ab 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -52,6 +52,7 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; +import android.view.autofill.AutofillManagerInternal; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -159,6 +160,7 @@ public class ClipboardService extends SystemService { private final PackageManager mPm; private final AppOpsManager mAppOps; private final ContentCaptureManagerInternal mContentCaptureInternal; + private final AutofillManagerInternal mAutofillInternal; private final IBinder mPermissionOwner; private HostClipboardMonitor mHostClipboardMonitor = null; private Thread mHostMonitorThread = null; @@ -179,6 +181,7 @@ public class ClipboardService extends SystemService { mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE); mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); mContentCaptureInternal = LocalServices.getService(ContentCaptureManagerInternal.class); + mAutofillInternal = LocalServices.getService(AutofillManagerInternal.class); final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard"); mPermissionOwner = permOwner; if (IS_EMULATOR) { @@ -653,13 +656,18 @@ public class ClipboardService extends SystemService { // Clipboard can only be read by applications with focus.. boolean allowed = mWm.isUidFocused(callingUid); if (!allowed && mContentCaptureInternal != null) { - // ...or the Intelligence Service + // ...or the Content Capture Service allowed = mContentCaptureInternal.isContentCaptureServiceForUser(callingUid, userId); } + if (!allowed && mAutofillInternal != null) { + // ...or the Augmented Autofill Service + allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(callingUid, + userId); + } if (!allowed) { Slog.e(TAG, "Denying clipboard access to " + callingPackage - + ", application is not in focus neither is the IntelligeService for " + + ", application is not in focus neither is a system service for " + "user " + userId); } return allowed; diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java index 948c690956d3..a1a8e355dcec 100644 --- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java +++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java @@ -33,6 +33,7 @@ import android.text.TextUtils; import android.util.Pair; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.TrafficStatsConstants; import libcore.io.IoUtils; @@ -381,7 +382,8 @@ public class NetworkDiagnostics { protected void setupSocket( int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort) throws ErrnoException, IOException { - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_PROBE); try { mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol); } finally { diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 1ac09ad462a1..f6ce2dc68b99 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -39,6 +39,7 @@ import android.provider.Settings; import android.util.Log; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.TrafficStatsConstants; import com.android.net.IProxyCallback; import com.android.net.IProxyPortListener; import com.android.net.IProxyService; @@ -111,7 +112,8 @@ public class PacManager { String file; final Uri pacUrl = mPacUrl; if (Uri.EMPTY.equals(pacUrl)) return; - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PAC); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_PAC); try { file = get(pacUrl); } catch (IOException ioe) { diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index a2515c8bb8cc..580150ed887e 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1228,6 +1228,24 @@ public final class MultiClientInputMethodManagerService { public boolean isUidAllowedOnDisplay(int displayId, int uid) { return mIWindowManagerInternal.isUidAllowedOnDisplay(displayId, uid); } + + @BinderThread + @Override + public void setActive(int clientId, boolean active) { + synchronized (mPerUserData.mLock) { + final InputMethodClientInfo clientInfo = + mPerUserData.getClientFromIdLocked(clientId); + if (clientInfo == null) { + Slog.e(TAG, "Unknown clientId=" + clientId); + return; + } + try { + clientInfo.mClient.setActive(active, false /* fullscreen */); + } catch (RemoteException e) { + return; + } + } + } } /** diff --git a/services/core/java/com/android/server/location/GpsXtraDownloader.java b/services/core/java/com/android/server/location/GpsXtraDownloader.java index c012ee41b29b..7dffcb4c32a9 100644 --- a/services/core/java/com/android/server/location/GpsXtraDownloader.java +++ b/services/core/java/com/android/server/location/GpsXtraDownloader.java @@ -20,6 +20,8 @@ import android.net.TrafficStats; import android.text.TextUtils; import android.util.Log; +import com.android.internal.util.TrafficStatsConstants; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -92,7 +94,8 @@ public class GpsXtraDownloader { // load balance our requests among the available servers while (result == null) { - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_GPS); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_GPS); try { result = doDownload(mXtraServers[mNextServerIndex]); } finally { diff --git a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java index 686ae2bbaede..5cbd237a0722 100644 --- a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java +++ b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java @@ -52,10 +52,12 @@ public class PasswordSlotManager { // This maps each used password slot to the OS image that created it. Password slots are // integer keys/indices into secure storage. The OS image is recorded as a string. The factory // image is "host" and GSIs are "gsi<N>" where N >= 1. - private final Map<Integer, String> mSlotMap; + private Map<Integer, String> mSlotMap; + + // Cache the active slots until loadSlotMap() is called. + private Set<Integer> mActiveSlots; public PasswordSlotManager() { - mSlotMap = loadSlotMap(); } @VisibleForTesting @@ -74,6 +76,11 @@ public class PasswordSlotManager { * @throws RuntimeException */ public void refreshActiveSlots(Set<Integer> activeSlots) throws RuntimeException { + if (mSlotMap == null) { + mActiveSlots = new HashSet<Integer>(activeSlots); + return; + } + // Update which slots are owned by the current image. final HashSet<Integer> slotsToDelete = new HashSet<Integer>(); for (Map.Entry<Integer, String> entry : mSlotMap.entrySet()) { @@ -100,6 +107,7 @@ public class PasswordSlotManager { * @throws RuntimeException */ public void markSlotInUse(int slot) throws RuntimeException { + ensureSlotMapLoaded(); if (mSlotMap.containsKey(slot) && !mSlotMap.get(slot).equals(getMode())) { throw new RuntimeException("password slot " + slot + " is not available"); } @@ -113,6 +121,7 @@ public class PasswordSlotManager { * @throws RuntimeException */ public void markSlotDeleted(int slot) throws RuntimeException { + ensureSlotMapLoaded(); if (mSlotMap.containsKey(slot) && mSlotMap.get(slot) != getMode()) { throw new RuntimeException("password slot " + slot + " cannot be deleted"); } @@ -126,6 +135,7 @@ public class PasswordSlotManager { * @return Integer set of all used slots. */ public Set<Integer> getUsedSlots() { + ensureSlotMapLoaded(); return Collections.unmodifiableSet(mSlotMap.keySet()); } @@ -167,8 +177,21 @@ public class PasswordSlotManager { return new HashMap<Integer, String>(); } + private void ensureSlotMapLoaded() { + if (mSlotMap == null) { + mSlotMap = loadSlotMap(); + if (mActiveSlots != null) { + refreshActiveSlots(mActiveSlots); + mActiveSlots = null; + } + } + } + @VisibleForTesting protected void saveSlotMap(OutputStream stream) throws IOException { + if (mSlotMap == null) { + return; + } final Properties props = new Properties(); for (Map.Entry<Integer, String> entry : mSlotMap.entrySet()) { props.setProperty(entry.getKey().toString(), entry.getValue()); @@ -177,6 +200,9 @@ public class PasswordSlotManager { } private void saveSlotMap() { + if (mSlotMap == null) { + return; + } if (!getSlotMapFile().getParentFile().exists()) { Slog.w(TAG, "Not saving slot map, " + getSlotMapDir() + " does not exist"); return; diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java index a5f22175331d..fdb499b5cd0e 100644 --- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -140,6 +140,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { private AudioPlayerStateMonitor mAudioPlayerStateMonitor; // Used to notify System UI and Settings when remote volume was changed. + @GuardedBy("mLock") final RemoteCallbackList<IRemoteVolumeController> mRemoteVolumeControllers = new RemoteCallbackList<>(); @@ -287,17 +288,19 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { if (!session.isActive()) { return; } - int size = mRemoteVolumeControllers.beginBroadcast(); - MediaSession.Token token = session.getSessionToken(); - for (int i = size - 1; i >= 0; i--) { - try { - IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i); - cb.remoteVolumeChanged(token, flags); - } catch (Exception e) { - Log.w(TAG, "Error sending volume change.", e); + synchronized (mLock) { + int size = mRemoteVolumeControllers.beginBroadcast(); + MediaSession.Token token = session.getSessionToken(); + for (int i = size - 1; i >= 0; i--) { + try { + IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i); + cb.remoteVolumeChanged(token, flags); + } catch (Exception e) { + Log.w(TAG, "Error sending volume change.", e); + } } + mRemoteVolumeControllers.finishBroadcast(); } - mRemoteVolumeControllers.finishBroadcast(); } @Override @@ -647,19 +650,21 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { return; } - int size = mRemoteVolumeControllers.beginBroadcast(); - MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); - MediaSession.Token token = record == null ? null : record.getSessionToken(); + synchronized (mLock) { + int size = mRemoteVolumeControllers.beginBroadcast(); + MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); + MediaSession.Token token = record == null ? null : record.getSessionToken(); - for (int i = size - 1; i >= 0; i--) { - try { - IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i); - cb.updateRemoteController(token); - } catch (Exception e) { - Log.w(TAG, "Error sending default remote volume.", e); + for (int i = size - 1; i >= 0; i--) { + try { + IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i); + cb.updateRemoteController(token); + } catch (Exception e) { + Log.w(TAG, "Error sending default remote volume.", e); + } } + mRemoteVolumeControllers.finishBroadcast(); } - mRemoteVolumeControllers.finishBroadcast(); } void pushSession2TokensChangedLocked(int userId) { @@ -1676,11 +1681,13 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); - try { - enforceStatusBarServicePermission("listen for volume changes", pid, uid); - mRemoteVolumeControllers.register(rvc); - } finally { - Binder.restoreCallingIdentity(token); + synchronized (mLock) { + try { + enforceStatusBarServicePermission("listen for volume changes", pid, uid); + mRemoteVolumeControllers.register(rvc); + } finally { + Binder.restoreCallingIdentity(token); + } } } @@ -1689,11 +1696,13 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); - try { - enforceStatusBarServicePermission("listen for volume changes", pid, uid); - mRemoteVolumeControllers.unregister(rvc); - } finally { - Binder.restoreCallingIdentity(token); + synchronized (mLock) { + try { + enforceStatusBarServicePermission("listen for volume changes", pid, uid); + mRemoteVolumeControllers.unregister(rvc); + } finally { + Binder.restoreCallingIdentity(token); + } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ee2e4f573fb9..042ac8c5760e 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -455,7 +455,6 @@ public class NotificationManagerService extends SystemService { private int mAutoGroupAtCount; private boolean mIsTelevision; private boolean mIsAutomotive; - private boolean mNotificationEffectsEnabledForAutomotive; private MetricsLogger mMetricsLogger; private TriPredicate<String, Integer, String> mAllowedManagedServicePackages; @@ -1687,11 +1686,12 @@ public class NotificationManagerService extends SystemService { mIsAutomotive = mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0); - mNotificationEffectsEnabledForAutomotive = - resources.getBoolean(R.bool.config_enableServerNotificationEffectsForAutomotive); mPreferencesHelper.lockChannelsForOEM(getContext().getResources().getStringArray( com.android.internal.R.array.config_nonBlockableNotificationPackages)); + + mZenModeHelper.setPriorityOnlyDndExemptPackages(getContext().getResources().getStringArray( + com.android.internal.R.array.config_priorityOnlyDndExemptPackages)); } @Override @@ -5563,9 +5563,6 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting @GuardedBy("mNotificationLock") void buzzBeepBlinkLocked(NotificationRecord record) { - if (mIsAutomotive && !mNotificationEffectsEnabledForAutomotive) { - return; - } boolean buzz = false; boolean beep = false; boolean blink = false; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 7e74cc2368cd..1f5b99cb3349 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -80,6 +80,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -96,7 +97,7 @@ public class ZenModeHelper { private final Context mContext; private final H mHandler; private final SettingsObserver mSettingsObserver; - @VisibleForTesting protected final AppOpsManager mAppOps; + private final AppOpsManager mAppOps; @VisibleForTesting protected final NotificationManager mNotificationManager; @VisibleForTesting protected ZenModeConfig mDefaultConfig; private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); @@ -123,11 +124,13 @@ public class ZenModeHelper { @VisibleForTesting protected boolean mIsBootComplete; + private String[] mPriorityOnlyDndExemptPackages; + public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) { mContext = context; mHandler = new H(looper); addCallback(mMetrics); - mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mAppOps = context.getSystemService(AppOpsManager.class); mNotificationManager = context.getSystemService(NotificationManager.class); mDefaultConfig = readDefaultConfig(mContext.getResources()); @@ -214,6 +217,10 @@ public class ZenModeHelper { loadConfigForUser(user, "onUserUnlocked"); } + void setPriorityOnlyDndExemptPackages(String[] packages) { + mPriorityOnlyDndExemptPackages = packages; + } + private void loadConfigForUser(int user, String reason) { if (mUser == user || user < UserHandle.USER_SYSTEM) return; mUser = user; @@ -994,53 +1001,47 @@ public class ZenModeHelper { for (int usage : AudioAttributes.SDK_USAGES) { final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage); if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NEVER) { - applyRestrictions(false /*mute*/, usage); + applyRestrictions(zenPriorityOnly, false /*mute*/, usage); } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NOTIFICATION) { - applyRestrictions(muteNotifications || muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteNotifications || muteEverything, usage); } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_CALL) { - applyRestrictions(muteCalls || muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteCalls || muteEverything, usage); } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_ALARM) { - applyRestrictions(muteAlarms || muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteAlarms || muteEverything, usage); } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_MEDIA) { - applyRestrictions(muteMedia || muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteMedia || muteEverything, usage); } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_SYSTEM) { if (usage == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) { // normally DND will only restrict touch sounds, not haptic feedback/vibrations - applyRestrictions(muteSystem || muteEverything, usage, + applyRestrictions(zenPriorityOnly, muteSystem || muteEverything, usage, AppOpsManager.OP_PLAY_AUDIO); - applyRestrictions(false, usage, AppOpsManager.OP_VIBRATE); + applyRestrictions(zenPriorityOnly, false, usage, AppOpsManager.OP_VIBRATE); } else { - applyRestrictions(muteSystem || muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteSystem || muteEverything, usage); } } else { - applyRestrictions(muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteEverything, usage); } } } @VisibleForTesting - protected void applyRestrictions(boolean mute, int usage, int code) { - final String[] exceptionPackages = null; // none (for now) - - // Only do this if we are executing within the system process... otherwise - // we are running as test code, so don't have access to the protected call. - if (Process.myUid() == Process.SYSTEM_UID) { - final long ident = Binder.clearCallingIdentity(); - try { - mAppOps.setRestriction(code, usage, - mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, - exceptionPackages); - } finally { - Binder.restoreCallingIdentity(ident); - } + protected void applyRestrictions(boolean zenPriorityOnly, boolean mute, int usage, int code) { + final long ident = Binder.clearCallingIdentity(); + try { + mAppOps.setRestriction(code, usage, + mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, + zenPriorityOnly ? mPriorityOnlyDndExemptPackages : null); + } finally { + Binder.restoreCallingIdentity(ident); } } @VisibleForTesting - protected void applyRestrictions(boolean mute, int usage) { - applyRestrictions(mute, usage, AppOpsManager.OP_VIBRATE); - applyRestrictions(mute, usage, AppOpsManager.OP_PLAY_AUDIO); + protected void applyRestrictions(boolean zenPriorityOnly, boolean mute, int usage) { + applyRestrictions(zenPriorityOnly, mute, usage, AppOpsManager.OP_VIBRATE); + applyRestrictions(zenPriorityOnly, mute, usage, AppOpsManager.OP_PLAY_AUDIO); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7b418b51957c..c4d4106804e1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2801,6 +2801,11 @@ public class PackageManagerService extends IPackageManager.Stub if (disabledPs.codePath == null || !disabledPs.codePath.exists() || disabledPs.pkg == null) { possiblyDeletedUpdatedSystemApps.add(ps.name); + } else { + // We're expecting that the system app should remain disabled, but add + // it to expecting better to recover in case the data version cannot + // be scanned. + mExpectingBetter.put(disabledPs.name, disabledPs.codePath); } } } @@ -5733,6 +5738,8 @@ public class PackageManagerService extends IPackageManager.Stub "getWhitelistedRestrictedPermissions for user " + userId); } + final PackageParser.Package pkg; + synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { @@ -5740,6 +5747,8 @@ public class PackageManagerService extends IPackageManager.Stub return null; } + pkg = packageSetting.pkg; + final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; @@ -5767,14 +5776,14 @@ public class PackageManagerService extends IPackageManager.Stub UserHandle.getCallingUserId())) { return null; } + } - final long identity = Binder.clearCallingIdentity(); - try { - return mPermissionManager.getWhitelistedRestrictedPermissions( - packageSetting.pkg, whitelistFlags, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } + final long identity = Binder.clearCallingIdentity(); + try { + return mPermissionManager.getWhitelistedRestrictedPermissions( + pkg, whitelistFlags, userId); + } finally { + Binder.restoreCallingIdentity(identity); } } @@ -5785,6 +5794,10 @@ public class PackageManagerService extends IPackageManager.Stub // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); + if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { + return false; + } + List<String> permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions == null) { @@ -5792,13 +5805,31 @@ public class PackageManagerService extends IPackageManager.Stub } if (permissions.indexOf(permission) < 0) { permissions.add(permission); - setWhitelistedRestrictedPermissions(packageName, permissions, + return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); - return true; } return false; } + private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission( + @NonNull String permission) { + synchronized (mPackages) { + final BasePermission bp = mPermissionManager.getPermissionTEMP(permission); + if (bp == null) { + Slog.w(TAG, "No such permissions: " + permission); + return false; + } + if (bp.isHardOrSoftRestricted() && bp.isImmutablyRestricted() + && mContext.checkCallingOrSelfPermission( + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Cannot modify whitelisting of an immutably " + + "restricted permission: " + permission); + } + return true; + } + } + @Override public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, @@ -5806,17 +5837,20 @@ public class PackageManagerService extends IPackageManager.Stub // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); + if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { + return false; + } + final List<String> permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions != null && permissions.remove(permission)) { - setWhitelistedRestrictedPermissions(packageName, permissions, + return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); - return true; } return false; } - private void setWhitelistedRestrictedPermissions(@NonNull String packageName, + private boolean setWhitelistedRestrictedPermissions(@NonNull String packageName, @Nullable List<String> permissions, @PermissionWhitelistFlags int whitelistFlag, @UserIdInt int userId) { Preconditions.checkNotNull(packageName); @@ -5833,13 +5867,17 @@ public class PackageManagerService extends IPackageManager.Stub "setWhitelistedRestrictedPermissions for user " + userId); } + final PackageParser.Package pkg; + synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { Slog.w(TAG, "Unknown package: " + packageName); - return; + return false; } + pkg = packageSetting.pkg; + final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; @@ -5864,7 +5902,7 @@ public class PackageManagerService extends IPackageManager.Stub packageName, whitelistFlag, userId); if (permissions == null || permissions.isEmpty()) { if (whitelistedPermissions == null || whitelistedPermissions.isEmpty()) { - return; + return true; } } else { // Only the system can add and remove while the installer can only remove. @@ -5890,18 +5928,20 @@ public class PackageManagerService extends IPackageManager.Stub if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), UserHandle.getCallingUserId())) { - return; + return false; } + } - final long identity = Binder.clearCallingIdentity(); - try { - mPermissionManager.setWhitelistedRestrictedPermissions(packageSetting.pkg, - new int[]{userId}, permissions, Process.myUid(), whitelistFlag, - mPermissionCallback); - } finally { - Binder.restoreCallingIdentity(identity); - } + final long identity = Binder.clearCallingIdentity(); + try { + mPermissionManager.setWhitelistedRestrictedPermissions(pkg, + new int[]{userId}, permissions, Process.myUid(), whitelistFlag, + mPermissionCallback); + } finally { + Binder.restoreCallingIdentity(identity); } + + return true; } @Override @@ -9334,6 +9374,7 @@ public class PackageManagerService extends IPackageManager.Stub | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { synchronized (mPackages) { + boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked( @@ -9346,11 +9387,12 @@ public class PackageManagerService extends IPackageManager.Stub Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), mSettings.mKeySetManagerService); - prepareScanResultLocked(scanResult); - commitReconciledScanResultLocked( - reconcileResult.get(pkgName)); + appIdCreated = optimisticallyRegisterAppId(scanResult); + commitReconciledScanResultLocked(reconcileResult.get(pkgName)); } catch (PackageManagerException e) { - unprepareScanResultLocked(scanResult); + if (appIdCreated) { + cleanUpAppIdCreation(scanResult); + } throw e; } } @@ -10837,27 +10879,32 @@ public class PackageManagerService extends IPackageManager.Stub } - /** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */ - private void prepareScanResultLocked(@NonNull ScanResult result) + /** + * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering + * the app ID required for reconcile. + * @return {@code true} if a new app ID was registered and will need to be cleaned up on + * failure. + */ + private boolean optimisticallyRegisterAppId(@NonNull ScanResult result) throws PackageManagerException { if (!result.existingSettingCopied) { // THROWS: when we can't allocate a user id. add call to check if there's // enough space to ensure we won't throw; otherwise, don't modify state - mSettings.registerAppIdLPw(result.pkgSetting); + return mSettings.registerAppIdLPw(result.pkgSetting); } + return false; } /** - * Reverts any changes to the system that were made by - * {@link #prepareScanResultLocked(ScanResult)} + * Reverts any app ID creation that were made by + * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the + * referenced method returned true. */ - private void unprepareScanResultLocked(@NonNull ScanResult result) { - if (!result.existingSettingCopied) { - // iff we've acquired an app ID for a new package setting, remove it so that it can be - // acquired by another request. - if (result.pkgSetting.appId > 0) { - mSettings.removeAppIdLPw(result.pkgSetting.appId); - } + private void cleanUpAppIdCreation(@NonNull ScanResult result) { + // iff we've acquired an app ID for a new package setting, remove it so that it can be + // acquired by another request. + if (result.pkgSetting.appId > 0) { + mSettings.removeAppIdLPw(result.pkgSetting.appId); } } @@ -14608,6 +14655,20 @@ public class PackageManagerService extends IPackageManager.Stub return mUser; } + /** + * Gets the user handle for the user that the rollback agent should + * use to look up information about this installation when enabling + * rollback. + */ + UserHandle getRollbackUser() { + // The session for packages installed for "all" users is + // associated with the "system" user. + if (mUser == UserHandle.ALL) { + return UserHandle.SYSTEM; + } + return mUser; + } + HandlerParams setTraceMethod(String traceMethod) { this.traceMethod = traceMethod; return this; @@ -15226,7 +15287,7 @@ public class PackageManagerService extends IPackageManager.Stub installedUsers); enableRollbackIntent.putExtra( PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_USER, - getUser().getIdentifier()); + getRollbackUser().getIdentifier()); enableRollbackIntent.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)), PACKAGE_MIME_TYPE); enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); @@ -16701,6 +16762,7 @@ public class PackageManagerService extends IPackageManager.Stub final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size()); final Map<String, PackageSetting> lastStaticSharedLibSettings = new ArrayMap<>(requests.size()); + final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size()); boolean success = false; try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI"); @@ -16740,7 +16802,7 @@ public class PackageManagerService extends IPackageManager.Stub + " in multi-package install request."); return; } - prepareScanResultLocked(result); + createdAppId.put(packageName, optimisticallyRegisterAppId(result)); versionInfos.put(result.pkgSetting.pkg.packageName, getSettingsVersionForPackage(result.pkgSetting.pkg)); if (result.staticSharedLibraryInfo != null) { @@ -16797,7 +16859,9 @@ public class PackageManagerService extends IPackageManager.Stub } finally { if (!success) { for (ScanResult result : preparedScans.values()) { - unprepareScanResultLocked(result); + if (createdAppId.getOrDefault(result.request.pkg.packageName, false)) { + cleanUpAppIdCreation(result); + } } } for (PrepareResult result : prepareResults.values()) { @@ -19890,26 +19954,28 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "replace preferred activity"); - synchronized (mPackages) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS) - != PackageManager.PERMISSION_GRANTED) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS) + != PackageManager.PERMISSION_GRANTED) { + synchronized (mPackages) { if (getUidTargetSdkVersionLockedLPr(callingUid) < Build.VERSION_CODES.FROYO) { Slog.w(TAG, "Ignoring replacePreferredActivity() from uid " + Binder.getCallingUid()); return; } - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + } - PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); + synchronized (mPackages) { + final PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); if (pir != null) { // Get all of the existing entries that exactly match this filter. - ArrayList<PreferredActivity> existing = pir.findFilters(filter); + final ArrayList<PreferredActivity> existing = pir.findFilters(filter); if (existing != null && existing.size() == 1) { - PreferredActivity cur = existing.get(0); + final PreferredActivity cur = existing.get(0); if (DEBUG_PREFERRED) { Slog.i(TAG, "Checking replace of preferred:"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); @@ -19939,14 +20005,13 @@ public class PackageManagerService extends IPackageManager.Stub return; } } - if (existing != null) { if (DEBUG_PREFERRED) { Slog.i(TAG, existing.size() + " existing preferred matches for:"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); } - for (int i = 0; i < existing.size(); i++) { - PreferredActivity pa = existing.get(i); + for (int i = existing.size() - 1; i >= 0; --i) { + final PreferredActivity pa = existing.get(i); if (DEBUG_PREFERRED) { Slog.i(TAG, "Removing existing preferred activity " + pa.mPref.mComponent + ":"); @@ -19956,9 +20021,9 @@ public class PackageManagerService extends IPackageManager.Stub } } } - addPreferredActivityInternal(filter, match, set, activity, true, userId, - "Replacing preferred"); } + addPreferredActivityInternal(filter, match, set, activity, true, userId, + "Replacing preferred"); } @Override @@ -20032,7 +20097,7 @@ public class PackageManagerService extends IPackageManager.Stub PreferredActivity pa = removed.get(j); pir.removeFilter(pa); } - outUserChanged.setValueAt(thisUserId, true); + outUserChanged.put(thisUserId, true); } } } @@ -20752,8 +20817,16 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public String getAttentionServicePackageName() { - return mContext.getString(R.string.config_defaultAttentionService); + public @Nullable String getAttentionServicePackageName() { + final String flattenedComponentName = + mContext.getString(R.string.config_defaultAttentionService); + if (flattenedComponentName != null) { + ComponentName componentName = ComponentName.unflattenFromString(flattenedComponentName); + if (componentName != null && componentName.getPackageName() != null) { + return componentName.getPackageName(); + } + } + return null; } private @Nullable String getDocumenterPackageName() { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 4e4a0e420d86..6b804df2e068 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2423,6 +2423,16 @@ class PackageManagerShellCommand extends ShellCommand { sessionParams.setStaged(); break; case "--enable-rollback": + if (params.installerPackageName == null) { + // com.android.shell has the TEST_MANAGE_ROLLBACKS + // permission needed to enable rollback for non-module + // packages, which is likely what the user wants when + // enabling rollback through the shell command. Set + // the installer to com.android.shell if no installer + // has been provided so that the user doesn't have to + // remember to set it themselves. + params.installerPackageName = "com.android.shell"; + } sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK; break; default: diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 41a8a776e3f7..db2fba97f4ac 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -810,15 +810,20 @@ public final class Settings { /** * Registers a user ID with the system. Potentially allocates a new user ID. + * @return {@code true} if a new app ID was created in the process. {@code false} can be + * returned in the case that a shared user ID already exists or the explicit app ID is + * already registered. * @throws PackageManagerException If a user ID could not be allocated. */ - void registerAppIdLPw(PackageSetting p) throws PackageManagerException { + boolean registerAppIdLPw(PackageSetting p) throws PackageManagerException { + final boolean createdNew; if (p.appId == 0) { // Assign new user ID p.appId = acquireAndRegisterNewAppIdLPw(p); + createdNew = true; } else { // Add new setting to list of user IDs - registerExistingAppIdLPw(p.appId, p, p.name); + createdNew = registerExistingAppIdLPw(p.appId, p, p.name); } if (p.appId < 0) { PackageManagerService.reportSettingsProblem(Log.WARN, @@ -826,6 +831,7 @@ public final class Settings { throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Package " + p.name + " could not be assigned a valid UID"); } + return createdNew; } /** @@ -4413,7 +4419,7 @@ public final class Settings { ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION", ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE", ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, "ALLOW_AUDIO_PLAYBACK_CAPTURE", - ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX, "ALLOW_EXTERNAL_STORAGE_SANDBOX", + ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE, "PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE", ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND", ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE", ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE", @@ -4737,7 +4743,7 @@ public final class Settings { } pw.print(prefix); pw.print(" "); pw.print(perm); final BasePermission bp = mPermissions.getPermission(perm); - if (bp != null && bp.isRestricted()) { + if (bp != null && bp.isHardOrSoftRestricted()) { pw.println(": restricted=true"); } else { pw.println(); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 72d543862c58..803ab2d299e2 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -261,13 +261,13 @@ public class StagingManager { if (storageManager.supportsCheckpoint()) { storageManager.startCheckpoint(1 /* numRetries */); } - } catch (RemoteException e) { + } catch (Exception e) { // TODO(b/130190815) make a RemoteException again // While StorageManager lives in the same process, the native implementation // it calls through lives in 'vold'; so, this call can fail if 'vold' isn't // reachable. // Since we can live without filesystem checkpointing, just warn in this case // and continue. - Slog.w(TAG, "Could not start filesystem checkpoint."); + Slog.w(TAG, "Could not start filesystem checkpoint:", e); } session.setStagedSessionReady(); diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index 490c647c22b1..6d22faa7032e 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -203,12 +203,17 @@ public final class BasePermission { && (perm.info.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0; } - public boolean isRestricted() { + public boolean isHardOrSoftRestricted() { return perm != null && perm.info != null && (perm.info.flags & (PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0; } + public boolean isImmutablyRestricted() { + return perm != null && perm.info != null + && (perm.info.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0; + } + public boolean isSignature() { return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) == PermissionInfo.PROTECTION_SIGNATURE; diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index dd63e3ca290e..9ede263284a1 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2059,7 +2059,7 @@ public class PermissionManagerService { return; } - if (RESTRICTED_PERMISSIONS_ENABLED && bp.isRestricted() + if (RESTRICTED_PERMISSIONS_ENABLED && bp.isHardOrSoftRestricted() && (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) { Log.e(TAG, "Cannot grant restricted non-exempt permission " + permName + " for package " + packageName); @@ -2241,7 +2241,7 @@ public class PermissionManagerService { continue; } - if (!bp.isRestricted()) { + if (!bp.isHardOrSoftRestricted()) { continue; } @@ -2294,13 +2294,16 @@ public class PermissionManagerService { updatePermissions = true; + final boolean wasWhitelisted = (oldFlags + & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; + final boolean isWhitelisted = (newFlags + & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; + // If the permission is policy fixed as granted but it is no longer // on any of the whitelists we need to clear the policy fixed flag // as whitelisting trumps policy i.e. policy cannot grant a non // grantable permission. if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { - final boolean isWhitelisted = (newFlags - & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; final boolean isGranted = permissionsState.hasPermission(permissionName, userId); if (!isWhitelisted && isGranted) { mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -2308,6 +2311,14 @@ public class PermissionManagerService { } } + // If we are whitelisting an app that does not support runtime permissions + // we need to make sure it goes through the permission review UI at launch. + if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M + && !wasWhitelisted && isWhitelisted) { + mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + } + updatePermissionFlags(permissionName, pkg.packageName, mask, newFlags, callingUid, userId, false, null /*callback*/); } diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 1fd8b711d348..a280d83fac27 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -19,6 +19,7 @@ package com.android.server.policy; import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; import static android.content.pm.PackageManager.GET_PERMISSIONS; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -36,9 +37,7 @@ import android.os.Process; import android.os.UserHandle; import android.permission.PermissionControllerManager; import android.permission.PermissionManagerInternal; -import android.util.ArraySet; import android.util.Slog; -import android.util.SparseArray; import android.util.SparseIntArray; import com.android.server.FgThread; @@ -46,7 +45,6 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CountDownLatch; /** @@ -57,17 +55,10 @@ import java.util.concurrent.CountDownLatch; * and app ops - and vise versa. */ public final class PermissionPolicyService extends SystemService { - private static final String PLATFORM_PACKAGE = "android"; - private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName(); - // No need to lock as this is populated on boot when the OS is - // single threaded and is never mutated until a reboot. - private static final ArraySet<String> sAllRestrictedPermissions = new ArraySet<>(); - public PermissionPolicyService(@NonNull Context context) { super(context); - cacheAllRestrictedPermissions(context); } @Override @@ -101,20 +92,6 @@ public final class PermissionPolicyService extends SystemService { startWatchingRuntimePermissionChanges(getContext(), userId); } - private static void cacheAllRestrictedPermissions(@NonNull Context context) { - try { - final PackageInfo packageInfo = context.getPackageManager() - .getPackageInfo(PLATFORM_PACKAGE, PackageManager.GET_PERMISSIONS); - for (PermissionInfo permissionInfo : packageInfo.permissions) { - if (permissionInfo.isRestricted()) { - sAllRestrictedPermissions.add(permissionInfo.name); - } - } - } catch (NameNotFoundException impossible) { - /* cannot happen */ - } - } - private static void grantOrUpgradeDefaultRuntimePermissionsInNeeded(@NonNull Context context, @UserIdInt int userId) { final PackageManagerInternal packageManagerInternal = LocalServices.getService( @@ -162,7 +139,7 @@ public final class PermissionPolicyService extends SystemService { } private static @Nullable Context getUserContext(@NonNull Context context, - @NonNull UserHandle user) { + @Nullable UserHandle user) { if (context.getUser().equals(user)) { return context; } else { @@ -230,18 +207,31 @@ public final class PermissionPolicyService extends SystemService { private final @NonNull SparseIntArray mAllUids = new SparseIntArray(); /** - * All ops that need to be restricted + * All ops that need to be set to default + * + * Currently, only used by the restricted permissions logic. * * @see #syncRestrictedOps */ - private final @NonNull ArrayList<OpToRestrict> mOpsToRestrict = new ArrayList<>(); + private final @NonNull ArrayList<OpToRestrict> mOpsToDefault = new ArrayList<>(); /** - * All ops that need to be unrestricted + * All ops that need to be flipped to allow if default. + * + * Currently, only used by the restricted permissions logic. * * @see #syncRestrictedOps */ - private final @NonNull ArrayList<OpToUnrestrict> mOpsToUnrestrict = new ArrayList<>(); + private final @NonNull ArrayList<OpToUnrestrict> mOpsToAllow = new ArrayList<>(); + + /** + * All ops that need to be flipped to ignore if default. + * + * Currently, only used by the restricted permissions logic. + * + * @see #syncRestrictedOps + */ + private final @NonNull ArrayList<OpToUnrestrict> mOpsToIgnore = new ArrayList<>(); /** * All foreground permissions @@ -262,89 +252,20 @@ public final class PermissionPolicyService extends SystemService { * <p>This processes ops previously added by {@link #addOpIfRestricted} */ private void syncRestrictedOps() { - final SparseIntArray unprocessedUids = mAllUids.clone(); - - // TRICKY: we set the app op for a restricted permission to allow if the app - // requesting the permission is whitelisted and to deny if the app requesting - // the permission is not whitelisted. However, there is another case where an - // app in a shared user can access a component in another app in the same shared - // user due to being in the same shared user and not by having the permission - // that guards the component form the rest of the world. We need to handle this. - // The way we do this is by setting app ops corresponding to non requested - // restricted permissions to allow as this would allow the shared uid access - // case and be okay for other apps as they would not have the permission and - // would fail on the permission checks before reaching the app op check. - final SparseArray<List<String>> unrequestedRestrictedPermissionsForUid = - new SparseArray<>(); - - final int unrestrictCount = mOpsToUnrestrict.size(); - for (int i = 0; i < unrestrictCount; i++) { - final OpToUnrestrict op = mOpsToUnrestrict.get(i); - setUidModeAllowed(op.code, op.uid, op.packageName); - - // Keep track this permission was requested by the UID. - List<String> unrequestedRestrictedPermissions = - unrequestedRestrictedPermissionsForUid.get(op.uid); - if (unrequestedRestrictedPermissions == null) { - unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions); - unrequestedRestrictedPermissionsForUid.put(op.uid, - unrequestedRestrictedPermissions); - } - unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(op.code)); - - unprocessedUids.delete(op.uid); + final int allowCount = mOpsToAllow.size(); + for (int i = 0; i < allowCount; i++) { + final OpToUnrestrict op = mOpsToAllow.get(i); + setUidModeAllowedIfDefault(op.code, op.uid, op.packageName); } - final int restrictCount = mOpsToRestrict.size(); - for (int i = 0; i < restrictCount; i++) { - final OpToRestrict op = mOpsToRestrict.get(i); - setUidModeDefault(op.code, op.uid); - - // Keep track this permission was requested by the UID. - List<String> unrequestedRestrictedPermissions = - unrequestedRestrictedPermissionsForUid.get(op.uid); - if (unrequestedRestrictedPermissions == null) { - unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions); - unrequestedRestrictedPermissionsForUid.put(op.uid, - unrequestedRestrictedPermissions); - } - unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(op.code)); - - unprocessedUids.delete(op.uid); - } - - // Give root access - unprocessedUids.put(Process.ROOT_UID, Process.ROOT_UID); - - // Add records for UIDs that don't use any restricted permissions. - final int uidCount = unprocessedUids.size(); - for (int i = 0; i < uidCount; i++) { - final int uid = unprocessedUids.keyAt(i); - unrequestedRestrictedPermissionsForUid.put(uid, - new ArrayList<>(sAllRestrictedPermissions)); + final int ignoreCount = mOpsToIgnore.size(); + for (int i = 0; i < ignoreCount; i++) { + final OpToUnrestrict op = mOpsToIgnore.get(i); + setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName); } - - // Flip ops for all unrequested restricted permission for the UIDs. - final int unrequestedUidCount = unrequestedRestrictedPermissionsForUid.size(); - for (int i = 0; i < unrequestedUidCount; i++) { - final List<String> unrequestedRestrictedPermissions = - unrequestedRestrictedPermissionsForUid.valueAt(i); - if (unrequestedRestrictedPermissions != null) { - final int uid = unrequestedRestrictedPermissionsForUid.keyAt(i); - final String[] packageNames = (uid != Process.ROOT_UID) - ? mPackageManager.getPackagesForUid(uid) - : new String[] {"root"}; - if (packageNames == null) { - continue; - } - final int permissionCount = unrequestedRestrictedPermissions.size(); - for (int j = 0; j < permissionCount; j++) { - final String permission = unrequestedRestrictedPermissions.get(j); - for (String packageName : packageNames) { - setUidModeAllowed(AppOpsManager.permissionToOpCode(permission), uid, - packageName); - } - } - } + final int defaultCount = mOpsToDefault.size(); + for (int i = 0; i < defaultCount; i++) { + final OpToRestrict op = mOpsToDefault.get(i); + setUidModeDefault(op.code, op.uid); } } @@ -411,12 +332,27 @@ public final class PermissionPolicyService extends SystemService { if (permissionInfo.isHardRestricted()) { if (applyRestriction) { - mOpsToRestrict.add(new OpToRestrict(uid, opCode)); + mOpsToDefault.add(new OpToRestrict(uid, opCode)); } else { - mOpsToUnrestrict.add(new OpToUnrestrict(uid, pkg.packageName, opCode)); + mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName, opCode)); } } else if (permissionInfo.isSoftRestricted()) { - //TODO: Implement soft restrictions like storage here. + // Storage uses a special app op to decide the mount state and + // supports soft restriction where the restricted state allows + // the permission but only for accessing the medial collections. + if (Manifest.permission.READ_EXTERNAL_STORAGE.equals(permission) + || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) { + if (applyRestriction) { + mOpsToDefault.add(new OpToRestrict(uid, + AppOpsManager.OP_LEGACY_STORAGE)); + } else if (pkg.applicationInfo.hasRequestedLegacyExternalStorage()) { + mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName, + AppOpsManager.OP_LEGACY_STORAGE)); + } else { + mOpsToIgnore.add(new OpToUnrestrict(uid, pkg.packageName, + AppOpsManager.OP_LEGACY_STORAGE)); + } + } } } @@ -474,11 +410,20 @@ public final class PermissionPolicyService extends SystemService { } } - private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) { + private void setUidModeAllowedIfDefault(int opCode, int uid, @NonNull String packageName) { + setUidModeIfDefault(opCode, uid, AppOpsManager.MODE_ALLOWED, packageName); + } + + private void setUidModeIgnoredIfDefault(int opCode, int uid, @NonNull String packageName) { + setUidModeIfDefault(opCode, uid, AppOpsManager.MODE_IGNORED, packageName); + } + + private void setUidModeIfDefault(int opCode, int uid, int mode, + @NonNull String packageName) { final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager .opToPublicName(opCode), uid, packageName); if (currentMode == AppOpsManager.MODE_DEFAULT) { - mAppOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_ALLOWED); + mAppOpsManager.setUidMode(opCode, uid, mode); } } diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java index d9d21babe210..5e829b2d6067 100644 --- a/services/core/java/com/android/server/power/AttentionDetector.java +++ b/services/core/java/com/android/server/power/AttentionDetector.java @@ -16,9 +16,14 @@ package com.android.server.power; +import static android.provider.Settings.System.ADAPTIVE_SLEEP; + +import android.Manifest; import android.attention.AttentionManagerInternal; import android.attention.AttentionManagerInternal.AttentionCallbackInternal; +import android.content.ContentResolver; import android.content.Context; +import android.content.pm.PackageManager; import android.database.ContentObserver; import android.os.Handler; import android.os.PowerManager; @@ -83,6 +88,12 @@ public class AttentionDetector { @VisibleForTesting protected AttentionManagerInternal mAttentionManager; + @VisibleForTesting + protected PackageManager mPackageManager; + + @VisibleForTesting + protected ContentResolver mContentResolver; + /** * Current wakefulness of the device. {@see PowerManagerInternal} */ @@ -137,6 +148,8 @@ public class AttentionDetector { public void systemReady(Context context) { updateEnabledFromSettings(context); + mPackageManager = context.getPackageManager(); + mContentResolver = context.getContentResolver(); mAttentionManager = LocalServices.getService(AttentionManagerInternal.class); mMaximumExtensionMillis = context.getResources().getInteger( com.android.internal.R.integer.config_attentionMaximumExtension); @@ -162,6 +175,11 @@ public class AttentionDetector { return nextScreenDimming; } + if (!serviceHasSufficientPermissions()) { + Settings.System.putInt(mContentResolver, ADAPTIVE_SLEEP, 0); + return nextScreenDimming; + } + final long now = SystemClock.uptimeMillis(); final long whenToCheck = nextScreenDimming - getAttentionTimeout(); final long whenToStopExtending = mLastUserActivityTime + mMaximumExtensionMillis; @@ -263,6 +281,18 @@ public class AttentionDetector { return mAttentionManager != null && mAttentionManager.isAttentionServiceSupported(); } + /** + * Returns {@code true} if the attention service has sufficient permissions, disables the + * depending features otherwise. + */ + @VisibleForTesting + boolean serviceHasSufficientPermissions() { + final String attentionPackage = mPackageManager.getAttentionServicePackageName(); + return attentionPackage != null && mPackageManager.checkPermission( + Manifest.permission.CAMERA, attentionPackage) + == PackageManager.PERMISSION_GRANTED; + } + public void dump(PrintWriter pw) { pw.print("AttentionDetector:"); pw.print(" mMaximumExtensionMillis=" + mMaximumExtensionMillis); diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 03814453fc52..1552fd517d30 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -312,10 +312,8 @@ public class ThermalManagerService extends SystemService { if (!mThermalEventListeners.register(listener, null)) { return false; } - if (mHalReady.get()) { - // Notify its callback after new client registered. - postEventListenerCurrentTemperatures(listener, null); - } + // Notify its callback after new client registered. + postEventListenerCurrentTemperatures(listener, null); return true; } finally { Binder.restoreCallingIdentity(token); @@ -334,10 +332,8 @@ public class ThermalManagerService extends SystemService { if (!mThermalEventListeners.register(listener, new Integer(type))) { return false; } - if (mHalReady.get()) { - // Notify its callback after new client registered. - postEventListenerCurrentTemperatures(listener, new Integer(type)); - } + // Notify its callback after new client registered. + postEventListenerCurrentTemperatures(listener, new Integer(type)); return true; } finally { Binder.restoreCallingIdentity(token); @@ -398,10 +394,8 @@ public class ThermalManagerService extends SystemService { if (!mThermalStatusListeners.register(listener)) { return false; } - if (mHalReady.get()) { - // Notify its callback after new client registered. - postStatusListener(listener); - } + // Notify its callback after new client registered. + postStatusListener(listener); return true; } finally { Binder.restoreCallingIdentity(token); diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index b2ac1b884483..654c47780f4a 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -147,6 +147,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C mLegacyRoleResolver = legacyRoleResolver; + RoleControllerManager.initializeRemoteServiceComponentName(context); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mAppOpsManager = context.getSystemService(AppOpsManager.class); @@ -231,7 +233,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C // Run grants again Slog.i(LOG_TAG, "Granting default permissions..."); CompletableFuture<Void> result = new CompletableFuture<>(); - getOrCreateControllerService(userId).grantDefaultRoles(FgThread.getExecutor(), + getOrCreateController(userId).grantDefaultRoles(FgThread.getExecutor(), successful -> { if (successful) { userState.setPackagesHash(packagesHash); @@ -318,7 +320,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } @NonNull - private RoleControllerManager getOrCreateControllerService(@UserIdInt int userId) { + private RoleControllerManager getOrCreateController(@UserIdInt int userId) { synchronized (mLock) { RoleControllerManager controller = mControllers.get(userId); if (controller == null) { @@ -330,7 +332,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } catch (NameNotFoundException e) { throw new RuntimeException(e); } - controller = new RoleControllerManager(context, FgThread.getHandler()); + controller = RoleControllerManager.createWithInitializedRemoteServiceComponentName( + FgThread.getHandler(), context); mControllers.put(userId, controller); } return controller; @@ -474,7 +477,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); - getOrCreateControllerService(userId).onAddRoleHolder(roleName, packageName, flags, + getOrCreateController(userId).onAddRoleHolder(roleName, packageName, flags, callback); } @@ -494,7 +497,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); - getOrCreateControllerService(userId).onRemoveRoleHolder(roleName, packageName, flags, + getOrCreateController(userId).onRemoveRoleHolder(roleName, packageName, flags, callback); } @@ -513,7 +516,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); - getOrCreateControllerService(userId).onClearRoleHolders(roleName, flags, callback); + getOrCreateController(userId).onClearRoleHolders(roleName, flags, callback); } @Override @@ -738,10 +741,10 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } }); if (packageName != null) { - getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, + getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, packageName, 0, callback); } else { - getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, + getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, callback); } try { @@ -762,10 +765,10 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } }); if (packageName != null) { - getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, + getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, packageName, 0, callback); } else { - getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, + getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, callback); } } @@ -791,10 +794,10 @@ public class RoleManagerService extends SystemService implements RoleUserState.C callback.accept(successful); }); if (packageName != null) { - getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_HOME, + getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_HOME, packageName, 0, remoteCallback); } else { - getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0, + getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0, remoteCallback); } } diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 10ed88f49231..da9bc1646f4b 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -64,6 +64,7 @@ import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.CoolingDevice; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -1798,6 +1799,28 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { e.writeInt(temp.getType()); e.writeString(temp.getName()); e.writeInt((int) (temp.getValue() * 10)); + e.writeInt(temp.getStatus()); + pulledData.add(e); + } + } catch (RemoteException e) { + // Should not happen. + Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures."); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + } + + private void pullCoolingDevices(int tagId, long elapsedNanos, long wallClockNanos, + List<StatsLogEventWrapper> pulledData) { + long callingToken = Binder.clearCallingIdentity(); + try { + List<CoolingDevice> devices = sThermalService.getCurrentCoolingDevices(); + for (CoolingDevice device : devices) { + StatsLogEventWrapper e = + new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeInt(device.getType()); + e.writeString(device.getName()); + e.writeInt((int) (device.getValue())); pulledData.add(e); } } catch (RemoteException e) { @@ -2084,6 +2107,9 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, 0, userId) != 0); + e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED, 1, + userId) != 0); pulledData.add(e); } @@ -2268,6 +2294,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullTemperature(tagId, elapsedNanos, wallClockNanos, ret); break; } + case StatsLog.COOLING_DEVICE: { + pullCoolingDevices(tagId, elapsedNanos, wallClockNanos, ret); + break; + } case StatsLog.DEBUG_ELAPSED_CLOCK: { pullDebugElapsedClock(tagId, elapsedNanos, wallClockNanos, ret); break; @@ -2532,12 +2562,9 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private static final class ThermalEventListener extends IThermalEventListener.Stub { @Override public void notifyThrottling(Temperature temp) { - boolean isThrottling = temp.getStatus() >= Temperature.THROTTLING_SEVERE; StatsLog.write(StatsLog.THERMAL_THROTTLING, temp.getType(), - isThrottling ? - StatsLog.THERMAL_THROTTLING_STATE_CHANGED__STATE__START : - StatsLog.THERMAL_THROTTLING_STATE_CHANGED__STATE__STOP, - temp.getValue()); + StatsLog.THERMAL_THROTTLING_STATE_CHANGED__STATE__UNKNOWN, + temp.getValue(), temp.getStatus(), temp.getName()); } } diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index b287a0b011e7..7eac07c47741 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -457,7 +457,7 @@ public class ActivityStartController { "pendingActivityLaunch"); try { starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, - resume, pal.r.pendingOptions, null, null /* outRecords */); + resume, pal.r.pendingOptions, null); } catch (Exception e) { Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e); pal.sendErrorResult(e.getMessage()); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 895744424ee1..4ef8753bd131 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -544,11 +544,17 @@ class ActivityStarter { */ int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, - int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, - ActivityRecord[] outActivity) { + int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) { try { - return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, - doResume, options, inTask, outActivity); + mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(r.intent); + mLastStartReason = "startResolvedActivity"; + mLastStartActivityTimeMs = System.currentTimeMillis(); + mLastStartActivityRecord[0] = r; + mLastStartActivityResult = startActivity(r, sourceRecord, voiceSession, voiceInteractor, + startFlags, doResume, options, inTask, mLastStartActivityRecord); + mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult, + mLastStartActivityRecord[0]); + return mLastStartActivityResult; } finally { onExecutionComplete(); } @@ -965,8 +971,9 @@ class ActivityStarter { : (realCallingUid == Process.SYSTEM_UID) || realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; if (realCallingUid != callingUid) { - // don't abort if the realCallingUid has a visible window - if (realCallingUidHasAnyVisibleWindow) { + // don't abort if the realCallingUid has a visible window, unless realCallingUid is + // SYSTEM_UID, in which case it start needs to be explicitly whitelisted + if (realCallingUidHasAnyVisibleWindow && realCallingUid != Process.SYSTEM_UID) { return false; } // if the realCallingUid is a persistent system process, abort if the IntentSender @@ -980,31 +987,6 @@ class ActivityStarter { return false; } } - // If we don't have callerApp at this point, no caller was provided to startActivity(). - // That's the case for PendingIntent-based starts, since the creator's process might not be - // up and alive. If that's the case, we retrieve the WindowProcessController for the send() - // caller, so that we can make the decision based on its foreground/whitelisted state. - if (callerApp == null) { - callerApp = mService.getProcessController(realCallingPid, realCallingUid); - } - if (callerApp != null) { - // don't abort if the callerApp is instrumenting with background activity starts privs - if (callerApp.isInstrumentingWithBackgroundActivityStartPrivileges()) { - return false; - } - // don't abort if the caller is currently temporarily whitelisted - if (callerApp.areBackgroundActivityStartsAllowed()) { - return false; - } - // don't abort if the caller has an activity in any foreground task - if (callerApp.hasActivityInVisibleTask()) { - return false; - } - // don't abort if the caller is bound by a UID that's currently foreground - if (isBoundByForegroundUid(callerApp)) { - return false; - } - } // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) { @@ -1029,6 +1011,33 @@ class ActivityStarter { + " temporarily whitelisted. This will not be supported in future Q builds."); return false; } + // If we don't have callerApp at this point, no caller was provided to startActivity(). + // That's the case for PendingIntent-based starts, since the creator's process might not be + // up and alive. If that's the case, we retrieve the WindowProcessController for the send() + // caller, so that we can make the decision based on its foreground/whitelisted state. + int callerAppUid = callingUid; + if (callerApp == null) { + callerApp = mService.getProcessController(realCallingPid, realCallingUid); + callerAppUid = realCallingUid; + } + // don't abort if the callerApp or other processes of that uid are whitelisted in any way + if (callerApp != null) { + // first check the original calling process + if (callerApp.areBackgroundActivityStartsAllowed()) { + return false; + } + // only if that one wasn't whitelisted, check the other ones + final ArraySet<WindowProcessController> uidProcesses = + mService.mProcessMap.getProcesses(callerAppUid); + if (uidProcesses != null) { + for (int i = uidProcesses.size() - 1; i >= 0; i--) { + final WindowProcessController proc = uidProcesses.valueAt(i); + if (proc != callerApp && proc.areBackgroundActivityStartsAllowed()) { + return false; + } + } + } + } // anything that has fallen through would currently be aborted Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage + "; callingUid: " + callingUid @@ -1053,17 +1062,6 @@ class ActivityStarter { return true; } - private boolean isBoundByForegroundUid(WindowProcessController callerApp) { - final ArraySet<Integer> boundClientUids = callerApp.getBoundClientUids(); - for (int i = boundClientUids.size() - 1; i >= 0; --i) { - final int uid = boundClientUids.valueAt(i); - if (mService.isUidForeground(uid)) { - return true; - } - } - return false; - } - // TODO: remove this toast after feature development is done void showBackgroundActivityBlockedToast(boolean abort, String callingPackage) { final Resources res = mService.mContext.getResources(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 76774d73cc03..3fa026873953 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -373,8 +373,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>(); /** All processes currently running that might have a window organized by name. */ final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>(); - /** All processes we currently have running mapped by pid */ - final SparseArray<WindowProcessController> mPidMap = new SparseArray<>(); + /** All processes we currently have running mapped by pid and uid */ + final WindowProcessControllerMap mProcessMap = new WindowProcessControllerMap(); /** This is the process holding what we currently consider to be the "home" activity. */ WindowProcessController mHomeProcess; /** The currently running heavy-weight process, if any. */ @@ -913,7 +913,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return getGlobalConfiguration(); } synchronized (mGlobalLock) { - final WindowProcessController app = mPidMap.get(pid); + final WindowProcessController app = mProcessMap.getProcess(pid); return app != null ? app.getConfiguration() : getGlobalConfiguration(); } } @@ -4640,7 +4640,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { enforceSystemHasVrFeature(); synchronized (mGlobalLock) { final int pid = Binder.getCallingPid(); - final WindowProcessController wpc = mPidMap.get(pid); + final WindowProcessController wpc = mProcessMap.getProcess(pid); mVrController.setVrThreadLocked(tid, pid, wpc); } } @@ -4659,7 +4659,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { enforceSystemHasVrFeature(); synchronized (mGlobalLock) { final int pid = Binder.getCallingPid(); - final WindowProcessController proc = mPidMap.get(pid); + final WindowProcessController proc = mProcessMap.getProcess(pid); mVrController.setPersistentVrThreadLocked(tid, pid, proc); } } @@ -5204,9 +5204,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mH.sendMessage(msg); } - for (int i = mPidMap.size() - 1; i >= 0; i--) { - final int pid = mPidMap.keyAt(i); - final WindowProcessController app = mPidMap.get(pid); + SparseArray<WindowProcessController> pidMap = mProcessMap.getPidMap(); + for (int i = pidMap.size() - 1; i >= 0; i--) { + final int pid = pidMap.keyAt(i); + final WindowProcessController app = pidMap.get(pid); if (DEBUG_CONFIGURATION) { Slog.v(TAG_CONFIGURATION, "Update process config of " + app.mName + " to new config " + configCopy); @@ -5859,7 +5860,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } WindowProcessController getProcessController(int pid, int uid) { - final WindowProcessController proc = mPidMap.get(pid); + final WindowProcessController proc = mProcessMap.getProcess(pid); if (proc == null) return null; if (UserHandle.isApp(uid) && proc.mUid == uid) { return proc; @@ -6423,14 +6424,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void onProcessMapped(int pid, WindowProcessController proc) { synchronized (mGlobalLock) { - mPidMap.put(pid, proc); + mProcessMap.put(pid, proc); } } @Override public void onProcessUnMapped(int pid) { synchronized (mGlobalLock) { - mPidMap.remove(pid); + mProcessMap.remove(pid); } } @@ -6503,7 +6504,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } return; } - final WindowProcessController process = mPidMap.get(pid); + final WindowProcessController process = mProcessMap.getProcess(pid); if (process == null) { if (DEBUG_CONFIGURATION) { Slog.w(TAG, "Trying to update display configuration for invalid " @@ -6696,7 +6697,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Only allow this from foreground processes, so that background // applications can't abuse it to prevent system UI from being shown. if (uid >= FIRST_APPLICATION_UID) { - final WindowProcessController proc = mPidMap.get(pid); + final WindowProcessController proc = mProcessMap.getProcess(pid); if (!proc.isPerceptible()) { Slog.w(TAG, "Ignoring closeSystemDialogs " + reason + " from background process " + proc); diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index c1d872f23f0f..b5e706710823 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -435,6 +435,18 @@ public class BoundsAnimationController { moveFromFullscreen, moveToFullscreen, animationType); } + /** + * Cancel existing animation if the destination was modified. + */ + void cancel(final BoundsAnimationTarget target) { + final BoundsAnimator existing = mRunningAnimations.get(target); + if (existing != null) { + // Cancel animation. Since its already started, send animation end to client. + if (DEBUG) Slog.d(TAG, "cancel: mTarget= " + target); + existing.cancelAndCallAnimationEnd(); + } + } + @VisibleForTesting BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to, int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java index c8f8e82bdb18..104805fba308 100644 --- a/services/core/java/com/android/server/wm/CompatModePackages.java +++ b/services/core/java/com/android/server/wm/CompatModePackages.java @@ -48,6 +48,7 @@ import android.os.Message; import android.os.RemoteException; import android.util.AtomicFile; import android.util.Slog; +import android.util.SparseArray; import android.util.Xml; public final class CompatModePackages { @@ -324,8 +325,9 @@ public final class CompatModePackages { ActivityRecord starting = stack.restartPackage(packageName); // Tell all processes that loaded this package about the change. - for (int i = mService.mPidMap.size() - 1; i >= 0; i--) { - final WindowProcessController app = mService.mPidMap.valueAt(i); + SparseArray<WindowProcessController> pidMap = mService.mProcessMap.getPidMap(); + for (int i = pidMap.size() - 1; i >= 0; i--) { + final WindowProcessController app = pidMap.valueAt(i); if (!app.mPkgList.contains(packageName)) { continue; } diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index 4bd8cab05700..a7a793fa8d34 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -41,7 +41,7 @@ class Dimmer { private static final int DEFAULT_DIM_ANIM_DURATION = 200; private class DimAnimatable implements SurfaceAnimator.Animatable { - private final SurfaceControl mDimLayer; + private SurfaceControl mDimLayer; private DimAnimatable(SurfaceControl dimLayer) { mDimLayer = dimLayer; @@ -100,6 +100,11 @@ class Dimmer { // See getSurfaceWidth() above for explanation. return mHost.getSurfaceHeight(); } + + void removeSurface() { + getPendingTransaction().remove(mDimLayer); + mDimLayer = null; + } } @VisibleForTesting @@ -129,8 +134,7 @@ class Dimmer { final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer); mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, () -> { if (!mDimming) { - dimAnimatable.getPendingTransaction().remove(mDimLayer); - mDimLayer = null; + dimAnimatable.removeSurface(); } }, mHost.mWmService); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 533705032b58..bd874ba786ed 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1726,14 +1726,16 @@ public class DisplayPolicy { // In case we forced the window to draw behind the navigation bar, restrict df/of to // DF.RestrictedOverscan to simulate old compat behavior. - final Rect parentDisplayFrame = attached.getDisplayFrameLw(); - final Rect parentOverscan = attached.getOverscanFrameLw(); + Rect parentDisplayFrame = attached.getDisplayFrameLw(); + Rect parentOverscan = attached.getOverscanFrameLw(); final WindowManager.LayoutParams attachedAttrs = attached.mAttrs; if ((attachedAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0 && (attachedAttrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 && (attachedAttrs.systemUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0) { + parentOverscan = new Rect(parentOverscan); parentOverscan.intersect(displayFrames.mRestrictedOverscan); + parentDisplayFrame = new Rect(parentDisplayFrame); parentDisplayFrame.intersect(displayFrames.mRestrictedOverscan); } diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java index 3d20501222b6..d774dc3fd2f1 100644 --- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java @@ -90,8 +90,6 @@ public class ImmersiveModeConfirmation { mShowDelayMs = getNavBarExitDuration() * 3; mPanicThresholdMs = context.getResources() .getInteger(R.integer.config_immersive_mode_confirmation_panic); - mWindowManager = (WindowManager) - mContext.getSystemService(Context.WINDOW_SERVICE); mVrModeEnabled = vrModeEnabled; } @@ -177,7 +175,7 @@ public class ImmersiveModeConfirmation { private void handleHide() { if (mClingWindow != null) { if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation"); - mWindowManager.removeView(mClingWindow); + getWindowManager().removeView(mClingWindow); mClingWindow = null; } } @@ -275,7 +273,7 @@ public class ImmersiveModeConfirmation { super.onAttachedToWindow(); DisplayMetrics metrics = new DisplayMetrics(); - mWindowManager.getDefaultDisplay().getMetrics(metrics); + getWindowManager().getDefaultDisplay().getMetrics(metrics); float density = metrics.density; getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener); @@ -341,6 +339,19 @@ public class ImmersiveModeConfirmation { } } + /** + * DO HOLD THE WINDOW MANAGER LOCK WHEN CALLING THIS METHOD + * The reason why we add this method is to avoid the deadlock of WMG->WMS and WMS->WMG + * when ImmersiveModeConfirmation object is created. + */ + private WindowManager getWindowManager() { + if (mWindowManager == null) { + mWindowManager = (WindowManager) + mContext.getSystemService(Context.WINDOW_SERVICE); + } + return mWindowManager; + } + private void handleShow() { if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation"); @@ -352,7 +363,7 @@ public class ImmersiveModeConfirmation { // show the confirmation WindowManager.LayoutParams lp = getClingWindowLayoutParams(); - mWindowManager.addView(mClingWindow, lp); + getWindowManager().addView(mClingWindow, lp); } private final Runnable mConfirm = new Runnable() { diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index d6c7b21e16b2..7dcbedf811c2 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -60,7 +60,6 @@ import android.os.Bundle; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; -import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.text.TextUtils; @@ -185,7 +184,6 @@ class RecentTasks { // front. Newly created tasks, or tasks that are removed from the list will continue to change // the list. This does not affect affiliated tasks. private boolean mFreezeTaskListReordering; - private long mFreezeTaskListReorderingTime; private long mFreezeTaskListTimeoutMs = FREEZE_TASK_LIST_TIMEOUT_MS; // Mainly to avoid object recreation on multiple calls. @@ -220,6 +218,9 @@ class RecentTasks { } }; + private final Runnable mResetFreezeTaskListOnTimeoutRunnable = + this::resetFreezeTaskListReorderingOnTimeout; + @VisibleForTesting RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) { mService = service; @@ -255,8 +256,7 @@ class RecentTasks { } @VisibleForTesting - void setFreezeTaskListTimeoutParams(long reorderingTime, long timeoutMs) { - mFreezeTaskListReorderingTime = reorderingTime; + void setFreezeTaskListTimeout(long timeoutMs) { mFreezeTaskListTimeoutMs = timeoutMs; } @@ -272,7 +272,8 @@ class RecentTasks { // Always update the reordering time when this is called to ensure that the timeout // is reset mFreezeTaskListReordering = true; - mFreezeTaskListReorderingTime = SystemClock.elapsedRealtime(); + mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable); + mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, mFreezeTaskListTimeoutMs); } /** @@ -286,6 +287,7 @@ class RecentTasks { // Once we end freezing the task list, reset the existing task order to the stable state mFreezeTaskListReordering = false; + mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable); // If the top task is provided, then restore the top task to the front of the list if (topTask != null) { @@ -295,6 +297,8 @@ class RecentTasks { // Resume trimming tasks trimInactiveRecentTasks(); + + mService.getTaskChangeNotificationController().notifyTaskStackChanged(); } /** @@ -302,13 +306,8 @@ class RecentTasks { * before we need to iterate the task list in order (either for purposes of returning the list * to SystemUI or if we need to trim tasks in order) */ + @VisibleForTesting void resetFreezeTaskListReorderingOnTimeout() { - // Unfreeze the recent task list if the time heuristic has passed - if (mFreezeTaskListReorderingTime - > (SystemClock.elapsedRealtime() - mFreezeTaskListTimeoutMs)) { - return; - } - final ActivityStack focusedStack = mService.getTopDisplayFocusedStack(); final TaskRecord topTask = focusedStack != null ? focusedStack.topTask() @@ -875,9 +874,6 @@ class RecentTasks { final Set<Integer> includedUsers = getProfileIds(userId); includedUsers.add(Integer.valueOf(userId)); - // Check if the frozen task list has timed out - resetFreezeTaskListReorderingOnTimeout(); - final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>(); final int size = mTasks.size(); int numVisibleTasks = 0; @@ -1654,8 +1650,8 @@ class RecentTasks { pw.println("mRecentsUid=" + mRecentsUid); pw.println("mRecentsComponent=" + mRecentsComponent); pw.println("mFreezeTaskListReordering=" + mFreezeTaskListReordering); - pw.println("mFreezeTaskListReorderingTime (time since)=" - + (SystemClock.elapsedRealtime() - mFreezeTaskListReorderingTime) + "ms"); + pw.println("mFreezeTaskListReorderingPendingTimeout=" + + mService.mH.hasCallbacks(mResetFreezeTaskListOnTimeoutRunnable)); if (mTasks.isEmpty()) { return; } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index f9fd54178ca7..9f4232400221 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -22,6 +22,7 @@ import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -312,11 +313,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> /** * Returns true if the callingUid has any non-toast window currently visible to the user. + * Also ignores TYPE_APPLICATION_STARTING, since those windows don't belong to apps. */ boolean isAnyNonToastWindowVisibleForUid(int callingUid) { return forAllWindows(w -> w.getOwningUid() == callingUid && w.mAttrs.type != TYPE_TOAST - && w.isVisibleNow(), + && w.mAttrs.type != TYPE_APPLICATION_STARTING && w.isVisibleNow(), true /* traverseTopToBottom */); } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 33d952e80a6b..b3b41b733cdb 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -286,8 +286,12 @@ class SurfaceAnimator { final boolean destroy = mLeash != null && surface != null && parent != null; if (destroy) { if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent"); - t.reparent(surface, parent); - scheduleAnim = true; + // We shouldn't really need these isValid checks but we do + // b/130364451 + if (surface.isValid() && parent.isValid()) { + t.reparent(surface, parent); + scheduleAnim = true; + } } mService.mAnimationTransferMap.remove(mAnimation); if (mLeash != null && destroyLeash) { diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index bdb4d0474865..7515b3fa1249 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -388,7 +388,9 @@ public class TaskStack extends WindowContainer<Task> implements * @return true if bounds were updated to some non-empty value. */ boolean calculatePinnedBoundsForConfigChange(Rect inOutBounds) { + boolean animating = false; if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) { + animating = true; getFinalAnimationBounds(mTmpRect2); } else { mTmpRect2.set(inOutBounds); @@ -398,6 +400,13 @@ public class TaskStack extends WindowContainer<Task> implements if (updated) { inOutBounds.set(mTmpRect3); + // The final boundary is updated while there is an existing boundary animation. Let's + // cancel this animation to prevent the obsolete animation overwritten updated bounds. + if (animating && !inOutBounds.equals(mBoundsAnimationTarget)) { + final DisplayContent displayContent = getDisplayContent(); + displayContent.mBoundsAnimationController.getHandler().post(() -> + displayContent.mBoundsAnimationController.cancel(this)); + } // Once we've set the bounds based on the rotation of the old bounds in the new // orientation, clear the animation target bounds since they are obsolete, and // cancel any currently running animations @@ -1585,7 +1594,6 @@ public class TaskStack extends WindowContainer<Task> implements mBoundsAnimatingRequested = false; mBoundsAnimating = true; - mCancelCurrentBoundsAnimation = false; mAnimationType = animationType; // If we are changing UI mode, as in the PiP to fullscreen @@ -1645,7 +1653,7 @@ public class TaskStack extends WindowContainer<Task> implements mBoundsAnimationTarget, false /* forceUpdate */); } - if (finalStackSize != null) { + if (finalStackSize != null && !mCancelCurrentBoundsAnimation) { setPinnedStackSize(finalStackSize, null); } else { // We have been canceled, so the final stack size is null, still run the @@ -1758,6 +1766,7 @@ public class TaskStack extends WindowContainer<Task> implements } final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType; + mCancelCurrentBoundsAnimation = false; displayContent.mBoundsAnimationController.getHandler().post(() -> { displayContent.mBoundsAnimationController.animateBounds(this, fromBounds, finalToBounds, animationDuration, finalSchedulePipModeChangedState, diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 4ca35f7d427b..eb919eb00f0c 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -372,16 +372,37 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mAllowBackgroundActivityStarts = allowBackgroundActivityStarts; } - public boolean areBackgroundActivityStartsAllowed() { - return mAllowBackgroundActivityStarts; + boolean areBackgroundActivityStartsAllowed() { + // allow if the whitelisting flag was explicitly set + if (mAllowBackgroundActivityStarts) { + return true; + } + // allow if the proc is instrumenting with background activity starts privs + if (mInstrumentingWithBackgroundActivityStartPrivileges) { + return true; + } + // allow if the caller has an activity in any foreground task + if (hasActivityInVisibleTask()) { + return true; + } + // allow if the caller is bound by a UID that's currently foreground + if (isBoundByForegroundUid()) { + return true; + } + return false; } - public void setBoundClientUids(ArraySet<Integer> boundClientUids) { - mBoundClientUids = boundClientUids; + private boolean isBoundByForegroundUid() { + for (int i = mBoundClientUids.size() - 1; i >= 0; --i) { + if (mAtm.isUidForeground(mBoundClientUids.valueAt(i))) { + return true; + } + } + return false; } - public ArraySet<Integer> getBoundClientUids() { - return mBoundClientUids; + public void setBoundClientUids(ArraySet<Integer> boundClientUids) { + mBoundClientUids = boundClientUids; } public void setInstrumenting(boolean instrumenting, @@ -394,14 +415,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mInstrumenting; } - /** - * @return true if the instrumentation was started by a holder of - * START_ACTIVITIES_FROM_BACKGROUND permission - */ - boolean isInstrumentingWithBackgroundActivityStartPrivileges() { - return mInstrumentingWithBackgroundActivityStartPrivileges; - } - public void setPerceptible(boolean perceptible) { mPerceptible = perceptible; } @@ -487,7 +500,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } } - boolean hasActivityInVisibleTask() { + private boolean hasActivityInVisibleTask() { for (int i = mActivities.size() - 1; i >= 0; --i) { TaskRecord task = mActivities.get(i).getTaskRecord(); if (task == null) { diff --git a/services/core/java/com/android/server/wm/WindowProcessControllerMap.java b/services/core/java/com/android/server/wm/WindowProcessControllerMap.java new file mode 100644 index 000000000000..2767972f7ea0 --- /dev/null +++ b/services/core/java/com/android/server/wm/WindowProcessControllerMap.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.util.ArraySet; +import android.util.SparseArray; + +import java.util.Map; +import java.util.HashMap; + +final class WindowProcessControllerMap { + + /** All processes we currently have running mapped by pid */ + private final SparseArray<WindowProcessController> mPidMap = new SparseArray<>(); + /** All processes we currently have running mapped by uid */ + private final Map<Integer, ArraySet<WindowProcessController>> mUidMap = new HashMap<>(); + + /** Retrieves a currently running process for pid. */ + WindowProcessController getProcess(int pid) { + return mPidMap.get(pid); + } + + /** Retrieves all currently running processes for uid. */ + ArraySet<WindowProcessController> getProcesses(int uid) { + return mUidMap.get(uid); + } + + SparseArray<WindowProcessController> getPidMap() { + return mPidMap; + } + + void put(int pid, WindowProcessController proc) { + // if there is a process for this pid already in mPidMap it'll get replaced automagically, + // but we actually need to remove it from mUidMap too before adding the new one + final WindowProcessController prevProc = mPidMap.get(pid); + if (prevProc != null) { + removeProcessFromUidMap(prevProc); + } + // put process into mPidMap + mPidMap.put(pid, proc); + // put process into mUidMap + final int uid = proc.mUid; + ArraySet<WindowProcessController> procSet = mUidMap.getOrDefault(uid, + new ArraySet<WindowProcessController>()); + procSet.add(proc); + mUidMap.put(uid, procSet); + } + + void remove(int pid) { + final WindowProcessController proc = mPidMap.get(pid); + if (proc != null) { + // remove process from mPidMap + mPidMap.remove(pid); + // remove process from mUidMap + removeProcessFromUidMap(proc); + } + } + + private void removeProcessFromUidMap(WindowProcessController proc) { + if (proc == null) { + return; + } + final int uid = proc.mUid; + ArraySet<WindowProcessController> procSet = mUidMap.get(uid); + if (procSet != null) { + procSet.remove(proc); + if (procSet.isEmpty()) { + mUidMap.remove(uid); + } + } + } +} diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index d990e6c6c24b..8e18683d3e76 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1305,7 +1305,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP void onDisplayChanged(DisplayContent dc) { super.onDisplayChanged(dc); // Window was not laid out for this display yet, so make sure mLayoutSeq does not match. - if (dc != null) { + if (dc != null && mInputWindowHandle.displayId != dc.getDisplayId()) { mLayoutSeq = dc.mLayoutSeq - 1; mInputWindowHandle.displayId = dc.getDisplayId(); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0cd730b68c72..2a2de7796424 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -32,7 +32,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.database.sqlite.SQLiteCompatibilityWalFlags; @@ -1248,11 +1247,6 @@ public final class SystemServer { mSystemServiceManager.startService(CONTENT_SUGGESTIONS_SERVICE_CLASS); traceEnd(); - // NOTE: ClipboardService indirectly depends on IntelligenceService - traceBeginAndSlog("StartClipboardService"); - mSystemServiceManager.startService(ClipboardService.class); - traceEnd(); - traceBeginAndSlog("InitNetworkStackClient"); try { NetworkStackClient.getInstance().init(); @@ -1887,6 +1881,11 @@ public final class SystemServer { traceEnd(); } + // NOTE: ClipboardService depends on ContentCapture and Autofill + traceBeginAndSlog("StartClipboardService"); + mSystemServiceManager.startService(ClipboardService.class); + traceEnd(); + traceBeginAndSlog("AppServiceManager"); mSystemServiceManager.startService(AppBindingService.Lifecycle.class); traceEnd(); diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java index 339607bbc73d..59aea21f46c8 100644 --- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java +++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java @@ -36,6 +36,7 @@ import android.system.StructTimeval; import android.util.Log; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.TrafficStatsConstants; import libcore.io.IoBridge; @@ -586,7 +587,8 @@ public class RouterAdvertisementDaemon { private boolean createSocket() { final int SEND_TIMEOUT_MS = 300; - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NEIGHBOR); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR); try { mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); // Setting SNDTIMEO is purely for defensive purposes. diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java index c8d1eb413ecf..74fe81c6f68e 100644 --- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java @@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; @@ -38,6 +39,7 @@ import static org.testng.Assert.expectThrows; import android.app.backup.BackupManager; import android.app.backup.IBackupObserver; import android.app.backup.ISelectBackupTransportCallback; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; @@ -48,6 +50,7 @@ import android.os.Binder; import android.os.HandlerThread; import android.os.PowerManager; import android.os.PowerSaveState; +import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.provider.Settings; @@ -66,6 +69,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @@ -1130,6 +1134,31 @@ public class UserBackupManagerServiceTest { /* transportManager */ null)); } + /** + * Test verifying that creating a new instance registers the broadcast receiver for package + * tracking + */ + @Test + public void testCreateAndInitializeService_registersPackageTrackingReceiver() throws Exception { + Context contextSpy = Mockito.spy(mContext); + + UserBackupManagerService service = UserBackupManagerService.createAndInitializeService( + USER_ID, + contextSpy, + new Trampoline(mContext), + mBackupThread, + mBaseStateDir, + mDataDir, + mTransportManager); + + BroadcastReceiver packageTrackingReceiver = service.getPackageTrackingReceiver(); + assertThat(packageTrackingReceiver).isNotNull(); + + // One call for package changes and one call for sd card events. + verify(contextSpy, times(2)).registerReceiverAsUser( + eq(packageTrackingReceiver), eq(UserHandle.of(USER_ID)), any(), any(), any()); + } + private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() { return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks( USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager); diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index 164570a84cb0..cc64323e51c7 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -86,6 +86,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.ConditionVariable; import android.os.DeadObjectException; @@ -100,6 +101,7 @@ import android.util.Pair; import com.android.internal.backup.IBackupTransport; import com.android.server.EventLogTags; +import com.android.server.LocalServices; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.DataChangedJournal; import com.android.server.backup.KeyValueBackupJob; @@ -116,7 +118,6 @@ import com.android.server.backup.testing.TransportTestUtils; import com.android.server.backup.testing.TransportTestUtils.TransportMock; import com.android.server.testing.shadows.FrameworkShadowLooper; import com.android.server.testing.shadows.ShadowApplicationPackageManager; -import com.android.server.testing.shadows.ShadowBackupActivityThread; import com.android.server.testing.shadows.ShadowBackupDataInput; import com.android.server.testing.shadows.ShadowBackupDataOutput; import com.android.server.testing.shadows.ShadowEventLog; @@ -163,8 +164,7 @@ import java.util.stream.Stream; ShadowBackupDataInput.class, ShadowBackupDataOutput.class, ShadowEventLog.class, - ShadowQueuedWork.class, - ShadowBackupActivityThread.class, + ShadowQueuedWork.class }) @Presubmit public class KeyValueBackupTaskTest { @@ -179,6 +179,7 @@ public class KeyValueBackupTaskTest { @Mock private IBackupObserver mObserver; @Mock private IBackupManagerMonitor mMonitor; @Mock private OnTaskFinishedListener mListener; + @Mock private PackageManagerInternal mPackageManagerInternal; private UserBackupManagerService mBackupManagerService; private TransportData mTransport; private ShadowLooper mShadowBackupLooper; @@ -243,6 +244,11 @@ public class KeyValueBackupTaskTest { mShadowBackupLooper = shadowOf(mBackupHandler.getLooper()); ShadowEventLog.setUp(); mReporter = spy(new KeyValueBackupReporter(mBackupManagerService, mObserver, mMonitor)); + + when(mPackageManagerInternal.getApplicationEnabledState(any(), anyInt())) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); } @After @@ -471,7 +477,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1); runTask(task); @@ -484,7 +490,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1, PM_PACKAGE); @@ -498,7 +504,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, false, PACKAGE_1); runTask(task); @@ -1307,7 +1313,7 @@ public class KeyValueBackupTaskTest { argThat(packageInfo(PM_PACKAGE)), any(), anyInt())) .then(copyBackupDataTo(backupDataPath)); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); agentOnBackupDo( pmAgent, (oldState, dataOutput, newState) -> { @@ -1371,7 +1377,7 @@ public class KeyValueBackupTaskTest { setUpAgent(PACKAGE_1); when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); agentOnBackupDo( pmAgent, (oldState, dataOutput, newState) -> { @@ -1395,7 +1401,7 @@ public class KeyValueBackupTaskTest { setUpAgent(PACKAGE_1); when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); agentOnBackupDo( pmAgent, (oldState, dataOutput, newState) -> { @@ -1957,7 +1963,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent); + doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); runTask(task); @@ -1970,7 +1976,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent); + doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); runTask(task); @@ -1983,7 +1989,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent); + doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); runTask(task); @@ -1996,7 +2002,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); agentOnBackupDo( pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel)); @@ -2011,7 +2017,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); agentOnBackupDo( pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel)); diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java deleted file mode 100644 index ca2e3b6dafef..000000000000 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.testing.shadows; - -import android.app.ActivityThread; -import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.os.RemoteException; - -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.shadows.ShadowActivityThread; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -import javax.annotation.Nonnull; - -/** - * Extends the existing {@link ShadowActivityThread} to add support for - * {@link PackageManager#getApplicationEnabledSetting(String)} in the shadow {@link PackageManager} - * returned by {@link ShadowBackupActivityThread#getPackageManager()}. - */ -@Implements(value = ActivityThread.class, isInAndroidSdk = false, looseSignatures = true) -public class ShadowBackupActivityThread extends ShadowActivityThread { - @Implementation - public static Object getPackageManager() { - ClassLoader classLoader = ShadowActivityThread.class.getClassLoader(); - Class<?> iPackageManagerClass; - try { - iPackageManagerClass = classLoader.loadClass("android.content.pm.IPackageManager"); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - - return Proxy.newProxyInstance( - classLoader, - new Class[] {iPackageManagerClass}, - new InvocationHandler() { - @Override - public Object invoke(Object proxy, @Nonnull Method method, Object[] args) - throws Exception { - if (method.getName().equals("getApplicationInfo")) { - String packageName = (String) args[0]; - int flags = (Integer) args[1]; - - try { - return RuntimeEnvironment.application - .getPackageManager() - .getApplicationInfo(packageName, flags); - } catch (PackageManager.NameNotFoundException e) { - throw new RemoteException(e.getMessage()); - } - } else if (method.getName().equals("getApplicationEnabledSetting")) { - return 0; - } else { - return null; - } - } - }); - } -} diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index c9aa63153a5d..32d7d026ff10 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -17,6 +17,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.mockingservicestests"> + <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <application android:testOnly="true" diff --git a/services/tests/servicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java index fc74c972ed83..73b3b8b1ce3c 100644 --- a/services/tests/servicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java @@ -16,87 +16,119 @@ package com.android.server.display.color; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_DISPLAY_COLOR; import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_SATURATION; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; + import android.hardware.display.ColorDisplayManager; import android.os.SystemProperties; import androidx.test.runner.AndroidJUnit4; +import com.android.dx.mockito.inline.extended.ExtendedMockito; + +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Answer; + +import java.util.HashMap; @RunWith(AndroidJUnit4.class) public class DisplayTransformManagerTest { + private MockitoSession mSession; private DisplayTransformManager mDtm; private float[] mNightDisplayMatrix; + private HashMap<String, String> mSystemProperties; @Before public void setUp() { mDtm = new DisplayTransformManager(); mNightDisplayMatrix = mDtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY); - SystemProperties.set(PERSISTENT_PROPERTY_DISPLAY_COLOR, null); - SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, null); + mSession = ExtendedMockito.mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .spyStatic(SystemProperties.class) + .startMocking(); + mSystemProperties = new HashMap<>(); + + doAnswer((Answer<Void>) invocationOnMock -> { + mSystemProperties.put(invocationOnMock.getArgument(0), + invocationOnMock.getArgument(1)); + return null; + } + ).when(() -> SystemProperties.set(anyString(), any())); + } + + @After + public void tearDown() throws Exception { + mSession.finishMocking(); + mSystemProperties.clear(); } @Test public void setColorMode_natural() { mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("0" /* managed */); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) .isEqualTo("1.0" /* natural */); } @Test public void setColorMode_boosted() { mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED, mNightDisplayMatrix); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("0" /* managed */); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) .isEqualTo("1.1" /* boosted */); } @Test public void setColorMode_saturated() { mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED, mNightDisplayMatrix); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("1" /* unmanaged */); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) .isEqualTo("1.0" /* natural */); } @Test public void setColorMode_automatic() { mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC, mNightDisplayMatrix); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("2" /* enhanced */); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) .isEqualTo("1.0" /* natural */); } @Test public void setColorMode_vendor() { mDtm.setColorMode(0x100, mNightDisplayMatrix); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo(Integer.toString(0x100) /* pass-through */); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) - .isEqualTo("1.0" /* default */); + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) + .isEqualTo("1.0" /* natural */); } @Test public void setColorMode_outOfBounds() { mDtm.setColorMode(0x50, mNightDisplayMatrix); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) - .isEqualTo("" /* default */); - assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) - .isEqualTo("" /* default */); + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) + .isEqualTo(null); + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) + .isEqualTo(null); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java index 2cba9d022866..2977414fb302 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java @@ -17,6 +17,7 @@ package com.android.server.accessibility; import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_HOVER_MOVE; import static android.view.MotionEvent.ACTION_UP; import static android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER; @@ -116,6 +117,7 @@ public class MotionEventInjectorTest { MotionEvent mClickDownEvent; MotionEvent mClickUpEvent; + MotionEvent mHoverMoveEvent; ArgumentCaptor<MotionEvent> mCaptor1 = ArgumentCaptor.forClass(MotionEvent.class); ArgumentCaptor<MotionEvent> mCaptor2 = ArgumentCaptor.forClass(MotionEvent.class); @@ -152,6 +154,10 @@ public class MotionEventInjectorTest { CLICK_POINT.y, 0); mClickUpEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN); + mHoverMoveEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, CLICK_POINT.x, CLICK_POINT.y, + 0); + mHoverMoveEvent.setSource(InputDevice.SOURCE_MOUSE); + mIsLineStart = allOf(IS_ACTION_DOWN, isAtPoint(LINE_START), hasStandardInitialization(), hasTimeFromDown(0)); mIsLineMiddle = allOf(IS_ACTION_MOVE, isAtPoint(LINE_END), hasStandardInitialization(), @@ -301,6 +307,23 @@ public class MotionEventInjectorTest { } @Test + public void + testOnMotionEvents_fromMouseWithInjectedGestureInProgress_shouldNotCancelAndPassReal() + throws RemoteException { + EventStreamTransformation next = attachMockNext(mMotionEventInjector); + injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); + mMessageCapturingHandler.sendOneMessage(); // Send a motion event + mMotionEventInjector.onMotionEvent(mHoverMoveEvent, mHoverMoveEvent, 0); + mMessageCapturingHandler.sendAllMessages(); + + verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); + assertThat(mCaptor1.getAllValues().get(0), mIsLineStart); + assertThat(mCaptor1.getAllValues().get(1), mIsLineMiddle); + assertThat(mCaptor1.getAllValues().get(2), mIsLineEnd); + verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, true); + } + + @Test public void testOnMotionEvents_closedInjectedGestureInProgress_shouldOnlyNotifyAndPassReal() throws RemoteException { EventStreamTransformation next = attachMockNext(mMotionEventInjector); diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java new file mode 100644 index 000000000000..8426a0bb2c11 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.attention; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.attention.AttentionManagerInternal.AttentionCallbackInternal; +import android.content.ComponentName; +import android.content.Context; +import android.os.IBinder; +import android.os.IPowerManager; +import android.os.PowerManager; +import android.os.RemoteException; +import android.service.attention.IAttentionCallback; +import android.service.attention.IAttentionService; + +import androidx.test.filters.SmallTest; + +import com.android.server.attention.AttentionManagerService.AttentionCheck; +import com.android.server.attention.AttentionManagerService.AttentionHandler; +import com.android.server.attention.AttentionManagerService.UserState; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link com.android.server.attention.AttentionManagerService} + */ +@SmallTest +public class AttentionManagerServiceTest { + private AttentionManagerService mSpyAttentionManager; + private UserState mSpyUserState; + private final int mTimeout = 1000; + @Mock private AttentionCallbackInternal mMockAttentionCallbackInternal; + @Mock private AttentionHandler mMockHandler; + @Mock private IAttentionCallback mMockIAttentionCallback; + @Mock private IPowerManager mMockIPowerManager; + @Mock Context mContext; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + // setup context mock + doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any()); + // setup power manager mock + PowerManager mPowerManager; + doReturn(true).when(mMockIPowerManager).isInteractive(); + mPowerManager = new PowerManager(mContext, mMockIPowerManager, null); + + Object mLock = new Object(); + // setup a spy on attention manager + AttentionManagerService mAttentionManager = new AttentionManagerService( + mContext, + mPowerManager, + mLock, + mMockHandler); + mSpyAttentionManager = Mockito.spy(mAttentionManager); + // setup a spy on user state + ComponentName componentName = new ComponentName("a", "b"); + mSpyAttentionManager.mComponentName = componentName; + UserState mUserState = new UserState(0, + mContext, + mLock, + componentName); + mUserState.mService = new MockIAttentionService(); + mSpyUserState = spy(mUserState); + } + + @Test + public void testCancelAttentionCheck_noCrashWhenNoUserStateLocked() { + mSpyAttentionManager.cancelAttentionCheck(null); + } + + @Test + public void testCancelAttentionCheck_noCrashWhenCallbackMismatched() { + mSpyUserState.mCurrentAttentionCheck = + new AttentionCheck(mMockAttentionCallbackInternal, mMockIAttentionCallback); + doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked(); + mSpyAttentionManager.cancelAttentionCheck(null); + } + + @Test + public void testCancelAttentionCheck_cancelCallbackWhenMatched() { + mSpyUserState.mCurrentAttentionCheck = + new AttentionCheck(mMockAttentionCallbackInternal, mMockIAttentionCallback); + doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked(); + mSpyAttentionManager.cancelAttentionCheck(mMockAttentionCallbackInternal); + verify(mSpyAttentionManager).cancel(any()); + } + + @Test + public void testCheckAttention_returnFalseWhenPowerManagerNotInteract() throws RemoteException { + doReturn(false).when(mMockIPowerManager).isInteractive(); + AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class); + assertThat(mSpyAttentionManager.checkAttention(mTimeout, callback)).isFalse(); + } + + @Test + public void testCheckAttention_callOnSuccess() throws RemoteException { + doReturn(true).when(mSpyAttentionManager).isServiceEnabled(); + doReturn(true).when(mMockIPowerManager).isInteractive(); + doReturn(mSpyUserState).when(mSpyAttentionManager).getOrCreateCurrentUserStateLocked(); + doNothing().when(mSpyAttentionManager).freeIfInactiveLocked(); + + AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class); + mSpyAttentionManager.checkAttention(mTimeout, callback); + verify(callback).onSuccess(anyInt(), anyLong()); + } + + private class MockIAttentionService implements IAttentionService { + public void checkAttention(IAttentionCallback callback) throws RemoteException { + callback.onSuccess(0, 0); + } + public void cancelAttentionCheck(IAttentionCallback callback) { + } + public IBinder asBinder() { + return null; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java deleted file mode 100644 index 97a6e6657b7a..000000000000 --- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java +++ /dev/null @@ -1,1217 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.testutils; - -import android.content.ComponentName; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.IntentSender; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.ChangedPackages; -import android.content.pm.IDexModuleRegisterCallback; -import android.content.pm.IOnPermissionsChangeListener; -import android.content.pm.IPackageDataObserver; -import android.content.pm.IPackageDeleteObserver; -import android.content.pm.IPackageDeleteObserver2; -import android.content.pm.IPackageInstaller; -import android.content.pm.IPackageManager; -import android.content.pm.IPackageMoveObserver; -import android.content.pm.IPackageStatsObserver; -import android.content.pm.InstrumentationInfo; -import android.content.pm.KeySet; -import android.content.pm.ModuleInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ParceledListSlice; -import android.content.pm.PermissionGroupInfo; -import android.content.pm.PermissionInfo; -import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.pm.SuspendDialogInfo; -import android.content.pm.VerifierDeviceIdentity; -import android.content.pm.VersionedPackage; -import android.content.pm.dex.IArtManager; -import android.graphics.Bitmap; -import android.os.IBinder; -import android.os.PersistableBundle; -import android.os.RemoteException; - -import java.util.List; - -/** - * Stub for IPackageManager to use in tests. - */ -public class IPackageManagerStub implements IPackageManager { - public static PackageInfo sPackageInfo; - public static int sApplicationEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; - - @Override - public PackageInfo getPackageInfo(String packageName, int flags, int userId) - throws RemoteException { - return sPackageInfo; - } - - @Override - public int getApplicationEnabledSetting(String packageName, int userId) throws RemoteException { - return sApplicationEnabledSetting; - } - - @Override - public void checkPackageStartable(String packageName, int userId) throws RemoteException { - - } - - @Override - public boolean isPackageAvailable(String packageName, int userId) throws RemoteException { - return false; - } - - @Override - public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public int getPackageUid(String packageName, int flags, int userId) throws RemoteException { - return 0; - } - - @Override - public int[] getPackageGids(String packageName, int flags, int userId) throws RemoteException { - return new int[0]; - } - - @Override - public String[] currentToCanonicalPackageNames(String[] names) throws RemoteException { - return new String[0]; - } - - @Override - public String[] canonicalToCurrentPackageNames(String[] names) throws RemoteException { - return new String[0]; - } - - @Override - public PermissionInfo getPermissionInfo(String name, String packageName, int flags) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryPermissionsByGroup(String group, int flags) - throws RemoteException { - return null; - } - - @Override - public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getAllPermissionGroups(int flags) throws RemoteException { - return null; - } - - @Override - public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ActivityInfo getActivityInfo(ComponentName className, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public boolean activitySupportsIntent(ComponentName className, Intent intent, - String resolvedType) - throws RemoteException { - return false; - } - - @Override - public ActivityInfo getReceiverInfo(ComponentName className, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ServiceInfo getServiceInfo(ComponentName className, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ProviderInfo getProviderInfo(ComponentName className, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public int checkPermission(String permName, String pkgName, int userId) throws RemoteException { - return 0; - } - - @Override - public int checkUidPermission(String permName, int uid) throws RemoteException { - return 0; - } - - @Override - public boolean addPermission(PermissionInfo info) throws RemoteException { - return false; - } - - @Override - public void removePermission(String name) throws RemoteException { - - } - - @Override - public void grantRuntimePermission(String packageName, String permissionName, int userId) - throws RemoteException { - - } - - @Override - public void revokeRuntimePermission(String packageName, String permissionName, int userId) - throws RemoteException { - - } - - @Override - public void resetRuntimePermissions() throws RemoteException { - - } - - @Override - public int getPermissionFlags(String permissionName, String packageName, int userId) - throws RemoteException { - return 0; - } - - @Override - public void updatePermissionFlags(String permissionName, String packageName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) - throws RemoteException { - - } - - @Override - public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) - throws RemoteException { - - } - - @Override - public List<String> getWhitelistedRestrictedPermissions(String packageName, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public boolean addWhitelistedRestrictedPermission(String packageName, String permission, - int whitelistFlags, int userId) throws RemoteException { - return false; - } - - @Override - public boolean removeWhitelistedRestrictedPermission(String packageName, String permission, - int whitelistFlags, int userId) throws RemoteException { - return false; - } - - @Override - public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, - int userId) throws RemoteException { - return false; - } - - @Override - public boolean isProtectedBroadcast(String actionName) throws RemoteException { - return false; - } - - @Override - public int checkSignatures(String pkg1, String pkg2) throws RemoteException { - return 0; - } - - @Override - public int checkUidSignatures(int uid1, int uid2) throws RemoteException { - return 0; - } - - @Override - public List<String> getAllPackages() throws RemoteException { - return null; - } - - @Override - public String[] getPackagesForUid(int uid) throws RemoteException { - return new String[0]; - } - - @Override - public String getNameForUid(int uid) throws RemoteException { - return null; - } - - @Override - public String[] getNamesForUids(int[] uids) throws RemoteException { - return new String[0]; - } - - @Override - public int getUidForSharedUser(String sharedUserName) throws RemoteException { - return 0; - } - - @Override - public int getFlagsForUid(int uid) throws RemoteException { - return 0; - } - - @Override - public int getPrivateFlagsForUid(int uid) throws RemoteException { - return 0; - } - - @Override - public boolean isUidPrivileged(int uid) throws RemoteException { - return false; - } - - @Override - public String[] getAppOpPermissionPackages(String permissionName) throws RemoteException { - return new String[0]; - } - - @Override - public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) - throws RemoteException { - return null; - } - - @Override - public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId, - int targetUserId) throws RemoteException { - return false; - } - - @Override - public ParceledListSlice queryIntentActivities(Intent intent, String resolvedType, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics, - String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryIntentReceivers(Intent intent, String resolvedType, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType, - int flags, int userId) throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getInstalledPackages(int flags, int userId) throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getPackagesHoldingPermissions(String[] permissions, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getInstalledApplications(int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getPersistentApplications(int flags) throws RemoteException { - return null; - } - - @Override - public ProviderInfo resolveContentProvider(String name, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) - throws RemoteException { - - } - - @Override - public ParceledListSlice queryContentProviders(String processName, int uid, int flags, - String metaDataKey) throws RemoteException { - return null; - } - - @Override - public InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryInstrumentation(String targetPackage, int flags) - throws RemoteException { - return null; - } - - @Override - public void finishPackageInstall(int token, boolean didLaunch) throws RemoteException { - - } - - @Override - public void setInstallerPackageName(String targetPackage, String installerPackageName) - throws RemoteException { - - } - - @Override - public void setApplicationCategoryHint(String packageName, int categoryHint, - String callerPackageName) throws RemoteException { - - } - - @Override - public void deletePackageAsUser(String packageName, int versionCode, - IPackageDeleteObserver observer, int userId, int flags) throws RemoteException { - - } - - @Override - public void deletePackageVersioned(VersionedPackage versionedPackage, - IPackageDeleteObserver2 observer, int userId, int flags) throws RemoteException { - - } - - @Override - public String getInstallerPackageName(String packageName) throws RemoteException { - return null; - } - - @Override - public void resetApplicationPreferences(int userId) throws RemoteException { - - } - - @Override - public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) - throws RemoteException { - return null; - } - - @Override - public void setLastChosenActivity(Intent intent, String resolvedType, int flags, - IntentFilter filter, int match, ComponentName activity) throws RemoteException { - - } - - @Override - public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, - ComponentName activity, int userId) throws RemoteException { - - } - - @Override - public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, - ComponentName activity, int userId) throws RemoteException { - - } - - @Override - public void clearPackagePreferredActivities(String packageName) throws RemoteException { - - } - - @Override - public int getPreferredActivities(List<IntentFilter> outFilters, - List<ComponentName> outActivities, String packageName) throws RemoteException { - return 0; - } - - @Override - public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity, - int userId) throws RemoteException { - - } - - @Override - public void clearPackagePersistentPreferredActivities(String packageName, int userId) - throws RemoteException { - - } - - @Override - public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage, - int sourceUserId, int targetUserId, int flags) throws RemoteException { - - } - - @Override - public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage) - throws RemoteException { - - } - - @Override - public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames, - int restrictionFlags, int userId) throws RemoteException { - return new String[0]; - } - - @Override - public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, - PersistableBundle appExtras, PersistableBundle launcherExtras, SuspendDialogInfo dialogInfo, - String callingPackage, int userId) throws RemoteException { - return new String[0]; - } - - @Override - public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) - throws RemoteException { - return new String[0]; - } - - @Override - public boolean isPackageSuspendedForUser(String packageName, int userId) - throws RemoteException { - return false; - } - - @Override - public PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId) - throws RemoteException { - return null; - } - - @Override - public byte[] getPreferredActivityBackup(int userId) throws RemoteException { - return new byte[0]; - } - - @Override - public void restorePreferredActivities(byte[] backup, int userId) throws RemoteException { - - } - - @Override - public byte[] getDefaultAppsBackup(int userId) throws RemoteException { - return new byte[0]; - } - - @Override - public void restoreDefaultApps(byte[] backup, int userId) throws RemoteException { - - } - - @Override - public byte[] getIntentFilterVerificationBackup(int userId) throws RemoteException { - return new byte[0]; - } - - @Override - public void restoreIntentFilterVerification(byte[] backup, int userId) throws RemoteException { - - } - - @Override - public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates) - throws RemoteException { - return null; - } - - @Override - public void setHomeActivity(ComponentName className, int userId) throws RemoteException { - - } - - @Override - public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags, - int userId) throws RemoteException { - - } - - @Override - public int getComponentEnabledSetting(ComponentName componentName, int userId) - throws RemoteException { - return 0; - } - - @Override - public void setApplicationEnabledSetting(String packageName, int newState, int flags, - int userId, - String callingPackage) throws RemoteException { - - } - - @Override - public void logAppProcessStartIfNeeded(String processName, int uid, String seinfo, - String apkFile, - int pid) throws RemoteException { - - } - - @Override - public void flushPackageRestrictionsAsUser(int userId) throws RemoteException { - - } - - @Override - public void setPackageStoppedState(String packageName, boolean stopped, int userId) - throws RemoteException { - - } - - @Override - public void freeStorageAndNotify(String volumeUuid, long freeStorageSize, int storageFlags, - IPackageDataObserver observer) throws RemoteException { - - } - - @Override - public void freeStorage(String volumeUuid, long freeStorageSize, int storageFlags, - IntentSender pi) throws RemoteException { - - } - - @Override - public void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) - throws RemoteException { - - } - - @Override - public void deleteApplicationCacheFilesAsUser(String packageName, int userId, - IPackageDataObserver observer) throws RemoteException { - - } - - @Override - public void clearApplicationUserData(String packageName, IPackageDataObserver observer, - int userId) throws RemoteException { - - } - - @Override - public void clearApplicationProfileData(String packageName) throws RemoteException { - - } - - @Override - public void getPackageSizeInfo(String packageName, int userHandle, - IPackageStatsObserver observer) - throws RemoteException { - - } - - @Override - public String[] getSystemSharedLibraryNames() throws RemoteException { - return new String[0]; - } - - @Override - public ParceledListSlice getSystemAvailableFeatures() throws RemoteException { - return null; - } - - @Override - public boolean hasSystemFeature(String name, int version) throws RemoteException { - return false; - } - - @Override - public void enterSafeMode() throws RemoteException { - - } - - @Override - public boolean isSafeMode() throws RemoteException { - return false; - } - - @Override - public void systemReady() throws RemoteException { - - } - - @Override - public boolean hasSystemUidErrors() throws RemoteException { - return false; - } - - @Override - public void performFstrimIfNeeded() throws RemoteException { - - } - - @Override - public void updatePackagesIfNeeded() throws RemoteException { - - } - - @Override - public void notifyPackageUse(String packageName, int reason) throws RemoteException { - - } - - @Override - public void notifyDexLoad(String loadingPackageName, List<String> classLoadersNames, - List<String> classPaths, String loaderIsa) throws RemoteException { - - } - - @Override - public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, - IDexModuleRegisterCallback callback) throws RemoteException { - - } - - @Override - public boolean performDexOptMode(String packageName, boolean checkProfiles, - String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) - throws RemoteException { - return false; - } - - @Override - public boolean performDexOptSecondary(String packageName, String targetCompilerFilter, - boolean force) throws RemoteException { - return false; - } - - @Override - public boolean compileLayouts(String packageName) throws RemoteException { - return false; - } - - @Override - public void dumpProfiles(String packageName) throws RemoteException { - - } - - @Override - public void forceDexOpt(String packageName) throws RemoteException { - - } - - @Override - public boolean runBackgroundDexoptJob(List<String> packageNames) throws RemoteException { - return false; - } - - @Override - public void reconcileSecondaryDexFiles(String packageName) throws RemoteException { - - } - - @Override - public int getMoveStatus(int moveId) throws RemoteException { - return 0; - } - - @Override - public void registerMoveCallback(IPackageMoveObserver callback) throws RemoteException { - - } - - @Override - public void unregisterMoveCallback(IPackageMoveObserver callback) throws RemoteException { - - } - - @Override - public int movePackage(String packageName, String volumeUuid) throws RemoteException { - return 0; - } - - @Override - public int movePrimaryStorage(String volumeUuid) throws RemoteException { - return 0; - } - - @Override - public boolean addPermissionAsync(PermissionInfo info) throws RemoteException { - return false; - } - - @Override - public boolean setInstallLocation(int loc) throws RemoteException { - return false; - } - - @Override - public int getInstallLocation() throws RemoteException { - return 0; - } - - @Override - public int installExistingPackageAsUser(String packageName, int userId, int installFlags, - int installReason) throws RemoteException { - return 0; - } - - @Override - public void verifyPendingInstall(int id, int verificationCode) throws RemoteException { - - } - - @Override - public void extendVerificationTimeout(int id, int verificationCodeAtTimeout, - long millisecondsToDelay) throws RemoteException { - - } - - @Override - public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) - throws RemoteException { - - } - - @Override - public int getIntentVerificationStatus(String packageName, int userId) throws RemoteException { - return 0; - } - - @Override - public boolean updateIntentVerificationStatus(String packageName, int status, int userId) - throws RemoteException { - return false; - } - - @Override - public ParceledListSlice getIntentFilterVerifications(String packageName) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getAllIntentFilters(String packageName) throws RemoteException { - return null; - } - - @Override - public boolean setDefaultBrowserPackageName(String packageName, int userId) - throws RemoteException { - return false; - } - - @Override - public String getDefaultBrowserPackageName(int userId) throws RemoteException { - return null; - } - - @Override - public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException { - return null; - } - - @Override - public boolean isFirstBoot() throws RemoteException { - return false; - } - - @Override - public boolean isOnlyCoreApps() throws RemoteException { - return false; - } - - @Override - public boolean isDeviceUpgrading() throws RemoteException { - return false; - } - - @Override - public void setPermissionEnforced(String permission, boolean enforced) throws RemoteException { - - } - - @Override - public boolean isPermissionEnforced(String permission) throws RemoteException { - return false; - } - - @Override - public boolean isStorageLow() throws RemoteException { - return false; - } - - @Override - public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId) - throws RemoteException { - return false; - } - - @Override - public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) - throws RemoteException { - return false; - } - - @Override - public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) - throws RemoteException { - - } - - @Override - public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) - throws RemoteException { - return false; - } - - @Override - public IPackageInstaller getPackageInstaller() throws RemoteException { - return null; - } - - @Override - public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId) - throws RemoteException { - return false; - } - - @Override - public boolean getBlockUninstallForUser(String packageName, int userId) throws RemoteException { - return false; - } - - @Override - public KeySet getKeySetByAlias(String packageName, String alias) throws RemoteException { - return null; - } - - @Override - public KeySet getSigningKeySet(String packageName) throws RemoteException { - return null; - } - - @Override - public boolean isPackageSignedByKeySet(String packageName, KeySet ks) throws RemoteException { - return false; - } - - @Override - public boolean isPackageSignedByKeySetExactly(String packageName, KeySet ks) - throws RemoteException { - return false; - } - - @Override - public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) - throws RemoteException { - - } - - @Override - public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) - throws RemoteException { - - } - - @Override - public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) - throws RemoteException { - - } - - @Override - public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) - throws RemoteException { - - } - - @Override - public void grantDefaultPermissionsToEnabledTelephonyDataServices(String[] packageNames, - int userId) throws RemoteException { - - } - - @Override - public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(String[] packageNames, - int userId) throws RemoteException { - - } - - @Override - public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) - throws RemoteException { - - } - - @Override - public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) - throws RemoteException { - - } - - @Override - public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId) - throws RemoteException { - return false; - } - - @Override - public String getPermissionControllerPackageName() throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getInstantApps(int userId) throws RemoteException { - return null; - } - - @Override - public byte[] getInstantAppCookie(String packageName, int userId) throws RemoteException { - return new byte[0]; - } - - @Override - public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) - throws RemoteException { - return false; - } - - @Override - public Bitmap getInstantAppIcon(String packageName, int userId) throws RemoteException { - return null; - } - - @Override - public boolean isInstantApp(String packageName, int userId) throws RemoteException { - return false; - } - - @Override - public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) - throws RemoteException { - return false; - } - - @Override - public void setUpdateAvailable(String packageName, boolean updateAvaialble) - throws RemoteException { - - } - - @Override - public String getServicesSystemSharedLibraryPackageName() throws RemoteException { - return null; - } - - @Override - public String getSharedSystemSharedLibraryPackageName() throws RemoteException { - return null; - } - - @Override - public ChangedPackages getChangedPackages(int sequenceNumber, int userId) - throws RemoteException { - return null; - } - - @Override - public boolean isPackageDeviceAdminOnAnyUser(String packageName) throws RemoteException { - return false; - } - - @Override - public int getInstallReason(String packageName, int userId) throws RemoteException { - return 0; - } - - @Override - public ParceledListSlice getSharedLibraries(String packageName, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getDeclaredSharedLibraries(String packageName, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public boolean canRequestPackageInstalls(String packageName, int userId) - throws RemoteException { - return false; - } - - @Override - public void deletePreloadsFileCache() throws RemoteException { - - } - - @Override - public ComponentName getInstantAppResolverComponent() throws RemoteException { - return null; - } - - @Override - public ComponentName getInstantAppResolverSettingsComponent() throws RemoteException { - return null; - } - - @Override - public ComponentName getInstantAppInstallerComponent() throws RemoteException { - return null; - } - - @Override - public String getInstantAppAndroidId(String packageName, int userId) throws RemoteException { - return null; - } - - @Override - public IArtManager getArtManager() throws RemoteException { - return null; - } - - @Override - public void setHarmfulAppWarning(String packageName, CharSequence warning, int userId) - throws RemoteException { - - } - - @Override - public CharSequence getHarmfulAppWarning(String packageName, int userId) - throws RemoteException { - return null; - } - - @Override - public boolean hasSigningCertificate(String packageName, byte[] signingCertificate, int flags) - throws RemoteException { - return false; - } - - @Override - public boolean hasUidSigningCertificate(int uid, byte[] signingCertificate, int flags) - throws RemoteException { - return false; - } - - @Override - public String getSystemTextClassifierPackageName() throws RemoteException { - return null; - } - - @Override - public String getWellbeingPackageName() throws RemoteException { - return null; - } - - @Override - public String getSystemCaptionsServicePackageName() throws RemoteException { - return null; - } - - @Override - public String getAttentionServicePackageName() throws RemoteException { - return null; - } - - public String getIncidentReportApproverPackageName() throws RemoteException { - return null; - } - - @Override - public String getAppPredictionServicePackageName() { - return null; - } - - @Override - public boolean isPackageStateProtected(String packageName, int userId) throws RemoteException { - return false; - } - - @Override - public void sendDeviceCustomizationReadyBroadcast() throws RemoteException { - - } - - @Override - public List<ModuleInfo> getInstalledModules(int flags) throws RemoteException { - return null; - } - - @Override - public ModuleInfo getModuleInfo(String packageName, int flags) throws RemoteException { - return null; - } - - @Override - public int getRuntimePermissionsVersion(int userId) throws RemoteException { - return 0; - } - - @Override - public void setRuntimePermissionsVersion(int version, int userId) throws RemoteException { - - } - - @Override - public IBinder asBinder() { - return null; - } -} diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java index a92b576e5d0f..a9011756240d 100644 --- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -36,7 +37,6 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.backup.UserBackupManagerService; -import com.android.server.backup.testutils.IPackageManagerStub; import org.junit.Before; import org.junit.Test; @@ -54,14 +54,12 @@ public class AppBackupUtilsTest { private static final Signature SIGNATURE_3 = generateSignature((byte) 3); private static final Signature SIGNATURE_4 = generateSignature((byte) 4); - private IPackageManagerStub mPackageManagerStub; private PackageManagerInternal mMockPackageManagerInternal; private int mUserId; @Before public void setUp() throws Exception { - mPackageManagerStub = new IPackageManagerStub(); mMockPackageManagerInternal = mock(PackageManagerInternal.class); mUserId = UserHandle.USER_SYSTEM; @@ -76,7 +74,7 @@ public class AppBackupUtilsTest { applicationInfo.packageName = TEST_PACKAGE_NAME; boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -91,7 +89,7 @@ public class AppBackupUtilsTest { applicationInfo.packageName = TEST_PACKAGE_NAME; boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -105,7 +103,7 @@ public class AppBackupUtilsTest { applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -118,12 +116,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.SYSTEM_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isTrue(); } @@ -136,12 +133,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = null; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isTrue(); } @@ -154,12 +150,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isTrue(); } @@ -172,12 +167,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.SYSTEM_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -190,12 +184,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = null; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -208,12 +201,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -226,12 +218,11 @@ public class AppBackupUtilsTest { applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; applicationInfo.enabled = true; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; - - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isFalse(); } @@ -244,12 +235,12 @@ public class AppBackupUtilsTest { applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; applicationInfo.enabled = false; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isTrue(); } @@ -261,12 +252,12 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isFalse(); } @@ -278,12 +269,12 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED; - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isTrue(); } @@ -295,12 +286,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; - - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isTrue(); } @@ -312,12 +302,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; - - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java index 5de41ea5e7d1..4de00f7b5565 100644 --- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java +++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.when; import android.attention.AttentionManagerInternal; import android.attention.AttentionManagerInternal.AttentionCallbackInternal; +import android.content.pm.PackageManager; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.SystemClock; @@ -49,6 +50,8 @@ import org.mockito.MockitoAnnotations; public class AttentionDetectorTest extends AndroidTestCase { @Mock + private PackageManager mPackageManager; + @Mock private AttentionManagerInternal mAttentionManagerInternal; @Mock private Runnable mOnUserAttention; @@ -60,6 +63,9 @@ public class AttentionDetectorTest extends AndroidTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mPackageManager.getAttentionServicePackageName()).thenReturn("com.google.android.as"); + when(mPackageManager.checkPermission(any(), any())).thenReturn( + PackageManager.PERMISSION_GRANTED); when(mAttentionManagerInternal.checkAttention(anyLong(), any())) .thenReturn(true); mAttentionDetector = new TestableAttentionDetector(); @@ -108,6 +114,27 @@ public class AttentionDetectorTest extends AndroidTestCase { } @Test + public void testOnUserActivity_doesntCheckIfNotSufficientPermissions() { + when(mPackageManager.checkPermission(any(), any())).thenReturn( + PackageManager.PERMISSION_DENIED); + + long when = registerAttention(); + verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any()); + assertThat(mNextDimming).isEqualTo(when); + } + + @Test + public void testOnUserActivity_disablesSettingIfNotSufficientPermissions() { + when(mPackageManager.checkPermission(any(), any())).thenReturn( + PackageManager.PERMISSION_DENIED); + + registerAttention(); + boolean enabled = Settings.System.getIntForUser(getContext().getContentResolver(), + Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1; + assertFalse(enabled); + } + + @Test public void testOnUserActivity_doesntCrashIfNoAttentionService() { mAttentionManagerInternal = null; registerAttention(); @@ -211,6 +238,8 @@ public class AttentionDetectorTest extends AndroidTestCase { TestableAttentionDetector() { super(AttentionDetectorTest.this.mOnUserAttention, new Object()); mAttentionManager = mAttentionManagerInternal; + mPackageManager = AttentionDetectorTest.this.mPackageManager; + mContentResolver = getContext().getContentResolver(); mMaximumExtensionMillis = 10000L; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 49ee8b3bba19..6be2c2e8c59e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -48,8 +48,8 @@ import android.app.Notification; import android.app.Notification.Builder; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.content.Context; import android.content.pm.PackageManager; -import android.content.res.Resources; import android.graphics.Color; import android.media.AudioAttributes; import android.media.AudioManager; @@ -63,7 +63,6 @@ import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; -import android.testing.TestableContext; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; @@ -71,7 +70,6 @@ import android.view.accessibility.IAccessibilityManagerClient; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.R; import com.android.internal.util.IntPair; import com.android.server.UiServiceTestCase; import com.android.server.lights.Light; @@ -88,7 +86,6 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class BuzzBeepBlinkTest extends UiServiceTestCase { - private TestableContext mContext = spy(getContext()); @Mock AudioManager mAudioManager; @Mock Vibrator mVibrator; @Mock android.media.IRingtonePlayer mRingtonePlayer; @@ -99,8 +96,6 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { NotificationUsageStats mUsageStats; @Mock IAccessibilityManager mAccessibilityService; - @Mock - Resources mResources; private NotificationManagerService mService; private String mPkg = "com.android.server.notification"; @@ -150,7 +145,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt()); assertTrue(accessibilityManager.isEnabled()); - mService = spy(new NotificationManagerService(mContext)); + mService = spy(new NotificationManagerService(getContext())); mService.setAudioManager(mAudioManager); mService.setVibrator(mVibrator); mService.setSystemReady(true); @@ -280,7 +275,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { boolean isLeanback) { NotificationChannel channel = new NotificationChannel("test", "test", IMPORTANCE_HIGH); - final Builder builder = new Builder(mContext) + final Builder builder = new Builder(getContext()) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon) .setPriority(Notification.PRIORITY_HIGH) @@ -326,14 +321,15 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { n.flags |= Notification.FLAG_INSISTENT; } - PackageManager packageManager = spy(mContext.getPackageManager()); - when(mContext.getPackageManager()).thenReturn(packageManager); + Context context = spy(getContext()); + PackageManager packageManager = spy(context.getPackageManager()); + when(context.getPackageManager()).thenReturn(packageManager); when(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) .thenReturn(isLeanback); StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid, n, mUser, null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(mContext, sbn, channel); + NotificationRecord r = new NotificationRecord(context, sbn, channel); mService.addNotification(r); return r; } @@ -459,25 +455,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { } @Test - public void testNoBeepForAutomotiveIfEffectsDisabled() throws Exception { - when(mContext.getResources()).thenReturn(mResources); - when(mResources.getBoolean(R.bool.config_enableServerNotificationEffectsForAutomotive)) - .thenReturn(false); - mService.setIsAutomotive(true); - - NotificationRecord r = getBeepyNotification(); - - mService.buzzBeepBlinkLocked(r); - - verifyNeverBeep(); - assertFalse(r.isInterruptive()); - } - - @Test - public void testNoBeepForImportanceDefaultInAutomotiveIfEffectsEnabled() throws Exception { - when(mContext.getResources()).thenReturn(mResources); - when(mResources.getBoolean(R.bool.config_enableServerNotificationEffectsForAutomotive)) - .thenReturn(true); + public void testNoBeepForImportanceDefaultInAutomotive() throws Exception { mService.setIsAutomotive(true); NotificationRecord r = getBeepyNotification(); @@ -490,10 +468,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { } @Test - public void testBeepForImportanceHighInAutomotiveIfEffectsEnabled() throws Exception { - when(mContext.getResources()).thenReturn(mResources); - when(mResources.getBoolean(R.bool.config_enableServerNotificationEffectsForAutomotive)) - .thenReturn(true); + public void testBeepForImportanceHighInAutomotive() throws Exception { mService.setIsAutomotive(true); NotificationRecord r = getBeepyNotification(); @@ -1040,12 +1015,12 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { public void testEmptyUriSoundTreatedAsNoSound() throws Exception { NotificationChannel channel = new NotificationChannel("test", "test", IMPORTANCE_HIGH); channel.setSound(Uri.EMPTY, null); - final Notification n = new Builder(mContext, "test") + final Notification n = new Builder(getContext(), "test") .setSmallIcon(android.R.drawable.sym_def_app_icon).build(); StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid, mPid, n, mUser, null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(mContext, sbn, channel); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); mService.addNotification(r); mService.buzzBeepBlinkLocked(r); @@ -1094,13 +1069,13 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { @Test public void testCrossUserSoundMuted() throws Exception { - final Notification n = new Builder(mContext, "test") + final Notification n = new Builder(getContext(), "test") .setSmallIcon(android.R.drawable.sym_def_app_icon).build(); int userId = mUser.getIdentifier() + 1; StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid, mPid, n, UserHandle.of(userId), null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(mContext, sbn, + NotificationRecord r = new NotificationRecord(getContext(), sbn, new NotificationChannel("test", "test", IMPORTANCE_HIGH)); mService.buzzBeepBlinkLocked(r); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 355ff63d18f7..2d101dd87a0f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -349,6 +349,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner); when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG}); when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG}); + mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); // write to a test file; the system file isn't readable from tests mFile = new File(mContext.getCacheDir(), "test.xml"); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index 91d3e5e4d7f6..7e3d4b47bbdf 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -206,6 +206,7 @@ public class RoleObserverTest extends UiServiceTestCase { LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.addService(WindowManagerInternal.class, mock(WindowManagerInternal.class)); + mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); mUsers = new ArrayList<>(); mUsers.add(new UserInfo(0, "system", 0)); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 08d83333e91b..89364500fd80 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -110,6 +110,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { private ZenModeHelper mZenModeHelperSpy; private Context mContext; private ContentResolver mContentResolver; + @Mock AppOpsManager mAppOps; @Before public void setUp() { @@ -127,6 +128,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { e.toString()); } + when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOps); when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager); mConditionProviders = new ConditionProviders(mContext, new UserProfiles(), AppGlobals.getPackageManager()); @@ -219,10 +221,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0); mZenModeHelperSpy.applyRestrictions(); - doNothing().when(mZenModeHelperSpy).applyRestrictions(anyBoolean(), anyInt()); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + doNothing().when(mZenModeHelperSpy).applyRestrictions(eq(false), anyBoolean(), anyInt()); + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_ALARM); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_MEDIA); } @@ -233,9 +235,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0); mZenModeHelperSpy.applyRestrictions(); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false, AudioAttributes.USAGE_ALARM); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false, AudioAttributes.USAGE_MEDIA); } @@ -244,13 +246,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0); mZenModeHelperSpy.applyRestrictions(); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true, AudioAttributes.USAGE_ALARM); // Media is a catch-all that includes games - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true, AudioAttributes.USAGE_MEDIA); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true, AudioAttributes.USAGE_GAME); } @@ -262,17 +264,17 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.applyRestrictions(); // Total silence will silence alarms, media and system noises (but not vibrations) - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_ALARM); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_MEDIA); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_GAME); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_VIBRATE); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_UNKNOWN); } @@ -283,19 +285,19 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.applyRestrictions(); // Alarms only mode will not silence alarms - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_ALARM); // Alarms only mode will not silence media - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_MEDIA); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_GAME); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_UNKNOWN); // Alarms only will silence system noises (but not vibrations) - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO); } @@ -306,9 +308,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.applyRestrictions(); // Alarms only mode will silence calls despite priority-mode config - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_NOTIFICATION_RINGTONE); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST); } @@ -319,7 +321,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0); mZenModeHelperSpy.applyRestrictions(); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_ALARM); } @@ -334,19 +336,64 @@ public class ZenModeHelperTest extends UiServiceTestCase { for (int usage : AudioAttributes.SDK_USAGES) { if (usage == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) { // only mute audio, not vibrations - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, usage, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true, usage, AppOpsManager.OP_PLAY_AUDIO); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, usage, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false, usage, AppOpsManager.OP_VIBRATE); } else { boolean shouldMute = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage) != AudioAttributes.SUPPRESSIBLE_NEVER; - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage); + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, shouldMute, usage); } } } @Test + public void testApplyRestrictions_whitelist_priorityOnlyMode() { + mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O}); + mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0); + mZenModeHelperSpy.applyRestrictions(); + + for (int usage : AudioAttributes.SDK_USAGES) { + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(new String[]{PKG_O})); + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(new String[]{PKG_O})); + } + } + + @Test + public void testApplyRestrictions_whitelist_alarmsOnlyMode() { + mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O}); + mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_ALARMS; + mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0); + mZenModeHelperSpy.applyRestrictions(); + + for (int usage : AudioAttributes.SDK_USAGES) { + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(null)); + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(null)); + } + } + + @Test + public void testApplyRestrictions_whitelist_totalSilenceMode() { + mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O}); + mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_NO_INTERRUPTIONS; + mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0); + mZenModeHelperSpy.applyRestrictions(); + + for (int usage : AudioAttributes.SDK_USAGES) { + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(null)); + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(null)); + } + } + + @Test public void testZenUpgradeNotification() { // shows zen upgrade notification if stored settings says to shows, // zen has not been updated, boot is completed diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java index 96db38b14ad5..a7bbe6e4cf02 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java @@ -87,7 +87,7 @@ public class ActivityStartControllerTests extends ActivityTestsBase { mController.doPendingActivityLaunches(resume); verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null), - eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null)); + eq(null), eq(startFlags), eq(resume), eq(null), eq(null)); } 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 c4009df27c1f..ca3f6846f74d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -52,6 +52,7 @@ import android.view.Display; import android.view.Surface; import android.view.WindowManager; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -383,6 +384,7 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 130392471) public void testAddRemoveRace() { // There was once a race condition between adding and removing starting windows for (int i = 0; i < 1000; i++) { 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 329af952c07a..bb574ceed4e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java @@ -48,6 +48,7 @@ import android.platform.test.annotations.Presubmit; import android.util.Log; import android.view.IWindowManager; +import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import com.android.server.am.AssistDataRequester; @@ -150,6 +151,7 @@ public class AssistDataRequesterTest extends ActivityTestsBase { } @Test + @FlakyTest(bugId = 130388718) public void testRequestData() throws Exception { setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, CALLER_ASSIST_SCREENSHOT_ALLOWED); @@ -250,6 +252,7 @@ public class AssistDataRequesterTest extends ActivityTestsBase { } @Test + @FlakyTest(bugId = 130388718) public void testNoFetchScreenshots_expectNoScreenshotCallbacks() throws Exception { setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, CALLER_ASSIST_SCREENSHOT_ALLOWED); @@ -260,6 +263,7 @@ public class AssistDataRequesterTest extends ActivityTestsBase { } @Test + @FlakyTest(bugId = 130388718) public void testDisallowAssistScreenshot_expectNullScreenshotCallback() throws Exception { setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED, !CALLER_ASSIST_SCREENSHOT_ALLOWED); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index af048586b425..3392bc43e568 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -712,7 +712,6 @@ public class RecentTasksTest extends ActivityTestsBase { mRecentTasks.add(mTasks.get(4)); // Freeze the list - long freezeTime = SystemClock.elapsedRealtime(); mRecentTasks.setFreezeTaskListReordering(); assertTrue(mRecentTasks.isFreezeTaskListReorderingSet()); @@ -720,13 +719,11 @@ public class RecentTasksTest extends ActivityTestsBase { mRecentTasks.add(mTasks.get(2)); mRecentTasks.add(mTasks.get(1)); - // Override the freeze timeout params to simulate the timeout (simulate the freeze at 100ms - // ago with a timeout of 1ms) - mRecentTasks.setFreezeTaskListTimeoutParams(freezeTime - 100, 1); - ActivityStack stack = mTasks.get(2).getStack(); stack.moveToFront("", mTasks.get(2)); doReturn(stack).when(mTestService.mRootActivityContainer).getTopDisplayFocusedStack(); + + // Simulate the reset from the timeout mRecentTasks.resetFreezeTaskListReorderingOnTimeout(); assertFalse(mRecentTasks.isFreezeTaskListReorderingSet()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 263f6501bbf6..f51ce131b7bc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -44,14 +45,18 @@ public class RootWindowContainerTests extends WindowTestsBase { private static final int FAKE_CALLING_UID = 667; @Test - public void testIsAnyNonToastWindowVisibleForUid_oneToastOneNonToastBothVisible() { + public void testIsAnyNonToastWindowVisibleForUid_oneToastOneAppStartOneNonToastBothVisible() { final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID); final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID); + final WindowState appStart = createWindow(null, TYPE_APPLICATION_STARTING, "appStarting", + FAKE_CALLING_UID); toastyToast.mHasSurface = true; app.mHasSurface = true; + appStart.mHasSurface = true; assertTrue(toastyToast.isVisibleNow()); assertTrue(app.isVisibleNow()); + assertTrue(appStart.isVisibleNow()); assertTrue(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID)); } @@ -65,6 +70,16 @@ public class RootWindowContainerTests extends WindowTestsBase { } @Test + public void testIsAnyNonToastWindowVisibleForUid_onlyAppStartingVisible() { + final WindowState appStart = createWindow(null, TYPE_APPLICATION_STARTING, "appStarting", + FAKE_CALLING_UID); + appStart.mHasSurface = true; + + assertTrue(appStart.isVisibleNow()); + assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID)); + } + + @Test public void testIsAnyNonToastWindowVisibleForUid_aFewNonToastButNoneVisible() { final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar", FAKE_CALLING_UID); final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID); 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 9722d2ccce99..62247d889485 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java @@ -39,6 +39,7 @@ 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; @@ -78,6 +79,7 @@ public class TaskStackChangedListenerTest { @Test @Presubmit + @FlakyTest(bugId = 130388819) public void testTaskStackChanged_afterFinish() throws Exception { registerTaskStackChangedListener(new TaskStackListener() { @Override @@ -159,6 +161,7 @@ public class TaskStackChangedListenerTest { */ @Test @Presubmit + @FlakyTest(bugId = 130388819) public void testTaskChangeCallBacks() throws Exception { final Object[] params = new Object[2]; final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1); 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 78fca0f2e6ef..06bcdf8fcadf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -36,6 +36,7 @@ import android.view.Gravity; import android.view.IWindow; import android.view.WindowManager; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.wm.utils.WmDisplayCutout; @@ -323,6 +324,7 @@ public class WindowFrameTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 130388666) public void testCalculatePolicyCrop() { final FrameTestWindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; @@ -423,6 +425,7 @@ public class WindowFrameTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 130388666) public void testDisplayCutout() { // Regular fullscreen task and window WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); @@ -446,6 +449,7 @@ public class WindowFrameTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 130388666) public void testDisplayCutout_tempDisplayedBounds() { // Regular fullscreen task and window WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerMapTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerMapTests.java new file mode 100644 index 000000000000..cb7bff3a4f15 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerMapTests.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link WindowProcessControllerMap} class. + * + * Build/Install/Run: + * atest WmTests:WindowProcessControllerMapTests + */ +@SmallTest +@Presubmit +public class WindowProcessControllerMapTests extends ActivityTestsBase { + + private static final int FAKE_UID1 = 666; + private static final int FAKE_UID2 = 667; + private static final int FAKE_PID1 = 668; + private static final int FAKE_PID2 = 669; + private static final int FAKE_PID3 = 670; + private static final int FAKE_PID4 = 671; + + private WindowProcessControllerMap mProcessMap; + private WindowProcessController pid1uid1; + private WindowProcessController pid1uid2; + private WindowProcessController pid2uid1; + private WindowProcessController pid3uid1; + private WindowProcessController pid4uid2; + + @Before + public void setUp() throws Exception { + mProcessMap = new WindowProcessControllerMap(); + pid1uid1 = new WindowProcessController( + mService, mService.mContext.getApplicationInfo(), "fakepid1fakeuid1", FAKE_UID1, + UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class)); + pid1uid1.setPid(FAKE_PID1); + pid1uid2 = new WindowProcessController( + mService, mService.mContext.getApplicationInfo(), "fakepid1fakeuid2", FAKE_UID2, + UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class)); + pid1uid2.setPid(FAKE_PID1); + pid2uid1 = new WindowProcessController( + mService, mService.mContext.getApplicationInfo(), "fakepid2fakeuid1", FAKE_UID1, + UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class)); + pid2uid1.setPid(FAKE_PID2); + pid3uid1 = new WindowProcessController( + mService, mService.mContext.getApplicationInfo(), "fakepid3fakeuid1", FAKE_UID1, + UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class)); + pid3uid1.setPid(FAKE_PID3); + pid4uid2 = new WindowProcessController( + mService, mService.mContext.getApplicationInfo(), "fakepid4fakeuid2", FAKE_UID2, + UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class)); + pid4uid2.setPid(FAKE_PID4); + } + + @Test + public void testAdditionsAndRemovals() { + // test various additions and removals + mProcessMap.put(FAKE_PID1, pid1uid1); + mProcessMap.put(FAKE_PID2, pid2uid1); + assertEquals(pid1uid1, mProcessMap.getProcess(FAKE_PID1)); + assertEquals(pid2uid1, mProcessMap.getProcess(FAKE_PID2)); + ArraySet<WindowProcessController> uid1processes = mProcessMap.getProcesses(FAKE_UID1); + assertTrue(uid1processes.contains(pid1uid1)); + assertTrue(uid1processes.contains(pid2uid1)); + assertEquals(uid1processes.size(), 2); + + mProcessMap.remove(FAKE_PID2); + mProcessMap.put(FAKE_PID3, pid3uid1); + uid1processes = mProcessMap.getProcesses(FAKE_UID1); + assertTrue(uid1processes.contains(pid1uid1)); + assertFalse(uid1processes.contains(pid2uid1)); + assertTrue(uid1processes.contains(pid3uid1)); + assertEquals(uid1processes.size(), 2); + + mProcessMap.put(FAKE_PID4, pid4uid2); + ArraySet<WindowProcessController> uid2processes = mProcessMap.getProcesses(FAKE_UID2); + assertTrue(uid2processes.contains(pid4uid2)); + assertEquals(uid2processes.size(), 1); + + mProcessMap.remove(FAKE_PID1); + mProcessMap.remove(FAKE_PID3); + assertNull(mProcessMap.getProcesses(FAKE_UID1)); + assertEquals(mProcessMap.getProcess(FAKE_PID4), pid4uid2); + } + + @Test + public void testReplacement() { + // test that replacing a process is handled correctly + mProcessMap.put(FAKE_PID1, pid1uid1); + ArraySet<WindowProcessController> uid1processes = mProcessMap.getProcesses(FAKE_UID1); + assertTrue(uid1processes.contains(pid1uid1)); + assertEquals(uid1processes.size(), 1); + + mProcessMap.put(FAKE_PID1, pid1uid2); + assertNull(mProcessMap.getProcesses(FAKE_UID1)); + ArraySet<WindowProcessController> uid2processes = mProcessMap.getProcesses(FAKE_UID2); + assertTrue(uid2processes.contains(pid1uid2)); + assertEquals(uid2processes.size(), 1); + assertEquals(mProcessMap.getProcess(FAKE_PID1), pid1uid2); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 4f8fe5b10f19..715353e5d980 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -397,7 +397,9 @@ public class WindowStateTests extends WindowTestsBase { app.mLayoutSeq = 1; mDisplayContent.mLayoutSeq = 1; - app.onDisplayChanged(mDisplayContent); + DisplayContent newDisplay = createNewDisplay(); + + app.onDisplayChanged(newDisplay); assertThat(app.mLayoutSeq, not(is(mDisplayContent.mLayoutSeq))); } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 79e6851039ba..5b7d81697c8c 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -1095,6 +1095,14 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser Slog.d(TAG, "Clear notification"); mUsbNotificationId = 0; } + // Not relevant for automotive. + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUTOMOTIVE) + && id == SystemMessage.NOTE_USB_CHARGING) { + mUsbNotificationId = 0; + return; + } + if (id != 0) { CharSequence title = r.getText(titleRes); PendingIntent pi; diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index 6382acf0511d..6000b56d191e 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -68,6 +68,7 @@ public abstract class Conference extends Conferenceable { public void onExtrasRemoved(Conference c, List<String> keys) {} public void onConferenceStateChanged(Conference c, boolean isConference) {} public void onAddressChanged(Conference c, Uri newAddress, int presentation) {} + public void onConnectionEvent(Conference c, String event, Bundle extras) {} public void onCallerDisplayNameChanged( Conference c, String callerDisplayName, int presentation) {} } @@ -1024,4 +1025,14 @@ public abstract class Conference extends Conferenceable { } onExtrasChanged(b); } + + /** + * See {@link Connection#sendConnectionEvent(String, Bundle)} + * @hide + */ + public void sendConnectionEvent(String event, Bundle extras) { + for (Listener l : mListeners) { + l.onConnectionEvent(this, event, extras); + } + } } diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 9bafbe09998e..49b34b38c663 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -1279,6 +1279,14 @@ public abstract class ConnectionService extends Service { mAdapter.setCallerDisplayName(id, callerDisplayName, presentation); } } + + @Override + public void onConnectionEvent(Conference c, String event, Bundle extras) { + String id = mIdByConference.get(c); + if (id != null) { + mAdapter.onConnectionEvent(id, event, extras); + } + } }; private final Connection.Listener mConnectionListener = new Connection.Listener() { diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index cbcd40f15583..ebfa3a15639a 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -41,26 +41,12 @@ import java.util.List; /** * This service is implemented by an app that wishes to provide functionality for managing * phone calls. - * <p> - * There are three types of apps which Telecom can bind to when there exists a live (active or - * incoming) call: - * <ol> - * <li>Default Dialer/Phone app - the default dialer/phone app is one which provides the - * in-call user interface while the device is in a call. A device is bundled with a system - * provided default dialer/phone app. The user may choose a single app to take over this role - * from the system app.</li> - * <li>Default Car-mode Dialer/Phone app - the default car-mode dialer/phone app is one which - * provides the in-call user interface while the device is in a call and the device is in car - * mode. The user may choose a single app to fill this role.</li> - * <li>Call Companion app - a call companion app is one which provides no user interface itself, - * but exposes call information to another display surface, such as a wearable device. The - * user may choose multiple apps to fill this role.</li> - * </ol> - * <p> - * Apps which wish to fulfill one of the above roles use the {@link android.app.role.RoleManager} - * to request that they fill the desired role. - * * <h2>Becoming the Default Phone App</h2> + * The default dialer/phone app is one which provides the in-call user interface while the device is + * in a call. A device is bundled with a system provided default dialer/phone app. The user may + * choose a single app to take over this role from the system app. An app which wishes to fulfill + * one this role uses the {@code android.app.role.RoleManager} to request that they fill the role. + * <p> * An app filling the role of the default phone app provides a user interface while the device is in * a call, and the device is not in car mode. * <p> @@ -193,47 +179,6 @@ import java.util.List; * notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build()); * }</pre> * <p> - * <h2>Becoming the Default Car-mode Phone App</h2> - * An app filling the role of the default car-mode dialer/phone app provides a user interface while - * the device is in a call, and in car mode. See - * {@link android.app.UiModeManager#ACTION_ENTER_CAR_MODE} for more information about car mode. - * When the device is in car mode, Telecom binds to the default car-mode dialer/phone app instead - * of the usual dialer/phone app. - * <p> - * Similar to the requirements for becoming the default dialer/phone app, your app must declare a - * manifest entry for its {@link InCallService} implementation. Your manifest entry should ensure - * the following conditions are met: - * <ul> - * <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} metadata.</li> - * <li>Set the {@link TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI} metadata to - * {@code true}<li> - * <li>Your app must request the permission - * {@link android.Manifest.permission.CALL_COMPANION_APP}.</li> - * </ul> - * <p> - * Your app should request to fill the role {@code android.app.role.CAR_MODE_DIALER} in order to - * become the default (see <a href="#requestRole">above</a> for how to request your app fills this - * role). - * - * <h2>Becoming a Call Companion App</h2> - * An app which fills the companion app role does not directly provide a user interface while the - * device is in a call. Instead, it is typically used to relay information about calls to another - * display surface, such as a wearable device. - * <p> - * Similar to the requirements for becoming the default dialer/phone app, your app must declare a - * manifest entry for its {@link InCallService} implementation. Your manifest entry should - * ensure the following conditions are met: - * <ul> - * <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} metadata.</li> - * <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI} - * metadata.</li> - * <li>Your app must request the permission - * {@link android.Manifest.permission.CALL_COMPANION_APP}.</li> - * </ul> - * <p> - * Your app should request to fill the role {@code android.app.role.CALL_COMPANION} in order to - * become a call companion app (see <a href="#requestRole">above</a> for how to request your app - * fills this role). */ public abstract class InCallService extends Service { diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java index 71a28b575394..eb568e04ebf3 100644 --- a/telecomm/java/android/telecom/PhoneAccountHandle.java +++ b/telecomm/java/android/telecom/PhoneAccountHandle.java @@ -17,6 +17,7 @@ package android.telecom; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.os.Build; @@ -174,4 +175,21 @@ public final class PhoneAccountHandle implements Parcelable { in.readString(), UserHandle.CREATOR.createFromParcel(in)); } + + /** + * Determines if two {@link PhoneAccountHandle}s are from the same package. + * + * @param a Phone account handle to check for same {@link ConnectionService} package. + * @param b Other phone account handle to check for same {@link ConnectionService} package. + * @return {@code true} if the two {@link PhoneAccountHandle}s passed in belong to the same + * {@link ConnectionService} / package, {@code false} otherwise. Note: {@code null} phone + * account handles are considered equivalent to other {@code null} phone account handles. + * @hide + */ + public static boolean areFromSamePackage(@Nullable PhoneAccountHandle a, + @Nullable PhoneAccountHandle b) { + String aPackageName = a != null ? a.getComponentName().getPackageName() : null; + String bPackageName = b != null ? b.getComponentName().getPackageName() : null; + return Objects.equals(aPackageName, bPackageName); + } } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 391d788cfe72..db6319871540 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -16,7 +16,6 @@ package android.telecom; import android.Manifest; import android.annotation.IntDef; -import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; @@ -497,6 +496,9 @@ public class TelecomManager { * Dialer implementations (see {@link #getDefaultDialerPackage()}) which would also like to * override the system provided ringing should set this meta-data to {@code true} in the * manifest registration of their {@link InCallService}. + * <p> + * When {@code true}, it is the {@link InCallService}'s responsibility to play a ringtone for + * all incoming calls. */ public static final String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING"; @@ -1495,8 +1497,21 @@ public class TelecomManager { /** * Silences the ringer if a ringing call exists. - * - * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * <p> + * This method can only be relied upon to stop the ringtone for a call if the ringtone has + * already started playing. It is intended to handle use-cases such as silencing a ringing call + * when the user presses the volume button during ringing. + * <p> + * If this method is called prior to when the ringtone begins playing, the ringtone will not be + * silenced. As such it is not intended as a means to avoid playing of a ringtone. + * <p> + * A dialer app which wants to have more control over ringtone playing should declare + * {@link TelecomManager#METADATA_IN_CALL_SERVICE_RINGING} in the manifest entry for their + * {@link InCallService} implementation to indicate that the app wants to be responsible for + * playing the ringtone for all incoming calls. + * <p> + * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the + * app fills the dialer role (see {@link #getDefaultDialerPackage()}). */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger() { diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index 2d8a8cbae59f..9bba2e8947ed 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -3039,6 +3039,7 @@ public final class Telephony { * The {@code content://} style URL for this table. Can be appended with a part ID to * address individual parts. */ + @NonNull public static final Uri CONTENT_URI = Uri.withAppendedPath(Mms.CONTENT_URI, "part"); /** @@ -3394,6 +3395,7 @@ public final class Telephony { * {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId for MSIM, * use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id. */ + @NonNull public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers"); /** @@ -3406,6 +3408,7 @@ public final class Telephony { * {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId for MSIM, * use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id. */ + @NonNull public static final Uri SIM_APN_URI = Uri.parse( "content://telephony/carriers/sim_apn_list"); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 9f6528bc4709..d2f88bbda654 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2804,7 +2804,7 @@ public class CarrierConfigManager { * @hide */ public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING = - "key_subscription_group_uuid_string"; + "subscription_group_uuid_string"; /** * A boolean property indicating whether this subscription should be managed as an opportunistic @@ -2819,7 +2819,7 @@ public class CarrierConfigManager { * @hide */ public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL = - "key_is_opportunistic_subscription_bool"; + "is_opportunistic_subscription_bool"; /** * A list of 4 GSM RSSI thresholds above which a signal level is considered POOR, diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java index 30875165867a..258a873c3ac5 100644 --- a/telephony/java/android/telephony/CellIdentity.java +++ b/telephony/java/android/telephony/CellIdentity.java @@ -243,4 +243,23 @@ public abstract class CellIdentity implements Parcelable { protected void log(String s) { Rlog.w(mTag, s); } + + /** @hide */ + protected static final int inRangeOrUnavailable(int value, int rangeMin, int rangeMax) { + if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE; + return value; + } + + /** @hide */ + protected static final long inRangeOrUnavailable(long value, long rangeMin, long rangeMax) { + if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE_LONG; + return value; + } + + /** @hide */ + protected static final int inRangeOrUnavailable( + int value, int rangeMin, int rangeMax, int special) { + if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE; + return value; + } } diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java index 4c00611b2355..880d3db681b5 100644 --- a/telephony/java/android/telephony/CellIdentityCdma.java +++ b/telephony/java/android/telephony/CellIdentityCdma.java @@ -28,12 +28,25 @@ public final class CellIdentityCdma extends CellIdentity { private static final String TAG = CellIdentityCdma.class.getSimpleName(); private static final boolean DBG = false; + private static final int NETWORK_ID_MAX = 65535; + private static final int SYSTEM_ID_MAX = 32767; + private static final int BASESTATION_ID_MAX = 65535; + + private static final int LONGITUDE_MIN = -2592000; + private static final int LONGITUDE_MAX = 2592000; + + private static final int LATITUDE_MIN = -1296000; + private static final int LATITUDE_MAX = 1296000; + // Network Id 0..65535 private final int mNetworkId; + // CDMA System Id 0..32767 private final int mSystemId; + // Base Station Id 0..65535 private final int mBasestationId; + /** * Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0. * It is represented in units of 0.25 seconds and ranges from -2592000 @@ -41,6 +54,7 @@ public final class CellIdentityCdma extends CellIdentity { * to +180 degrees). */ private final int mLongitude; + /** * Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0. * It is represented in units of 0.25 seconds and ranges from -1296000 @@ -78,9 +92,12 @@ public final class CellIdentityCdma extends CellIdentity { public CellIdentityCdma( int nid, int sid, int bid, int lon, int lat, String alphal, String alphas) { super(TAG, CellInfo.TYPE_CDMA, null, null, alphal, alphas); - mNetworkId = nid; - mSystemId = sid; - mBasestationId = bid; + mNetworkId = inRangeOrUnavailable(nid, 0, NETWORK_ID_MAX); + mSystemId = inRangeOrUnavailable(sid, 0, SYSTEM_ID_MAX); + mBasestationId = inRangeOrUnavailable(bid, 0, BASESTATION_ID_MAX); + lat = inRangeOrUnavailable(lat, LATITUDE_MIN, LATITUDE_MAX); + lon = inRangeOrUnavailable(lon, LONGITUDE_MIN, LONGITUDE_MAX); + if (!isNullIsland(lat, lon)) { mLongitude = lon; mLatitude = lat; diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java index 864540d91be3..25c6577bdcf5 100644 --- a/telephony/java/android/telephony/CellIdentityGsm.java +++ b/telephony/java/android/telephony/CellIdentityGsm.java @@ -31,6 +31,11 @@ public final class CellIdentityGsm extends CellIdentity { private static final String TAG = CellIdentityGsm.class.getSimpleName(); private static final boolean DBG = false; + private static final int MAX_LAC = 65535; + private static final int MAX_CID = 65535; + private static final int MAX_ARFCN = 65535; + private static final int MAX_BSIC = 63; + // 16-bit Location Area Code, 0..65535 private final int mLac; // 16-bit GSM Cell Identity described in TS 27.007, 0..65535 @@ -68,10 +73,10 @@ public final class CellIdentityGsm extends CellIdentity { public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr, String mncStr, String alphal, String alphas) { super(TAG, CellInfo.TYPE_GSM, mccStr, mncStr, alphal, alphas); - mLac = lac; - mCid = cid; - mArfcn = arfcn; - mBsic = bsic; + mLac = inRangeOrUnavailable(lac, 0, MAX_LAC); + mCid = inRangeOrUnavailable(cid, 0, MAX_CID); + mArfcn = inRangeOrUnavailable(arfcn, 0, MAX_ARFCN); + mBsic = inRangeOrUnavailable(bsic, 0, MAX_BSIC); } /** @hide */ diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index 14503c7cdd4d..997b19f3d4eb 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -32,6 +32,12 @@ public final class CellIdentityLte extends CellIdentity { private static final String TAG = CellIdentityLte.class.getSimpleName(); private static final boolean DBG = false; + private static final int MAX_CI = 268435455; + private static final int MAX_PCI = 503; + private static final int MAX_TAC = 65535; + private static final int MAX_EARFCN = 262143; + private static final int MAX_BANDWIDTH = 20000; + // 28-bit cell identity private final int mCi; // physical cell id 0..503 @@ -89,11 +95,11 @@ public final class CellIdentityLte extends CellIdentity { public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr, String mncStr, String alphal, String alphas) { super(TAG, CellInfo.TYPE_LTE, mccStr, mncStr, alphal, alphas); - mCi = ci; - mPci = pci; - mTac = tac; - mEarfcn = earfcn; - mBandwidth = bandwidth; + mCi = inRangeOrUnavailable(ci, 0, MAX_CI); + mPci = inRangeOrUnavailable(pci, 0, MAX_PCI); + mTac = inRangeOrUnavailable(tac, 0, MAX_TAC); + mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN); + mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH); } /** @hide */ diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index 62d23cee3bf5..6df60ba0ca32 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -29,6 +29,11 @@ import java.util.Objects; public final class CellIdentityNr extends CellIdentity { private static final String TAG = "CellIdentityNr"; + private static final int MAX_PCI = 1007; + private static final int MAX_TAC = 65535; + private static final int MAX_NRARFCN = 3279165; + private static final long MAX_NCI = 68719476735L; + private final int mNrArfcn; private final int mPci; private final int mTac; @@ -41,6 +46,7 @@ public final class CellIdentityNr extends CellIdentity { * @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165]. * @param mccStr 3-digit Mobile Country Code in string format. * @param mncStr 2 or 3-digit Mobile Network Code in string format. + * @param nci The 36-bit NR Cell Identity in range [0, 68719476735]. * @param alphal long alpha Operator Name String or Enhanced Operator Name String. * @param alphas short alpha Operator Name String or Enhanced Operator Name String. * @@ -49,10 +55,10 @@ public final class CellIdentityNr extends CellIdentity { public CellIdentityNr(int pci, int tac, int nrArfcn, String mccStr, String mncStr, long nci, String alphal, String alphas) { super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas); - mPci = pci; - mTac = tac; - mNrArfcn = nrArfcn; - mNci = nci; + mPci = inRangeOrUnavailable(pci, 0, MAX_PCI); + mTac = inRangeOrUnavailable(tac, 0, MAX_TAC); + mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN); + mNci = inRangeOrUnavailable(nci, 0, MAX_NCI); } /** @hide */ diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index 937de706aec0..558e346284ea 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -30,12 +30,18 @@ public final class CellIdentityTdscdma extends CellIdentity { private static final String TAG = CellIdentityTdscdma.class.getSimpleName(); private static final boolean DBG = false; + private static final int MAX_LAC = 65535; + private static final int MAX_CID = 268435455; + private static final int MAX_CPID = 127; + private static final int MAX_UARFCN = 65535; + // 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown. private final int mLac; // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.UNAVAILABLE // if unknown. private final int mCid; - // 8-bit Cell Parameters ID described in TS 25.331, 0..127, CellInfo.UNAVAILABLE if unknown. + // 8-bit Cell Parameters ID described in TS 25.331 sec 10.3.6.9, + // 0..127, CellInfo.UNAVAILABLE if unknown. private final int mCpid; // 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3 private final int mUarfcn; @@ -68,10 +74,10 @@ public final class CellIdentityTdscdma extends CellIdentity { public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid, int uarfcn, String alphal, String alphas) { super(TAG, CellInfo.TYPE_TDSCDMA, mcc, mnc, alphal, alphas); - mLac = lac; - mCid = cid; - mCpid = cpid; - mUarfcn = uarfcn; + mLac = inRangeOrUnavailable(lac, 0, MAX_LAC); + mCid = inRangeOrUnavailable(cid, 0, MAX_CID); + mCpid = inRangeOrUnavailable(cpid, 0, MAX_CPID); + mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN); } private CellIdentityTdscdma(CellIdentityTdscdma cid) { diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java index b4a2ead7fc3d..031fed13d9f1 100644 --- a/telephony/java/android/telephony/CellIdentityWcdma.java +++ b/telephony/java/android/telephony/CellIdentityWcdma.java @@ -31,6 +31,11 @@ public final class CellIdentityWcdma extends CellIdentity { private static final String TAG = CellIdentityWcdma.class.getSimpleName(); private static final boolean DBG = false; + private static final int MAX_LAC = 65535; + private static final int MAX_CID = 268435455; + private static final int MAX_PSC = 511; + private static final int MAX_UARFCN = 16383; // a 14 bit number; TS 25.331 ex sec 10.3.8.15 + // 16-bit Location Area Code, 0..65535 private final int mLac; // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455 @@ -68,10 +73,10 @@ public final class CellIdentityWcdma extends CellIdentity { public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn, String mccStr, String mncStr, String alphal, String alphas) { super(TAG, CellInfo.TYPE_WCDMA, mccStr, mncStr, alphal, alphas); - mLac = lac; - mCid = cid; - mPsc = psc; - mUarfcn = uarfcn; + mLac = inRangeOrUnavailable(lac, 0, MAX_LAC); + mCid = inRangeOrUnavailable(cid, 0, MAX_CID); + mPsc = inRangeOrUnavailable(psc, 0, MAX_PSC); + mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN); } /** @hide */ diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index cf15b92ae640..c57f9e63f9db 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -39,6 +39,7 @@ import android.util.DisplayMetrics; import android.util.Log; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -123,6 +124,16 @@ public class SubscriptionInfo implements Parcelable { private String mMnc; /** + * EHPLMNs associated with the subscription + */ + private String[] mEhplmns; + + /** + * HPLMNs associated with the subscription + */ + private String[] mHplmns; + + /** * ISO Country code for the subscription's provider */ private String mCountryIso; @@ -316,6 +327,14 @@ public class SubscriptionInfo implements Parcelable { } /** + * @hide + */ + public void setAssociatedPlmns(String[] ehplmns, String[] hplmns) { + mEhplmns = ehplmns; + mHplmns = hplmns; + } + + /** * Creates and returns an icon {@code Bitmap} to represent this {@code SubscriptionInfo} in a * user interface. * @@ -467,6 +486,20 @@ public class SubscriptionInfo implements Parcelable { } /** + * @hide + */ + public List<String> getEhplmns() { + return mEhplmns == null ? Collections.emptyList() : Arrays.asList(mEhplmns); + } + + /** + * @hide + */ + public List<String> getHplmns() { + return mHplmns == null ? Collections.emptyList() : Arrays.asList(mHplmns); + } + + /** * @return the profile class of this subscription. * @hide */ @@ -600,7 +633,7 @@ public class SubscriptionInfo implements Parcelable { String mcc = source.readString(); String mnc = source.readString(); String countryIso = source.readString(); - Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source); + Bitmap iconBitmap = source.readParcelable(Bitmap.class.getClassLoader()); boolean isEmbedded = source.readBoolean(); UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR); String cardString = source.readString(); @@ -611,11 +644,15 @@ public class SubscriptionInfo implements Parcelable { int carrierid = source.readInt(); int profileClass = source.readInt(); int subType = source.readInt(); - - return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName, - nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso, - isEmbedded, accessRules, cardString, cardId, isOpportunistic, groupUUID, - isGroupDisabled, carrierid, profileClass, subType); + String[] ehplmns = source.readStringArray(); + String[] hplmns = source.readStringArray(); + + SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName, + carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, + countryIso, isEmbedded, accessRules, cardString, cardId, isOpportunistic, + groupUUID, isGroupDisabled, carrierid, profileClass, subType); + info.setAssociatedPlmns(ehplmns, hplmns); + return info; } @Override @@ -638,7 +675,7 @@ public class SubscriptionInfo implements Parcelable { dest.writeString(mMcc); dest.writeString(mMnc); dest.writeString(mCountryIso); - mIconBitmap.writeToParcel(dest, flags); + dest.writeParcelable(mIconBitmap, flags); dest.writeBoolean(mIsEmbedded); dest.writeTypedArray(mAccessRules, flags); dest.writeString(mCardString); @@ -649,6 +686,8 @@ public class SubscriptionInfo implements Parcelable { dest.writeInt(mCarrierId); dest.writeInt(mProfileClass); dest.writeInt(mSubscriptionType); + dest.writeStringArray(mEhplmns); + dest.writeStringArray(mHplmns); } @Override @@ -686,6 +725,8 @@ public class SubscriptionInfo implements Parcelable { + " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID + " mIsGroupDisabled=" + mIsGroupDisabled + " profileClass=" + mProfileClass + + " ehplmns = " + Arrays.toString(mEhplmns) + + " hplmns = " + Arrays.toString(mHplmns) + " subscriptionType=" + mSubscriptionType + "}"; } @@ -729,6 +770,8 @@ public class SubscriptionInfo implements Parcelable { && TextUtils.equals(mDisplayName, toCompare.mDisplayName) && TextUtils.equals(mCarrierName, toCompare.mCarrierName) && Arrays.equals(mAccessRules, toCompare.mAccessRules) - && mProfileClass == toCompare.mProfileClass; + && mProfileClass == toCompare.mProfileClass + && Arrays.equals(mEhplmns, toCompare.mEhplmns) + && Arrays.equals(mHplmns, toCompare.mHplmns); } } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 0c6341111029..d4f98743089e 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -294,6 +294,19 @@ public class SubscriptionManager { public static final String SUBSCRIPTION_TYPE = "subscription_type"; /** + * TelephonyProvider column name white_listed_apn_data. + * It's a bitmask of APN types that will be allowed on this subscription even if it's metered + * and mobile data is turned off by the user. + * <P>Type: INTEGER (int)</P> For example, if TYPE_MMS is is true, Telephony will allow MMS + * data connection to setup even if MMS is metered and mobile_data is turned off on that + * subscription. + * + * Default value is 0. + */ + /** @hide */ + public static final String WHITE_LISTED_APN_DATA = "white_listed_apn_data"; + + /** * This constant is to designate a subscription as a Local-SIM Subscription. * <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on the * device. @@ -458,6 +471,18 @@ public class SubscriptionManager { public static final String CARRIER_ID = "carrier_id"; /** + * @hide A comma-separated list of EHPLMNs associated with the subscription + * <P>Type: TEXT (String)</P> + */ + public static final String EHPLMNS = "ehplmns"; + + /** + * @hide A comma-separated list of HPLMNs associated with the subscription + * <P>Type: TEXT (String)</P> + */ + public static final String HPLMNS = "hplmns"; + + /** * TelephonyProvider column name for the MCC associated with a SIM, stored as a string. * <P>Type: TEXT (String)</P> * @hide @@ -3074,6 +3099,31 @@ public class SubscriptionManager { return subId; } + /** + * Set whether a subscription always allows MMS connection. If true, MMS network + * request will be accepted by telephony even if user turns "mobile data" off + * on this subscription. + * + * @param subId which subscription it's setting to. + * @param alwaysAllow whether Mms data is always allowed. + * @return whether operation is successful. + * + * @hide + */ + public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) { + try { + ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + if (iSub != null) { + return iSub.setAlwaysAllowMmsData(subId, alwaysAllow); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + return false; + } + private interface CallISubMethodHelper { int callMethod(ISub iSub) throws RemoteException; } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 0d3bc1db831f..18b9fbbf414e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -10376,10 +10376,10 @@ public class TelephonyManager { * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). * - * @return Map including the key as the active subscription ID (Note: if there is no active + * @return Map including the keys as the active subscription IDs (Note: if there is no active * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value - * as the list of {@link EmergencyNumber}; null if this information is not available; or throw - * a SecurityException if the caller does not have the permission. + * as the list of {@link EmergencyNumber}; empty Map if this information is not available; + * or throw a SecurityException if the caller does not have the permission. */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @NonNull @@ -10429,10 +10429,10 @@ public class TelephonyManager { * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li> * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li> * </ol> - * @return Map including the key as the active subscription ID (Note: if there is no active + * @return Map including the keys as the active subscription IDs (Note: if there is no active * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value - * as the list of {@link EmergencyNumber}; null if this information is not available; or throw - * a SecurityException if the caller does not have the permission. + * as the list of {@link EmergencyNumber}; empty Map if this information is not available; + * or throw a SecurityException if the caller does not have the permission. */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @NonNull @@ -10948,4 +10948,33 @@ public class TelephonyManager { } return new Pair<Integer, Integer>(-1, -1); } + + + /** + * Return whether MMS data is enabled. This will tell if framework will accept a MMS network + * request on a subId. + * + * Mms is enabled if: + * 1) user data is turned on, or + * 2) MMS is un-metered for this subscription, or + * 3) alwaysAllowMms setting {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on. + * + * @return whether MMS data is allowed. + * + * @hide + */ + public boolean isMmsDataEnabled() { + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isMmsDataEnabled(getSubId(), pkgForDebug); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + return false; + } } diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index e9aede7336f0..be5872387d7b 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -781,8 +781,13 @@ public class ImsMmTelManager { } /** - * Change the user's setting for RTT capability of this device. - * @param isEnabled if true RTT will be enabled during calls. + * Sets the capability of RTT for IMS calls placed on this subscription. + * + * Note: This does not affect the value of + * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting + * for RTT. That value is enabled/disabled separately by the user through the Accessibility + * settings. + * @param isEnabled if true RTT should be enabled during calls made on this subscription. */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean isEnabled) { diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index c008711ff236..e6777c17203a 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -40,8 +40,6 @@ import android.util.SparseArray; import com.android.ims.internal.IImsFeatureStatusCallback; import com.android.internal.annotations.VisibleForTesting; -import static android.Manifest.permission.MODIFY_PHONE_STATE; - /** * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend * ImsService must register the service in their AndroidManifest to be detected by the framework. @@ -229,8 +227,8 @@ public class ImsService extends Service { private void setupFeature(ImsFeature f, int slotId, int featureType, IImsFeatureStatusCallback c) { - f.addImsFeatureStatusCallback(c); f.initialize(this, slotId); + f.addImsFeatureStatusCallback(c); addImsFeature(slotId, featureType, f); } diff --git a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java index e8fcac19f675..de4f17466a47 100644 --- a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java @@ -19,7 +19,6 @@ package android.telephony.ims.compat.feature; import android.annotation.IntDef; import android.annotation.UnsupportedAppUsage; import android.content.Context; -import android.content.Intent; import android.os.IInterface; import android.os.RemoteException; import android.telephony.SubscriptionManager; @@ -42,32 +41,6 @@ public abstract class ImsFeature { private static final String LOG_TAG = "ImsFeature"; - /** - * Action to broadcast when ImsService is up. - * Internal use only. - * Only defined here separately compatibility purposes with the old ImsService. - * @hide - */ - public static final String ACTION_IMS_SERVICE_UP = - "com.android.ims.IMS_SERVICE_UP"; - - /** - * Action to broadcast when ImsService is down. - * Internal use only. - * Only defined here separately for compatibility purposes with the old ImsService. - * @hide - */ - public static final String ACTION_IMS_SERVICE_DOWN = - "com.android.ims.IMS_SERVICE_DOWN"; - - /** - * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents. - * A long value; the phone ID corresponding to the IMS service coming up or down. - * Only defined here separately for compatibility purposes with the old ImsService. - * @hide - */ - public static final String EXTRA_PHONE_ID = "android:phone_id"; - // Invalid feature value public static final int INVALID = -1; // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously @@ -162,30 +135,6 @@ public abstract class ImsFeature { } } } - sendImsServiceIntent(state); - } - - /** - * Provide backwards compatibility using deprecated service UP/DOWN intents. - */ - private void sendImsServiceIntent(@ImsState int state) { - if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { - return; - } - Intent intent; - switch (state) { - case ImsFeature.STATE_NOT_AVAILABLE: - case ImsFeature.STATE_INITIALIZING: - intent = new Intent(ACTION_IMS_SERVICE_DOWN); - break; - case ImsFeature.STATE_READY: - intent = new Intent(ACTION_IMS_SERVICE_UP); - break; - default: - intent = new Intent(ACTION_IMS_SERVICE_DOWN); - } - intent.putExtra(EXTRA_PHONE_ID, mSlotId); - mContext.sendBroadcast(intent); } /** diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index 5e3f3983b0a1..74af6bf6fe6e 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.Context; -import android.content.Intent; import android.os.IInterface; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -395,30 +394,6 @@ public abstract class ImsFeature { } } } - sendImsServiceIntent(state); - } - - /** - * Provide backwards compatibility using deprecated service UP/DOWN intents. - */ - private void sendImsServiceIntent(@ImsState int state) { - if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { - return; - } - Intent intent; - switch (state) { - case ImsFeature.STATE_UNAVAILABLE: - case ImsFeature.STATE_INITIALIZING: - intent = new Intent(ACTION_IMS_SERVICE_DOWN); - break; - case ImsFeature.STATE_READY: - intent = new Intent(ACTION_IMS_SERVICE_UP); - break; - default: - intent = new Intent(ACTION_IMS_SERVICE_DOWN); - } - intent.putExtra(EXTRA_PHONE_ID, mSlotId); - mContext.sendBroadcast(intent); } /** diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 6e8d038eda4f..bb5c251b69e1 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -94,6 +94,7 @@ public class DctConstants { public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48; public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49; public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50; + public static final int EVENT_APN_WHITE_LIST_CHANGE = BASE + 51; /***** Constants *****/ diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index cfba0529e664..b081228d9616 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -279,4 +279,6 @@ interface ISub { int getSimStateForSlotIndex(int slotIndex); boolean isActiveSubId(int subId, String callingPackage); + + boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index ecbbd6a6c786..536c514356cb 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1966,4 +1966,6 @@ interface ITelephony { int getRadioHalVersion(); boolean isModemEnabledForSlot(int slotIndex, String callingPackage); + + boolean isMmsDataEnabled(int subId, String callingPackage); } diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index 80fb58d45078..12b20efcb0b3 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -344,10 +344,22 @@ public final class TelephonyPermissions { return true; } // if the calling package is null then return now as there's no way to perform the - // DevicePolicyManager device / profile owner checks. + // DevicePolicyManager device / profile owner and AppOp checks if (callingPackage == null) { return false; } + // Allow access to an app that has been granted the READ_DEVICE_IDENTIFIERS app op. + long token = Binder.clearCallingIdentity(); + AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService( + Context.APP_OPS_SERVICE); + try { + if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid, + callingPackage) == AppOpsManager.MODE_ALLOWED) { + return true; + } + } finally { + Binder.restoreCallingIdentity(token); + } // Allow access to a device / profile owner app. DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); diff --git a/tests/FlickerTests/lib/Android.bp b/tests/FlickerTests/lib/Android.bp index 982fcbadddf7..5d8ed2c205e9 100644 --- a/tests/FlickerTests/lib/Android.bp +++ b/tests/FlickerTests/lib/Android.bp @@ -20,7 +20,7 @@ java_test { srcs: ["src/**/*.java"], static_libs: [ "androidx.test.janktesthelper", - "cts-amwm-util", + "cts-wm-util", "platformprotosnano", "layersprotosnano", "truth-prebuilt", diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index e0d74e0908ee..c9e3404e0f1a 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -34,6 +34,8 @@ import com.android.test.filters.SelectTest; public final class FrameworksTestsFilter extends SelectTest { private static final String[] SELECTED_TESTS = { + // Test specifications for FrameworksMockingCoreTests. + "android.app.activity.ActivityThreadClientTest", // Test specifications for FrameworksCoreTests. "android.app.servertransaction.", // all tests under the package. "android.view.DisplayCutoutTest", diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index 98324850f3f5..3da22b4fb9fa 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -129,12 +129,20 @@ class ValueBodyPrinter : public ConstValueVisitor { constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_FLAGS; if (attr->type_mask & kMask) { for (const auto& symbol : attr->symbols) { - printer_->Print(symbol.symbol.name.value().entry); - if (symbol.symbol.id) { - printer_->Print("("); + if (symbol.symbol.name) { + printer_->Print(symbol.symbol.name.value().entry); + + if (symbol.symbol.id) { + printer_->Print("("); + printer_->Print(symbol.symbol.id.value().to_string()); + printer_->Print(")"); + } + } else if (symbol.symbol.id) { printer_->Print(symbol.symbol.id.value().to_string()); - printer_->Print(")"); + } else { + printer_->Print("???"); } + printer_->Println(StringPrintf("=0x%08x", symbol.value)); } } diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 950c6f801461..9d4b837ce2d1 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -967,6 +967,10 @@ public class WifiEnterpriseConfig implements Parcelable { * from the top-level domain and all the labels in domain_suffix_match shall be included in the * certificate. The certificate may include additional sub-level labels in addition to the * required labels. + * <p>More than one match string can be provided by using semicolons to separate the strings + * (e.g., example.org;example.com). When multiple strings are specified, a match with any one of + * the values is considered a sufficient match for the certificate, i.e., the conditions are + * ORed ogether. * <p>For example, domain_suffix_match=example.com would match test.example.com but would not * match test-example.com. * @param domain The domain value |