diff options
186 files changed, 6904 insertions, 2834 deletions
diff --git a/Android.mk b/Android.mk index 634272b1d7ea..e27aa307e1e6 100644 --- a/Android.mk +++ b/Android.mk @@ -320,6 +320,8 @@ LOCAL_SRC_FILES += \ core/java/android/service/wallpaper/IWallpaperService.aidl \ core/java/android/service/chooser/IChooserTargetService.aidl \ core/java/android/service/chooser/IChooserTargetResult.aidl \ + core/java/android/service/resolver/IResolverRankerService.aidl \ + core/java/android/service/resolver/IResolverRankerResult.aidl \ core/java/android/text/ITextClassificationService.aidl \ core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\ core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\ @@ -367,7 +369,6 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \ core/java/com/android/internal/backup/IBackupTransport.aidl \ core/java/com/android/internal/backup/IObbBackupService.aidl \ - core/java/com/android/internal/font/IFontManager.aidl \ core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl \ core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl \ core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl \ @@ -729,6 +730,7 @@ aidl_files := \ frameworks/base/core/java/android/service/notification/SnoozeCriterion.aidl \ frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \ frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \ + frameworks/base/core/java/android/service/resolver/ResolverTarget.aidl \ frameworks/base/core/java/android/speech/tts/Voice.aidl \ frameworks/base/core/java/android/app/usage/CacheQuotaHint.aidl \ frameworks/base/core/java/android/app/usage/ExternalStorageStats.aidl \ @@ -1461,8 +1463,7 @@ LOCAL_PROTOC_FLAGS := \ LOCAL_SRC_FILES := \ $(call all-proto-files-under, core/proto) \ $(call all-proto-files-under, libs/incident/proto) -LOCAL_SHARED_LIBRARIES := libprotobuf-cpp-full -include $(BUILD_SHARED_LIBRARY) +include $(BUILD_STATIC_LIBRARY) # ==== c++ proto host library ============================== include $(CLEAR_VARS) diff --git a/api/current.txt b/api/current.txt index 74d9889dfb26..e640e30cae33 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8889,7 +8889,6 @@ package android.content { field public static final java.lang.String DOWNLOAD_SERVICE = "download"; field public static final java.lang.String DROPBOX_SERVICE = "dropbox"; field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint"; - field public static final java.lang.String FONT_SERVICE = "font"; field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; field public static final java.lang.String INPUT_SERVICE = "input"; @@ -10645,6 +10644,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen"; field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods"; field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback"; + field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv"; field public static final java.lang.String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper"; field public static final java.lang.String FEATURE_LOCATION = "android.hardware.location"; @@ -13242,7 +13242,7 @@ package android.graphics { method public void setFilterBitmap(boolean); method public void setFlags(int); method public void setFontFeatureSettings(java.lang.String); - method public boolean setFontVariationSettings(java.lang.String); + method public boolean setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; method public void setHinting(int); method public void setLetterSpacing(float); method public void setLinearText(boolean); @@ -13776,8 +13776,8 @@ package android.graphics { method public static android.graphics.Typeface.Builder obtain(); method public void recycle(); method public void reset(); - method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String); - method public android.graphics.Typeface.Builder setFontVariationSettings(android.text.FontConfig.Axis[]); + method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; + method public android.graphics.Typeface.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]); method public android.graphics.Typeface.Builder setItalic(int); method public android.graphics.Typeface.Builder setSourceFromAsset(android.content.res.AssetManager, java.lang.String); method public android.graphics.Typeface.Builder setSourceFromFile(java.io.File); @@ -14387,6 +14387,21 @@ package android.graphics.fonts { field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR; } + public final class FontVariationAxis implements android.os.Parcelable { + ctor public FontVariationAxis(java.lang.String, float) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; + method public int describeContents(); + method public static android.graphics.fonts.FontVariationAxis[] fromFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; + method public float getStyleValue(); + method public java.lang.String getTag(); + method public static java.lang.String toFontVariationSettings(android.graphics.fonts.FontVariationAxis[]); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontVariationAxis> CREATOR; + } + + public static class FontVariationAxis.InvalidFormatException extends java.lang.Exception { + ctor public FontVariationAxis.InvalidFormatException(java.lang.String); + } + } package android.graphics.pdf { @@ -15982,11 +15997,11 @@ package android.hardware.usb { ctor public UsbRequest(); method public boolean cancel(); method public void close(); - method public boolean enqueue(java.nio.ByteBuffer); method public java.lang.Object getClientData(); method public android.hardware.usb.UsbEndpoint getEndpoint(); method public boolean initialize(android.hardware.usb.UsbDeviceConnection, android.hardware.usb.UsbEndpoint); method public deprecated boolean queue(java.nio.ByteBuffer, int); + method public boolean queue(java.nio.ByteBuffer); method public void setClientData(java.lang.Object); } @@ -22999,6 +23014,7 @@ package android.media { method public void setProfile(android.media.CamcorderProfile); method public void setVideoEncoder(int) throws java.lang.IllegalStateException; method public void setVideoEncodingBitRate(int); + method public void setVideoEncodingProfileLevel(int, int); method public void setVideoFrameRate(int) throws java.lang.IllegalStateException; method public void setVideoSize(int, int) throws java.lang.IllegalStateException; method public void setVideoSource(int) throws java.lang.IllegalStateException; @@ -24540,85 +24556,6 @@ package android.media.tv { field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID"; } - public static abstract interface TvContract.BasePreviewProgramColumns implements android.media.tv.TvContract.BaseProgramColumns { - field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; - field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; - field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; - field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; - field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; - field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; - field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; - field public static final java.lang.String COLUMN_AUTHOR = "author"; - field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; - field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; - field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; - field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; - field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; - field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; - field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; - field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; - field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; - field public static final java.lang.String COLUMN_LIVE = "live"; - field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; - field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; - field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; - field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; - field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; - field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; - field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; - field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; - field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; - field public static final java.lang.String COLUMN_TRANSIENT = "transient"; - field public static final java.lang.String COLUMN_TYPE = "type"; - field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; - field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; - field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; - field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; - field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; - field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; - field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; - field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; - field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; - field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; - field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; - field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; - field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; - field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; - field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; - field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; - field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; - field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; - field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; - field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; - } - - public static abstract interface TvContract.BaseProgramColumns implements android.media.tv.TvContract.BaseTvColumns { - field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; - field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; - field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; - field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; - field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; - field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; - field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; - field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; - field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; - field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; - field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; - field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; - field public static final java.lang.String COLUMN_TITLE = "title"; - field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; - field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; - field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; - } - public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns { field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name"; } @@ -24705,22 +24642,116 @@ package android.media.tv { field public static final java.lang.String CONTENT_DIRECTORY = "logo"; } - public static final class TvContract.PreviewPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + public static final class TvContract.PreviewPrograms implements android.media.tv.TvContract.BaseTvColumns { + field public static final int ASPECT_RATIO_16_9 = 0; // 0x0 + field public static final int ASPECT_RATIO_1_1 = 2; // 0x2 + field public static final int ASPECT_RATIO_2_3 = 3; // 0x3 + field public static final int ASPECT_RATIO_3_2 = 1; // 0x1 + field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0 + field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1 + field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2 + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; + field public static final java.lang.String COLUMN_AUTHOR = "author"; + field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; + field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; + field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; + field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; + field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; + field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; + field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; + field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; + field public static final java.lang.String COLUMN_LIVE = "live"; + field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; + field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; + field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; + field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; + field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; + field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; + field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; + field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; + field public static final java.lang.String COLUMN_TYPE = "type"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String COLUMN_WEIGHT = "weight"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/preview_program"; field public static final android.net.Uri CONTENT_URI; - } - - public static final class TvContract.Programs implements android.media.tv.TvContract.BaseProgramColumns { + field public static final int INTERACTION_TYPE_FANS = 3; // 0x3 + field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2 + field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4 + field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1 + field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5 + field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6 + field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2 + field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1 + field public static final int TYPE_ALBUM = 8; // 0x8 + field public static final int TYPE_ARTIST = 9; // 0x9 + field public static final int TYPE_CHANNEL = 6; // 0x6 + field public static final int TYPE_CLIP = 4; // 0x4 + field public static final int TYPE_EVENT = 5; // 0x5 + field public static final int TYPE_MOVIE = 0; // 0x0 + field public static final int TYPE_PLAYLIST = 10; // 0xa + field public static final int TYPE_STATION = 11; // 0xb + field public static final int TYPE_TRACK = 7; // 0x7 + field public static final int TYPE_TV_EPISODE = 3; // 0x3 + field public static final int TYPE_TV_SEASON = 2; // 0x2 + field public static final int TYPE_TV_SERIES = 1; // 0x1 + } + + public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns { + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program"; field public static final android.net.Uri CONTENT_URI; @@ -24749,32 +24780,126 @@ package android.media.tv { field public static final java.lang.String TRAVEL = "TRAVEL"; } - public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseProgramColumns { + public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseTvColumns { + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; field public static final java.lang.String COLUMN_INPUT_ID = "input_id"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes"; field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri"; field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis"; field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program"; field public static final android.net.Uri CONTENT_URI; } - public static final class TvContract.WatchNextPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + public static final class TvContract.WatchNextPrograms implements android.media.tv.TvContract.BaseTvColumns { ctor public TvContract.WatchNextPrograms(); + field public static final int ASPECT_RATIO_16_9 = 0; // 0x0 + field public static final int ASPECT_RATIO_1_1 = 2; // 0x2 + field public static final int ASPECT_RATIO_2_3 = 3; // 0x3 + field public static final int ASPECT_RATIO_3_2 = 1; // 0x1 + field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0 + field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1 + field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2 + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; + field public static final java.lang.String COLUMN_AUTHOR = "author"; + field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; + field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; + field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; + field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; + field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; + field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; + field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; + field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; field public static final java.lang.String COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS = "last_engagement_time_utc_millis"; + field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; + field public static final java.lang.String COLUMN_LIVE = "live"; + field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; + field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; + field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; + field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; + field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; + field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; + field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; + field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; + field public static final java.lang.String COLUMN_TYPE = "type"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program"; field public static final android.net.Uri CONTENT_URI; - field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; - field public static final java.lang.String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + field public static final int INTERACTION_TYPE_FANS = 3; // 0x3 + field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2 + field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4 + field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1 + field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5 + field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6 + field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2 + field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1 + field public static final int TYPE_ALBUM = 8; // 0x8 + field public static final int TYPE_ARTIST = 9; // 0x9 + field public static final int TYPE_CHANNEL = 6; // 0x6 + field public static final int TYPE_CLIP = 4; // 0x4 + field public static final int TYPE_EVENT = 5; // 0x5 + field public static final int TYPE_MOVIE = 0; // 0x0 + field public static final int TYPE_PLAYLIST = 10; // 0xa + field public static final int TYPE_STATION = 11; // 0xb + field public static final int TYPE_TRACK = 7; // 0x7 + field public static final int TYPE_TV_EPISODE = 3; // 0x3 + field public static final int TYPE_TV_SEASON = 2; // 0x2 + field public static final int TYPE_TV_SERIES = 1; // 0x1 + field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0 + field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2 + field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1 + field public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; // 0x3 } public final class TvInputInfo implements android.os.Parcelable { @@ -40007,7 +40132,7 @@ package android.telephony { method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(java.lang.String); method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String); method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); - method public boolean isConcurrentVoiceAndDataAllowed(); + method public boolean isConcurrentVoiceAndDataSupported(); method public boolean isDataEnabled(); method public boolean isHearingAidCompatibilitySupported(); method public boolean isNetworkRoaming(); @@ -40020,7 +40145,7 @@ package android.telephony { method public deprecated boolean sendDialerCode(java.lang.String); method public void sendDialerSpecialCode(java.lang.String); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); - method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); + method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); method public void setDataEnabled(boolean); method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String); method public boolean setOperatorBrandOverride(java.lang.String); @@ -40096,14 +40221,17 @@ package android.telephony { field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3 field public static final int SIM_STATE_READY = 5; // 0x5 field public static final int SIM_STATE_UNKNOWN = 0; // 0x0 + field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe + field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff + field public static final int USSD_RETURN_SUCCESS = 100; // 0x64 field public static final java.lang.String VVM_TYPE_CVVM = "vvm_type_cvvm"; field public static final java.lang.String VVM_TYPE_OMTP = "vvm_type_omtp"; } - public static abstract class TelephonyManager.OnReceiveUssdResponseCallback { - ctor public TelephonyManager.OnReceiveUssdResponseCallback(); - method public void onReceiveUssdResponse(java.lang.String, java.lang.CharSequence); - method public void onReceiveUssdResponseFailed(java.lang.String, int); + public static abstract class TelephonyManager.UssdResponseCallback { + ctor public TelephonyManager.UssdResponseCallback(); + method public void onReceiveUssdResponse(android.telephony.TelephonyManager, java.lang.String, java.lang.CharSequence); + method public void onReceiveUssdResponseFailed(android.telephony.TelephonyManager, java.lang.String, int); } public abstract class VisualVoicemailService extends android.app.Service { @@ -41047,65 +41175,6 @@ package android.text { method public android.text.Editable newEditable(java.lang.CharSequence); } - public final class FontConfig implements android.os.Parcelable { - ctor public FontConfig(android.text.FontConfig.Family[], android.text.FontConfig.Alias[]); - method public int describeContents(); - method public android.text.FontConfig.Alias[] getAliases(); - method public android.text.FontConfig.Family[] getFamilies(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR; - } - - public static final class FontConfig.Alias implements android.os.Parcelable { - ctor public FontConfig.Alias(java.lang.String, java.lang.String, int); - method public int describeContents(); - method public java.lang.String getName(); - method public java.lang.String getToName(); - method public int getWeight(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Alias> CREATOR; - } - - public static final class FontConfig.Axis implements android.os.Parcelable { - ctor public FontConfig.Axis(int, float); - ctor public FontConfig.Axis(java.lang.String, float); - method public int describeContents(); - method public float getStyleValue(); - method public int getTag(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Axis> CREATOR; - } - - public static final class FontConfig.Family implements android.os.Parcelable { - ctor public FontConfig.Family(java.lang.String, android.text.FontConfig.Font[], java.lang.String, int); - method public int describeContents(); - method public android.text.FontConfig.Font[] getFonts(); - method public java.lang.String getLanguage(); - method public java.lang.String getName(); - method public int getVariant(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Family> CREATOR; - field public static final int VARIANT_COMPACT = 1; // 0x1 - field public static final int VARIANT_DEFAULT = 0; // 0x0 - field public static final int VARIANT_ELEGANT = 2; // 0x2 - } - - public static final class FontConfig.Font implements android.os.Parcelable { - method public int describeContents(); - method public android.text.FontConfig.Axis[] getAxes(); - method public java.lang.String getFontName(); - method public int getTtcIndex(); - method public android.net.Uri getUri(); - method public int getWeight(); - method public boolean isItalic(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Font> CREATOR; - } - - public final class FontManager { - method public android.text.FontConfig getSystemFonts(); - } - public abstract interface GetChars implements java.lang.CharSequence { method public abstract void getChars(int, int, char[], int); } @@ -41253,6 +41322,8 @@ package android.text { field public static final int HYPHENATION_FREQUENCY_FULL = 2; // 0x2 field public static final int HYPHENATION_FREQUENCY_NONE = 0; // 0x0 field public static final int HYPHENATION_FREQUENCY_NORMAL = 1; // 0x1 + field public static final int JUSTIFICATION_MODE_INTER_WORD = 1; // 0x1 + field public static final int JUSTIFICATION_MODE_NONE = 0; // 0x0 } public static final class Layout.Alignment extends java.lang.Enum { @@ -41454,7 +41525,7 @@ package android.text { method public android.text.StaticLayout.Builder setHyphenationFrequency(int); method public android.text.StaticLayout.Builder setIncludePad(boolean); method public android.text.StaticLayout.Builder setIndents(int[], int[]); - method public android.text.StaticLayout.Builder setJustify(boolean); + method public android.text.StaticLayout.Builder setJustificationMode(int); method public android.text.StaticLayout.Builder setLineSpacing(float, float); method public android.text.StaticLayout.Builder setMaxLines(int); method public android.text.StaticLayout.Builder setText(java.lang.CharSequence); @@ -48658,7 +48729,7 @@ package android.webkit { method public abstract boolean getOffscreenPreRaster(); method public abstract deprecated android.webkit.WebSettings.PluginState getPluginState(); method public abstract java.lang.String getSansSerifFontFamily(); - method public abstract boolean getSaveFormData(); + method public abstract deprecated boolean getSaveFormData(); method public abstract deprecated boolean getSavePassword(); method public abstract java.lang.String getSerifFontFamily(); method public abstract java.lang.String getStandardFontFamily(); @@ -48707,7 +48778,7 @@ package android.webkit { method public abstract deprecated void setPluginState(android.webkit.WebSettings.PluginState); method public abstract deprecated void setRenderPriority(android.webkit.WebSettings.RenderPriority); method public abstract void setSansSerifFontFamily(java.lang.String); - method public abstract void setSaveFormData(boolean); + method public abstract deprecated void setSaveFormData(boolean); method public abstract deprecated void setSavePassword(boolean); method public abstract void setSerifFontFamily(java.lang.String); method public abstract void setStandardFontFamily(java.lang.String); @@ -48998,12 +49069,12 @@ package android.webkit { public abstract class WebViewDatabase { ctor public WebViewDatabase(); - method public abstract void clearFormData(); + method public abstract deprecated void clearFormData(); method public abstract void clearHttpAuthUsernamePassword(); method public abstract deprecated void clearUsernamePassword(); method public abstract java.lang.String[] getHttpAuthUsernamePassword(java.lang.String, java.lang.String); method public static android.webkit.WebViewDatabase getInstance(android.content.Context); - method public abstract boolean hasFormData(); + method public abstract deprecated boolean hasFormData(); method public abstract boolean hasHttpAuthUsernamePassword(); method public abstract deprecated boolean hasUsernamePassword(); method public abstract void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String); @@ -51264,7 +51335,7 @@ package android.widget { method public boolean getIncludeFontPadding(); method public android.os.Bundle getInputExtras(boolean); method public int getInputType(); - method public boolean getJustify(); + method public int getJustificationMode(); method public final android.text.method.KeyListener getKeyListener(); method public final android.text.Layout getLayout(); method public float getLetterSpacing(); @@ -51359,7 +51430,7 @@ package android.widget { method public void setExtractedText(android.view.inputmethod.ExtractedText); method public void setFilters(android.text.InputFilter[]); method public void setFontFeatureSettings(java.lang.String); - method public boolean setFontVariationSettings(java.lang.String); + method public boolean setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; method protected boolean setFrame(int, int, int, int); method public void setFreezesText(boolean); method public void setGravity(int); @@ -51377,7 +51448,7 @@ package android.widget { method public void setIncludeFontPadding(boolean); method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public void setInputType(int); - method public void setJustify(boolean); + method public void setJustificationMode(int); method public void setKeyListener(android.text.method.KeyListener); method public void setLetterSpacing(float); method public void setLineSpacing(float, float); diff --git a/api/system-current.txt b/api/system-current.txt index 2b4dde72bf1b..e20ea98af430 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -53,6 +53,7 @@ package android { field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; + field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"; field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; @@ -9386,7 +9387,6 @@ package android.content { field public static final java.lang.String DOWNLOAD_SERVICE = "download"; field public static final java.lang.String DROPBOX_SERVICE = "dropbox"; field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint"; - field public static final java.lang.String FONT_SERVICE = "font"; field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties"; field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; @@ -9797,6 +9797,7 @@ package android.content { field public static final java.lang.String ACTION_DREAMING_STARTED = "android.intent.action.DREAMING_STARTED"; field public static final java.lang.String ACTION_DREAMING_STOPPED = "android.intent.action.DREAMING_STOPPED"; field public static final java.lang.String ACTION_EDIT = "android.intent.action.EDIT"; + field public static final deprecated java.lang.String ACTION_EPHEMERAL_RESOLVER_SETTINGS = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS"; field public static final java.lang.String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE"; field public static final java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE"; field public static final java.lang.String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET"; @@ -9809,7 +9810,10 @@ package android.content { field public static final java.lang.String ACTION_INPUT_METHOD_CHANGED = "android.intent.action.INPUT_METHOD_CHANGED"; field public static final java.lang.String ACTION_INSERT = "android.intent.action.INSERT"; field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT"; + field public static final deprecated java.lang.String ACTION_INSTALL_EPHEMERAL_PACKAGE = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE"; + field public static final java.lang.String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE"; field public static final java.lang.String ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE"; + field public static final java.lang.String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS"; field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; field public static final java.lang.String ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED"; field public static final java.lang.String ACTION_LOCKED_BOOT_COMPLETED = "android.intent.action.LOCKED_BOOT_COMPLETED"; @@ -9868,6 +9872,8 @@ package android.content { field public static final java.lang.String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK"; field public static final java.lang.String ACTION_QUICK_VIEW = "android.intent.action.QUICK_VIEW"; field public static final java.lang.String ACTION_REBOOT = "android.intent.action.REBOOT"; + field public static final deprecated java.lang.String ACTION_RESOLVE_EPHEMERAL_PACKAGE = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE"; + field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE"; field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS"; field public static final java.lang.String ACTION_RUN = "android.intent.action.RUN"; field public static final java.lang.String ACTION_SCREEN_OFF = "android.intent.action.SCREEN_OFF"; @@ -11298,6 +11304,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen"; field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods"; field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback"; + field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv"; field public static final java.lang.String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper"; field public static final java.lang.String FEATURE_LOCATION = "android.hardware.location"; @@ -13974,7 +13981,7 @@ package android.graphics { method public void setFilterBitmap(boolean); method public void setFlags(int); method public void setFontFeatureSettings(java.lang.String); - method public boolean setFontVariationSettings(java.lang.String); + method public boolean setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; method public void setHinting(int); method public void setLetterSpacing(float); method public void setLinearText(boolean); @@ -14508,8 +14515,8 @@ package android.graphics { method public static android.graphics.Typeface.Builder obtain(); method public void recycle(); method public void reset(); - method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String); - method public android.graphics.Typeface.Builder setFontVariationSettings(android.text.FontConfig.Axis[]); + method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; + method public android.graphics.Typeface.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]); method public android.graphics.Typeface.Builder setItalic(int); method public android.graphics.Typeface.Builder setSourceFromAsset(android.content.res.AssetManager, java.lang.String); method public android.graphics.Typeface.Builder setSourceFromFile(java.io.File); @@ -15119,6 +15126,21 @@ package android.graphics.fonts { field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR; } + public final class FontVariationAxis implements android.os.Parcelable { + ctor public FontVariationAxis(java.lang.String, float) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; + method public int describeContents(); + method public static android.graphics.fonts.FontVariationAxis[] fromFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; + method public float getStyleValue(); + method public java.lang.String getTag(); + method public static java.lang.String toFontVariationSettings(android.graphics.fonts.FontVariationAxis[]); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontVariationAxis> CREATOR; + } + + public static class FontVariationAxis.InvalidFormatException extends java.lang.Exception { + ctor public FontVariationAxis.InvalidFormatException(java.lang.String); + } + } package android.graphics.pdf { @@ -17442,11 +17464,11 @@ package android.hardware.usb { ctor public UsbRequest(); method public boolean cancel(); method public void close(); - method public boolean enqueue(java.nio.ByteBuffer); method public java.lang.Object getClientData(); method public android.hardware.usb.UsbEndpoint getEndpoint(); method public boolean initialize(android.hardware.usb.UsbDeviceConnection, android.hardware.usb.UsbEndpoint); method public deprecated boolean queue(java.nio.ByteBuffer, int); + method public boolean queue(java.nio.ByteBuffer); method public void setClientData(java.lang.Object); } @@ -22841,6 +22863,7 @@ package android.media { method public void adjustStreamVolume(int, int, int); method public void adjustSuggestedStreamVolume(int, int, int); method public void adjustVolume(int, int); + method public int dispatchAudioFocusChange(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy); method public void dispatchMediaKeyEvent(android.view.KeyEvent); method public int generateAudioSessionId(); method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations(); @@ -24792,6 +24815,7 @@ package android.media { method public void setProfile(android.media.CamcorderProfile); method public void setVideoEncoder(int) throws java.lang.IllegalStateException; method public void setVideoEncodingBitRate(int); + method public void setVideoEncodingProfileLevel(int, int); method public void setVideoFrameRate(int) throws java.lang.IllegalStateException; method public void setVideoSize(int, int) throws java.lang.IllegalStateException; method public void setVideoSource(int) throws java.lang.IllegalStateException; @@ -25842,8 +25866,10 @@ package android.media.audiopolicy { public static abstract 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 static abstract class AudioPolicy.AudioPolicyStatusListener { @@ -25858,6 +25884,7 @@ package android.media.audiopolicy { method 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 public android.media.audiopolicy.AudioPolicy.Builder setIsAudioFocusPolicy(boolean); method public android.media.audiopolicy.AudioPolicy.Builder setLooper(android.os.Looper) throws java.lang.IllegalArgumentException; } @@ -26481,85 +26508,6 @@ package android.media.tv { field public static final java.lang.String METHOD_GET_COLUMNS = "get_columns"; } - public static abstract interface TvContract.BasePreviewProgramColumns implements android.media.tv.TvContract.BaseProgramColumns { - field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; - field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; - field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; - field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; - field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; - field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; - field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; - field public static final java.lang.String COLUMN_AUTHOR = "author"; - field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; - field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; - field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; - field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; - field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; - field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; - field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; - field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; - field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; - field public static final java.lang.String COLUMN_LIVE = "live"; - field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; - field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; - field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; - field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; - field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; - field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; - field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; - field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; - field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; - field public static final java.lang.String COLUMN_TRANSIENT = "transient"; - field public static final java.lang.String COLUMN_TYPE = "type"; - field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; - field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; - field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; - field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; - field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; - field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; - field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; - field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; - field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; - field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; - field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; - field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; - field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; - field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; - field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; - field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; - field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; - field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; - field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; - field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; - } - - public static abstract interface TvContract.BaseProgramColumns implements android.media.tv.TvContract.BaseTvColumns { - field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; - field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; - field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; - field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; - field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; - field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; - field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; - field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; - field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; - field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; - field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; - field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; - field public static final java.lang.String COLUMN_TITLE = "title"; - field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; - field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; - field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; - } - public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns { field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name"; } @@ -26647,22 +26595,116 @@ package android.media.tv { field public static final java.lang.String CONTENT_DIRECTORY = "logo"; } - public static final class TvContract.PreviewPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + public static final class TvContract.PreviewPrograms implements android.media.tv.TvContract.BaseTvColumns { + field public static final int ASPECT_RATIO_16_9 = 0; // 0x0 + field public static final int ASPECT_RATIO_1_1 = 2; // 0x2 + field public static final int ASPECT_RATIO_2_3 = 3; // 0x3 + field public static final int ASPECT_RATIO_3_2 = 1; // 0x1 + field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0 + field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1 + field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2 + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; + field public static final java.lang.String COLUMN_AUTHOR = "author"; + field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; + field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; + field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; + field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; + field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; + field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; + field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; + field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; + field public static final java.lang.String COLUMN_LIVE = "live"; + field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; + field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; + field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; + field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; + field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; + field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; + field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; + field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; + field public static final java.lang.String COLUMN_TYPE = "type"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String COLUMN_WEIGHT = "weight"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/preview_program"; field public static final android.net.Uri CONTENT_URI; - } - - public static final class TvContract.Programs implements android.media.tv.TvContract.BaseProgramColumns { + field public static final int INTERACTION_TYPE_FANS = 3; // 0x3 + field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2 + field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4 + field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1 + field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5 + field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6 + field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2 + field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1 + field public static final int TYPE_ALBUM = 8; // 0x8 + field public static final int TYPE_ARTIST = 9; // 0x9 + field public static final int TYPE_CHANNEL = 6; // 0x6 + field public static final int TYPE_CLIP = 4; // 0x4 + field public static final int TYPE_EVENT = 5; // 0x5 + field public static final int TYPE_MOVIE = 0; // 0x0 + field public static final int TYPE_PLAYLIST = 10; // 0xa + field public static final int TYPE_STATION = 11; // 0xb + field public static final int TYPE_TRACK = 7; // 0x7 + field public static final int TYPE_TV_EPISODE = 3; // 0x3 + field public static final int TYPE_TV_SEASON = 2; // 0x2 + field public static final int TYPE_TV_SERIES = 1; // 0x1 + } + + public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns { + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program"; field public static final android.net.Uri CONTENT_URI; @@ -26691,32 +26733,126 @@ package android.media.tv { field public static final java.lang.String TRAVEL = "TRAVEL"; } - public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseProgramColumns { + public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseTvColumns { + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; field public static final java.lang.String COLUMN_INPUT_ID = "input_id"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes"; field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri"; field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis"; field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program"; field public static final android.net.Uri CONTENT_URI; } - public static final class TvContract.WatchNextPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + public static final class TvContract.WatchNextPrograms implements android.media.tv.TvContract.BaseTvColumns { ctor public TvContract.WatchNextPrograms(); + field public static final int ASPECT_RATIO_16_9 = 0; // 0x0 + field public static final int ASPECT_RATIO_1_1 = 2; // 0x2 + field public static final int ASPECT_RATIO_2_3 = 3; // 0x3 + field public static final int ASPECT_RATIO_3_2 = 1; // 0x1 + field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0 + field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1 + field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2 + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; + field public static final java.lang.String COLUMN_AUTHOR = "author"; + field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; + field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; + field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; + field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; + field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; + field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; + field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; + field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; field public static final java.lang.String COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS = "last_engagement_time_utc_millis"; + field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; + field public static final java.lang.String COLUMN_LIVE = "live"; + field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; + field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; + field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; + field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; + field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; + field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; + field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; + field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; + field public static final java.lang.String COLUMN_TYPE = "type"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program"; field public static final android.net.Uri CONTENT_URI; - field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; - field public static final java.lang.String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + field public static final int INTERACTION_TYPE_FANS = 3; // 0x3 + field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2 + field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4 + field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1 + field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5 + field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6 + field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2 + field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1 + field public static final int TYPE_ALBUM = 8; // 0x8 + field public static final int TYPE_ARTIST = 9; // 0x9 + field public static final int TYPE_CHANNEL = 6; // 0x6 + field public static final int TYPE_CLIP = 4; // 0x4 + field public static final int TYPE_EVENT = 5; // 0x5 + field public static final int TYPE_MOVIE = 0; // 0x0 + field public static final int TYPE_PLAYLIST = 10; // 0xa + field public static final int TYPE_STATION = 11; // 0xb + field public static final int TYPE_TRACK = 7; // 0x7 + field public static final int TYPE_TV_EPISODE = 3; // 0x3 + field public static final int TYPE_TV_SEASON = 2; // 0x2 + field public static final int TYPE_TV_SERIES = 1; // 0x1 + field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0 + field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2 + field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1 + field public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; // 0x3 } public static final class TvContract.WatchedPrograms implements android.media.tv.TvContract.BaseTvColumns { @@ -40459,6 +40595,36 @@ package android.service.quicksettings { } +package android.service.resolver { + + public abstract class ResolverRankerService extends android.app.Service { + ctor public ResolverRankerService(); + method public android.os.IBinder onBind(android.content.Intent); + method public void onPredictSharingProbabilities(java.util.List<android.service.resolver.ResolverTarget>); + method public void onTrainRankingModel(java.util.List<android.service.resolver.ResolverTarget>, int); + field public static final java.lang.String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; + field public static final java.lang.String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService"; + } + + public final class ResolverTarget implements android.os.Parcelable { + ctor public ResolverTarget(); + method public int describeContents(); + method public float getChooserScore(); + method public float getLaunchScore(); + method public float getRecencyScore(); + method public float getSelectProbability(); + method public float getTimeSpentScore(); + method public void setChooserScore(float); + method public void setLaunchScore(float); + method public void setRecencyScore(float); + method public void setSelectProbability(float); + method public void setTimeSpentScore(float); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.resolver.ResolverTarget> CREATOR; + } + +} + package android.service.restrictions { public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver { @@ -43409,7 +43575,7 @@ package android.telephony { method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(java.lang.String); method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String); method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); - method public boolean isConcurrentVoiceAndDataAllowed(); + method public boolean isConcurrentVoiceAndDataSupported(); method public boolean isDataConnectivityPossible(); method public boolean isDataEnabled(); method public boolean isHearingAidCompatibilitySupported(); @@ -43430,7 +43596,7 @@ package android.telephony { method public deprecated boolean sendDialerCode(java.lang.String); method public void sendDialerSpecialCode(java.lang.String); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); - method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); + method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); method public void setDataEnabled(boolean); method public void setDataEnabled(int, boolean); @@ -43522,14 +43688,17 @@ package android.telephony { field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3 field public static final int SIM_STATE_READY = 5; // 0x5 field public static final int SIM_STATE_UNKNOWN = 0; // 0x0 + field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe + field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff + field public static final int USSD_RETURN_SUCCESS = 100; // 0x64 field public static final java.lang.String VVM_TYPE_CVVM = "vvm_type_cvvm"; field public static final java.lang.String VVM_TYPE_OMTP = "vvm_type_omtp"; } - public static abstract class TelephonyManager.OnReceiveUssdResponseCallback { - ctor public TelephonyManager.OnReceiveUssdResponseCallback(); - method public void onReceiveUssdResponse(java.lang.String, java.lang.CharSequence); - method public void onReceiveUssdResponseFailed(java.lang.String, int); + public static abstract class TelephonyManager.UssdResponseCallback { + ctor public TelephonyManager.UssdResponseCallback(); + method public void onReceiveUssdResponse(android.telephony.TelephonyManager, java.lang.String, java.lang.CharSequence); + method public void onReceiveUssdResponseFailed(android.telephony.TelephonyManager, java.lang.String, int); } public abstract class VisualVoicemailService extends android.app.Service { @@ -44505,65 +44674,6 @@ package android.text { method public android.text.Editable newEditable(java.lang.CharSequence); } - public final class FontConfig implements android.os.Parcelable { - ctor public FontConfig(android.text.FontConfig.Family[], android.text.FontConfig.Alias[]); - method public int describeContents(); - method public android.text.FontConfig.Alias[] getAliases(); - method public android.text.FontConfig.Family[] getFamilies(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR; - } - - public static final class FontConfig.Alias implements android.os.Parcelable { - ctor public FontConfig.Alias(java.lang.String, java.lang.String, int); - method public int describeContents(); - method public java.lang.String getName(); - method public java.lang.String getToName(); - method public int getWeight(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Alias> CREATOR; - } - - public static final class FontConfig.Axis implements android.os.Parcelable { - ctor public FontConfig.Axis(int, float); - ctor public FontConfig.Axis(java.lang.String, float); - method public int describeContents(); - method public float getStyleValue(); - method public int getTag(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Axis> CREATOR; - } - - public static final class FontConfig.Family implements android.os.Parcelable { - ctor public FontConfig.Family(java.lang.String, android.text.FontConfig.Font[], java.lang.String, int); - method public int describeContents(); - method public android.text.FontConfig.Font[] getFonts(); - method public java.lang.String getLanguage(); - method public java.lang.String getName(); - method public int getVariant(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Family> CREATOR; - field public static final int VARIANT_COMPACT = 1; // 0x1 - field public static final int VARIANT_DEFAULT = 0; // 0x0 - field public static final int VARIANT_ELEGANT = 2; // 0x2 - } - - public static final class FontConfig.Font implements android.os.Parcelable { - method public int describeContents(); - method public android.text.FontConfig.Axis[] getAxes(); - method public java.lang.String getFontName(); - method public int getTtcIndex(); - method public android.net.Uri getUri(); - method public int getWeight(); - method public boolean isItalic(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Font> CREATOR; - } - - public final class FontManager { - method public android.text.FontConfig getSystemFonts(); - } - public abstract interface GetChars implements java.lang.CharSequence { method public abstract void getChars(int, int, char[], int); } @@ -44711,6 +44821,8 @@ package android.text { field public static final int HYPHENATION_FREQUENCY_FULL = 2; // 0x2 field public static final int HYPHENATION_FREQUENCY_NONE = 0; // 0x0 field public static final int HYPHENATION_FREQUENCY_NORMAL = 1; // 0x1 + field public static final int JUSTIFICATION_MODE_INTER_WORD = 1; // 0x1 + field public static final int JUSTIFICATION_MODE_NONE = 0; // 0x0 } public static final class Layout.Alignment extends java.lang.Enum { @@ -44912,7 +45024,7 @@ package android.text { method public android.text.StaticLayout.Builder setHyphenationFrequency(int); method public android.text.StaticLayout.Builder setIncludePad(boolean); method public android.text.StaticLayout.Builder setIndents(int[], int[]); - method public android.text.StaticLayout.Builder setJustify(boolean); + method public android.text.StaticLayout.Builder setJustificationMode(int); method public android.text.StaticLayout.Builder setLineSpacing(float, float); method public android.text.StaticLayout.Builder setMaxLines(int); method public android.text.StaticLayout.Builder setText(java.lang.CharSequence); @@ -52202,7 +52314,7 @@ package android.webkit { method public abstract deprecated android.webkit.WebSettings.PluginState getPluginState(); method public abstract deprecated boolean getPluginsEnabled(); method public abstract java.lang.String getSansSerifFontFamily(); - method public abstract boolean getSaveFormData(); + method public abstract deprecated boolean getSaveFormData(); method public abstract deprecated boolean getSavePassword(); method public abstract java.lang.String getSerifFontFamily(); method public abstract java.lang.String getStandardFontFamily(); @@ -52257,7 +52369,7 @@ package android.webkit { method public abstract deprecated void setPluginsEnabled(boolean); method public abstract deprecated void setRenderPriority(android.webkit.WebSettings.RenderPriority); method public abstract void setSansSerifFontFamily(java.lang.String); - method public abstract void setSaveFormData(boolean); + method public abstract deprecated void setSaveFormData(boolean); method public abstract deprecated void setSavePassword(boolean); method public abstract void setSerifFontFamily(java.lang.String); method public abstract void setStandardFontFamily(java.lang.String); @@ -52585,12 +52697,12 @@ package android.webkit { public abstract class WebViewDatabase { ctor public WebViewDatabase(); - method public abstract void clearFormData(); + method public abstract deprecated void clearFormData(); method public abstract void clearHttpAuthUsernamePassword(); method public abstract deprecated void clearUsernamePassword(); method public abstract java.lang.String[] getHttpAuthUsernamePassword(java.lang.String, java.lang.String); method public static android.webkit.WebViewDatabase getInstance(android.content.Context); - method public abstract boolean hasFormData(); + method public abstract deprecated boolean hasFormData(); method public abstract boolean hasHttpAuthUsernamePassword(); method public abstract deprecated boolean hasUsernamePassword(); method public abstract void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String); @@ -55090,7 +55202,7 @@ package android.widget { method public boolean getIncludeFontPadding(); method public android.os.Bundle getInputExtras(boolean); method public int getInputType(); - method public boolean getJustify(); + method public int getJustificationMode(); method public final android.text.method.KeyListener getKeyListener(); method public final android.text.Layout getLayout(); method public float getLetterSpacing(); @@ -55185,7 +55297,7 @@ package android.widget { method public void setExtractedText(android.view.inputmethod.ExtractedText); method public void setFilters(android.text.InputFilter[]); method public void setFontFeatureSettings(java.lang.String); - method public boolean setFontVariationSettings(java.lang.String); + method public boolean setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; method protected boolean setFrame(int, int, int, int); method public void setFreezesText(boolean); method public void setGravity(int); @@ -55203,7 +55315,7 @@ package android.widget { method public void setIncludeFontPadding(boolean); method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public void setInputType(int); - method public void setJustify(boolean); + method public void setJustificationMode(int); method public void setKeyListener(android.text.method.KeyListener); method public void setLetterSpacing(float); method public void setLineSpacing(float, float); diff --git a/api/test-current.txt b/api/test-current.txt index c4d513e59677..795342fdc763 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -8921,7 +8921,6 @@ package android.content { field public static final java.lang.String DOWNLOAD_SERVICE = "download"; field public static final java.lang.String DROPBOX_SERVICE = "dropbox"; field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint"; - field public static final java.lang.String FONT_SERVICE = "font"; field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; field public static final java.lang.String INPUT_SERVICE = "input"; @@ -10684,6 +10683,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen"; field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods"; field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback"; + field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv"; field public static final java.lang.String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper"; field public static final java.lang.String FEATURE_LOCATION = "android.hardware.location"; @@ -13292,7 +13292,7 @@ package android.graphics { method public void setFilterBitmap(boolean); method public void setFlags(int); method public void setFontFeatureSettings(java.lang.String); - method public boolean setFontVariationSettings(java.lang.String); + method public boolean setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; method public void setHinting(int); method public void setLetterSpacing(float); method public void setLinearText(boolean); @@ -13826,8 +13826,8 @@ package android.graphics { method public static android.graphics.Typeface.Builder obtain(); method public void recycle(); method public void reset(); - method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String); - method public android.graphics.Typeface.Builder setFontVariationSettings(android.text.FontConfig.Axis[]); + method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; + method public android.graphics.Typeface.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]); method public android.graphics.Typeface.Builder setItalic(int); method public android.graphics.Typeface.Builder setSourceFromAsset(android.content.res.AssetManager, java.lang.String); method public android.graphics.Typeface.Builder setSourceFromFile(java.io.File); @@ -14438,6 +14438,21 @@ package android.graphics.fonts { field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR; } + public final class FontVariationAxis implements android.os.Parcelable { + ctor public FontVariationAxis(java.lang.String, float) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; + method public int describeContents(); + method public static android.graphics.fonts.FontVariationAxis[] fromFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; + method public float getStyleValue(); + method public java.lang.String getTag(); + method public static java.lang.String toFontVariationSettings(android.graphics.fonts.FontVariationAxis[]); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontVariationAxis> CREATOR; + } + + public static class FontVariationAxis.InvalidFormatException extends java.lang.Exception { + ctor public FontVariationAxis.InvalidFormatException(java.lang.String); + } + } package android.graphics.pdf { @@ -16037,11 +16052,11 @@ package android.hardware.usb { ctor public UsbRequest(); method public boolean cancel(); method public void close(); - method public boolean enqueue(java.nio.ByteBuffer); method public java.lang.Object getClientData(); method public android.hardware.usb.UsbEndpoint getEndpoint(); method public boolean initialize(android.hardware.usb.UsbDeviceConnection, android.hardware.usb.UsbEndpoint); method public deprecated boolean queue(java.nio.ByteBuffer, int); + method public boolean queue(java.nio.ByteBuffer); method public void setClientData(java.lang.Object); } @@ -23112,6 +23127,7 @@ package android.media { method public void setProfile(android.media.CamcorderProfile); method public void setVideoEncoder(int) throws java.lang.IllegalStateException; method public void setVideoEncodingBitRate(int); + method public void setVideoEncodingProfileLevel(int, int); method public void setVideoFrameRate(int) throws java.lang.IllegalStateException; method public void setVideoSize(int, int) throws java.lang.IllegalStateException; method public void setVideoSource(int) throws java.lang.IllegalStateException; @@ -24653,85 +24669,6 @@ package android.media.tv { field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID"; } - public static abstract interface TvContract.BasePreviewProgramColumns implements android.media.tv.TvContract.BaseProgramColumns { - field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; - field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; - field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; - field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; - field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; - field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; - field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; - field public static final java.lang.String COLUMN_AUTHOR = "author"; - field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; - field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; - field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; - field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; - field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; - field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; - field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; - field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; - field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; - field public static final java.lang.String COLUMN_LIVE = "live"; - field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; - field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; - field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; - field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; - field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; - field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; - field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; - field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; - field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; - field public static final java.lang.String COLUMN_TRANSIENT = "transient"; - field public static final java.lang.String COLUMN_TYPE = "type"; - field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; - field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; - field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; - field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; - field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; - field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; - field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE"; - field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; - field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; - field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM"; - field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST"; - field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL"; - field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP"; - field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT"; - field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE"; - field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST"; - field public static final java.lang.String TYPE_STATION = "TYPE_STATION"; - field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK"; - field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; - field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON"; - field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES"; - } - - public static abstract interface TvContract.BaseProgramColumns implements android.media.tv.TvContract.BaseTvColumns { - field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; - field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; - field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; - field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; - field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; - field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; - field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; - field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; - field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; - field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; - field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; - field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; - field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; - field public static final java.lang.String COLUMN_TITLE = "title"; - field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; - field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; - field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; - } - public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns { field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name"; } @@ -24818,22 +24755,116 @@ package android.media.tv { field public static final java.lang.String CONTENT_DIRECTORY = "logo"; } - public static final class TvContract.PreviewPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + public static final class TvContract.PreviewPrograms implements android.media.tv.TvContract.BaseTvColumns { + field public static final int ASPECT_RATIO_16_9 = 0; // 0x0 + field public static final int ASPECT_RATIO_1_1 = 2; // 0x2 + field public static final int ASPECT_RATIO_2_3 = 3; // 0x3 + field public static final int ASPECT_RATIO_3_2 = 1; // 0x1 + field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0 + field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1 + field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2 + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; + field public static final java.lang.String COLUMN_AUTHOR = "author"; + field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; + field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; + field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; + field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; + field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; + field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; + field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; + field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; + field public static final java.lang.String COLUMN_LIVE = "live"; + field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; + field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; + field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; + field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; + field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; + field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; + field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; + field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; + field public static final java.lang.String COLUMN_TYPE = "type"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String COLUMN_WEIGHT = "weight"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/preview_program"; field public static final android.net.Uri CONTENT_URI; - } - - public static final class TvContract.Programs implements android.media.tv.TvContract.BaseProgramColumns { + field public static final int INTERACTION_TYPE_FANS = 3; // 0x3 + field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2 + field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4 + field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1 + field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5 + field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6 + field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2 + field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1 + field public static final int TYPE_ALBUM = 8; // 0x8 + field public static final int TYPE_ARTIST = 9; // 0x9 + field public static final int TYPE_CHANNEL = 6; // 0x6 + field public static final int TYPE_CLIP = 4; // 0x4 + field public static final int TYPE_EVENT = 5; // 0x5 + field public static final int TYPE_MOVIE = 0; // 0x0 + field public static final int TYPE_PLAYLIST = 10; // 0xa + field public static final int TYPE_STATION = 11; // 0xb + field public static final int TYPE_TRACK = 7; // 0x7 + field public static final int TYPE_TV_EPISODE = 3; // 0x3 + field public static final int TYPE_TV_SEASON = 2; // 0x2 + field public static final int TYPE_TV_SERIES = 1; // 0x1 + } + + public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns { + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program"; field public static final android.net.Uri CONTENT_URI; @@ -24862,32 +24893,126 @@ package android.media.tv { field public static final java.lang.String TRAVEL = "TRAVEL"; } - public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseProgramColumns { + public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseTvColumns { + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; field public static final java.lang.String COLUMN_INPUT_ID = "input_id"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes"; field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri"; field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis"; field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program"; field public static final android.net.Uri CONTENT_URI; } - public static final class TvContract.WatchNextPrograms implements android.media.tv.TvContract.BasePreviewProgramColumns { + public static final class TvContract.WatchNextPrograms implements android.media.tv.TvContract.BaseTvColumns { ctor public TvContract.WatchNextPrograms(); + field public static final int ASPECT_RATIO_16_9 = 0; // 0x0 + field public static final int ASPECT_RATIO_1_1 = 2; // 0x2 + field public static final int ASPECT_RATIO_2_3 = 3; // 0x3 + field public static final int ASPECT_RATIO_3_2 = 1; // 0x1 + field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0 + field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1 + field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2 + field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language"; + field public static final java.lang.String COLUMN_AUTHOR = "author"; + field public static final java.lang.String COLUMN_AVAILABILITY = "availability"; + field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; + field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre"; + field public static final java.lang.String COLUMN_CONTENT_ID = "content_id"; + field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating"; + field public static final java.lang.String COLUMN_DURATION_MILLIS = "duration_millis"; + field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number"; + field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title"; + field public static final java.lang.String COLUMN_INTENT_URI = "intent_uri"; + field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count"; + field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; + field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; + field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count"; field public static final java.lang.String COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS = "last_engagement_time_utc_millis"; + field public static final java.lang.String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis"; + field public static final java.lang.String COLUMN_LIVE = "live"; + field public static final java.lang.String COLUMN_LOGO_URI = "logo_uri"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price"; + field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; + field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri"; + field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri"; + field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date"; + field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating"; + field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; + field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; + field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number"; + field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title"; + field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description"; + field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; + field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; + field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; + field public static final java.lang.String COLUMN_TYPE = "type"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program"; field public static final android.net.Uri CONTENT_URI; - field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; - field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; - field public static final java.lang.String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + field public static final int INTERACTION_TYPE_FANS = 3; // 0x3 + field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2 + field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4 + field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1 + field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5 + field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6 + field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2 + field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0 + field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1 + field public static final int TYPE_ALBUM = 8; // 0x8 + field public static final int TYPE_ARTIST = 9; // 0x9 + field public static final int TYPE_CHANNEL = 6; // 0x6 + field public static final int TYPE_CLIP = 4; // 0x4 + field public static final int TYPE_EVENT = 5; // 0x5 + field public static final int TYPE_MOVIE = 0; // 0x0 + field public static final int TYPE_PLAYLIST = 10; // 0xa + field public static final int TYPE_STATION = 11; // 0xb + field public static final int TYPE_TRACK = 7; // 0x7 + field public static final int TYPE_TV_EPISODE = 3; // 0x3 + field public static final int TYPE_TV_SEASON = 2; // 0x2 + field public static final int TYPE_TV_SERIES = 1; // 0x1 + field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0 + field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2 + field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1 + field public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; // 0x3 } public final class TvInputInfo implements android.os.Parcelable { @@ -40211,7 +40336,7 @@ package android.telephony { method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(java.lang.String); method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String); method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); - method public boolean isConcurrentVoiceAndDataAllowed(); + method public boolean isConcurrentVoiceAndDataSupported(); method public boolean isDataEnabled(); method public boolean isHearingAidCompatibilitySupported(); method public boolean isNetworkRoaming(); @@ -40224,7 +40349,7 @@ package android.telephony { method public deprecated boolean sendDialerCode(java.lang.String); method public void sendDialerSpecialCode(java.lang.String); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); - method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler); + method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); method public void setDataEnabled(boolean); method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String); method public boolean setOperatorBrandOverride(java.lang.String); @@ -40300,14 +40425,17 @@ package android.telephony { field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3 field public static final int SIM_STATE_READY = 5; // 0x5 field public static final int SIM_STATE_UNKNOWN = 0; // 0x0 + field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe + field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff + field public static final int USSD_RETURN_SUCCESS = 100; // 0x64 field public static final java.lang.String VVM_TYPE_CVVM = "vvm_type_cvvm"; field public static final java.lang.String VVM_TYPE_OMTP = "vvm_type_omtp"; } - public static abstract class TelephonyManager.OnReceiveUssdResponseCallback { - ctor public TelephonyManager.OnReceiveUssdResponseCallback(); - method public void onReceiveUssdResponse(java.lang.String, java.lang.CharSequence); - method public void onReceiveUssdResponseFailed(java.lang.String, int); + public static abstract class TelephonyManager.UssdResponseCallback { + ctor public TelephonyManager.UssdResponseCallback(); + method public void onReceiveUssdResponse(android.telephony.TelephonyManager, java.lang.String, java.lang.CharSequence); + method public void onReceiveUssdResponseFailed(android.telephony.TelephonyManager, java.lang.String, int); } public abstract class VisualVoicemailService extends android.app.Service { @@ -41255,65 +41383,6 @@ package android.text { method public android.text.Editable newEditable(java.lang.CharSequence); } - public final class FontConfig implements android.os.Parcelable { - ctor public FontConfig(android.text.FontConfig.Family[], android.text.FontConfig.Alias[]); - method public int describeContents(); - method public android.text.FontConfig.Alias[] getAliases(); - method public android.text.FontConfig.Family[] getFamilies(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR; - } - - public static final class FontConfig.Alias implements android.os.Parcelable { - ctor public FontConfig.Alias(java.lang.String, java.lang.String, int); - method public int describeContents(); - method public java.lang.String getName(); - method public java.lang.String getToName(); - method public int getWeight(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Alias> CREATOR; - } - - public static final class FontConfig.Axis implements android.os.Parcelable { - ctor public FontConfig.Axis(int, float); - ctor public FontConfig.Axis(java.lang.String, float); - method public int describeContents(); - method public float getStyleValue(); - method public int getTag(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Axis> CREATOR; - } - - public static final class FontConfig.Family implements android.os.Parcelable { - ctor public FontConfig.Family(java.lang.String, android.text.FontConfig.Font[], java.lang.String, int); - method public int describeContents(); - method public android.text.FontConfig.Font[] getFonts(); - method public java.lang.String getLanguage(); - method public java.lang.String getName(); - method public int getVariant(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Family> CREATOR; - field public static final int VARIANT_COMPACT = 1; // 0x1 - field public static final int VARIANT_DEFAULT = 0; // 0x0 - field public static final int VARIANT_ELEGANT = 2; // 0x2 - } - - public static final class FontConfig.Font implements android.os.Parcelable { - method public int describeContents(); - method public android.text.FontConfig.Axis[] getAxes(); - method public java.lang.String getFontName(); - method public int getTtcIndex(); - method public android.net.Uri getUri(); - method public int getWeight(); - method public boolean isItalic(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.text.FontConfig.Font> CREATOR; - } - - public final class FontManager { - method public android.text.FontConfig getSystemFonts(); - } - public abstract interface GetChars implements java.lang.CharSequence { method public abstract void getChars(int, int, char[], int); } @@ -41461,6 +41530,8 @@ package android.text { field public static final int HYPHENATION_FREQUENCY_FULL = 2; // 0x2 field public static final int HYPHENATION_FREQUENCY_NONE = 0; // 0x0 field public static final int HYPHENATION_FREQUENCY_NORMAL = 1; // 0x1 + field public static final int JUSTIFICATION_MODE_INTER_WORD = 1; // 0x1 + field public static final int JUSTIFICATION_MODE_NONE = 0; // 0x0 } public static final class Layout.Alignment extends java.lang.Enum { @@ -41662,7 +41733,7 @@ package android.text { method public android.text.StaticLayout.Builder setHyphenationFrequency(int); method public android.text.StaticLayout.Builder setIncludePad(boolean); method public android.text.StaticLayout.Builder setIndents(int[], int[]); - method public android.text.StaticLayout.Builder setJustify(boolean); + method public android.text.StaticLayout.Builder setJustificationMode(int); method public android.text.StaticLayout.Builder setLineSpacing(float, float); method public android.text.StaticLayout.Builder setMaxLines(int); method public android.text.StaticLayout.Builder setText(java.lang.CharSequence); @@ -49042,7 +49113,7 @@ package android.webkit { method public abstract boolean getOffscreenPreRaster(); method public abstract deprecated android.webkit.WebSettings.PluginState getPluginState(); method public abstract java.lang.String getSansSerifFontFamily(); - method public abstract boolean getSaveFormData(); + method public abstract deprecated boolean getSaveFormData(); method public abstract deprecated boolean getSavePassword(); method public abstract java.lang.String getSerifFontFamily(); method public abstract java.lang.String getStandardFontFamily(); @@ -49091,7 +49162,7 @@ package android.webkit { method public abstract deprecated void setPluginState(android.webkit.WebSettings.PluginState); method public abstract deprecated void setRenderPriority(android.webkit.WebSettings.RenderPriority); method public abstract void setSansSerifFontFamily(java.lang.String); - method public abstract void setSaveFormData(boolean); + method public abstract deprecated void setSaveFormData(boolean); method public abstract deprecated void setSavePassword(boolean); method public abstract void setSerifFontFamily(java.lang.String); method public abstract void setStandardFontFamily(java.lang.String); @@ -49382,12 +49453,12 @@ package android.webkit { public abstract class WebViewDatabase { ctor public WebViewDatabase(); - method public abstract void clearFormData(); + method public abstract deprecated void clearFormData(); method public abstract void clearHttpAuthUsernamePassword(); method public abstract deprecated void clearUsernamePassword(); method public abstract java.lang.String[] getHttpAuthUsernamePassword(java.lang.String, java.lang.String); method public static android.webkit.WebViewDatabase getInstance(android.content.Context); - method public abstract boolean hasFormData(); + method public abstract deprecated boolean hasFormData(); method public abstract boolean hasHttpAuthUsernamePassword(); method public abstract deprecated boolean hasUsernamePassword(); method public abstract void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String); @@ -51655,7 +51726,7 @@ package android.widget { method public boolean getIncludeFontPadding(); method public android.os.Bundle getInputExtras(boolean); method public int getInputType(); - method public boolean getJustify(); + method public int getJustificationMode(); method public final android.text.method.KeyListener getKeyListener(); method public final android.text.Layout getLayout(); method public float getLetterSpacing(); @@ -51750,7 +51821,7 @@ package android.widget { method public void setExtractedText(android.view.inputmethod.ExtractedText); method public void setFilters(android.text.InputFilter[]); method public void setFontFeatureSettings(java.lang.String); - method public boolean setFontVariationSettings(java.lang.String); + method public boolean setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException; method protected boolean setFrame(int, int, int, int); method public void setFreezesText(boolean); method public void setGravity(int); @@ -51768,7 +51839,7 @@ package android.widget { method public void setIncludeFontPadding(boolean); method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public void setInputType(int); - method public void setJustify(boolean); + method public void setJustificationMode(int); method public void setKeyListener(android.text.method.KeyListener); method public void setLetterSpacing(float); method public void setLineSpacing(float, float); diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index 3a92b9e74144..0e2c13ee1719 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -26,7 +26,8 @@ LOCAL_SHARED_LIBRARIES := \ libGLESv1_CM \ libgui \ libOpenSLES \ - libtinyalsa + libtinyalsa \ + libbase LOCAL_MODULE:= bootanimation diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index a6d2986e185a..2435ffadd879 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -38,6 +38,8 @@ #include <utils/Log.h> #include <utils/SystemClock.h> +#include <android-base/properties.h> + #include <ui/PixelFormat.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -67,6 +69,9 @@ namespace android { static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip"; static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip"; static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip"; +static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip"; +static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip"; + static const char SYSTEM_DATA_DIR_PATH[] = "/data/system"; static const char SYSTEM_TIME_DIR_NAME[] = "time"; static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time"; @@ -106,7 +111,13 @@ BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true), mTimeIsAccu mSession = new SurfaceComposerClient(); // If the system has already booted, the animation is not being used for a boot. - mSystemBoot = !property_get_bool(BOOT_COMPLETED_PROP_NAME, 0); + mSystemBoot = !android::base::GetBoolProperty(BOOT_COMPLETED_PROP_NAME, false); + std::string powerCtl = android::base::GetProperty("sys.powerctl", ""); + if (powerCtl.empty()) { + mShuttingDown = false; + } else { + mShuttingDown = true; + } } void BootAnimation::onFirstRef() { @@ -314,16 +325,23 @@ status_t BootAnimation::readyToRun() { char decrypt[PROPERTY_VALUE_MAX]; property_get("vold.decrypt", decrypt, ""); - bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt); + bool encryptedAnimation = atoi(decrypt) != 0 || + !strcmp("trigger_restart_min_framework", decrypt); - if (encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) { + if (!mShuttingDown && encryptedAnimation && + (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) { mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE; + return NO_ERROR; } - else if (access(OEM_BOOTANIMATION_FILE, R_OK) == 0) { - mZipFileName = OEM_BOOTANIMATION_FILE; - } - else if (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) { - mZipFileName = SYSTEM_BOOTANIMATION_FILE; + static const char* bootFiles[] = {OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE}; + static const char* shutdownFiles[] = + {OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE}; + + for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) { + if (access(f, R_OK) == 0) { + mZipFileName = f; + return NO_ERROR; + } } return NO_ERROR; } @@ -1047,7 +1065,9 @@ bool BootAnimation::playSoundsAllowed() const { if (!mSystemBoot) { return false; } - + if (mShuttingDown) { // no audio while shutting down + return false; + } // Read the system property to see if we should play the sound. // If it's not present, default to allowed. if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) { @@ -1073,7 +1093,7 @@ bool BootAnimation::updateIsTimeAccurate() { if (mTimeIsAccurate) { return true; } - + if (mShuttingDown) return true; struct stat statResult; if(stat(TIME_FORMAT_12_HOUR_FLAG_FILE_PATH, &statResult) == 0) { diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index f1fc98e10c1e..181ef1c596d1 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -163,6 +163,7 @@ private: bool mTimeIsAccurate; bool mTimeFormat12Hour; bool mSystemBoot; + bool mShuttingDown; String8 mZipFileName; SortedVector<String8> mLoadedFiles; sp<TimeCheckThread> mTimeCheckThread = nullptr; diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc index 7344ba74f70b..469c9646a4aa 100644 --- a/cmds/bootanimation/bootanim.rc +++ b/cmds/bootanimation/bootanim.rc @@ -1,7 +1,7 @@ service bootanim /system/bin/bootanimation - class core + class core animation user graphics group graphics audio disabled oneshot - writepid /dev/stune/top-app/tasks
\ No newline at end of file + writepid /dev/stune/top-app/tasks diff --git a/compiled-classes-phone b/compiled-classes-phone index ea8f4a4d87fa..1a19b2ce2de5 100644 --- a/compiled-classes-phone +++ b/compiled-classes-phone @@ -4061,7 +4061,6 @@ android.text.FontConfig$Family android.text.FontConfig$Family$1 android.text.FontConfig$Font android.text.FontConfig$Font$1 -android.text.FontManager android.text.GetChars android.text.GraphicsOperations android.text.Html @@ -5501,8 +5500,6 @@ com.android.internal.content.PackageHelper com.android.internal.content.PackageMonitor com.android.internal.content.ReferrerIntent com.android.internal.content.ReferrerIntent$1 -com.android.internal.font.IFontManager -com.android.internal.font.IFontManager$Stub com.android.internal.graphics.drawable.AnimationScaleListDrawable com.android.internal.graphics.drawable.AnimationScaleListDrawable$AnimationScaleListState com.android.internal.hardware.AmbientDisplayConfiguration diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index dbf81f95d5c8..d432160ccfbf 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4159,14 +4159,25 @@ public class Activity extends ContextThemeWrapper mTaskDescription.setPrimaryColor(colorPrimary); } } - // For dev-preview only. - if (mTaskDescription.getBackgroundColor() == 0) { - int colorBackground = a.getColor( - com.android.internal.R.styleable.ActivityTaskDescription_colorBackground, 0); - if (colorBackground != 0 && Color.alpha(colorBackground) == 0xFF) { - mTaskDescription.setBackgroundColor(colorBackground); - } + + int colorBackground = a.getColor( + com.android.internal.R.styleable.ActivityTaskDescription_colorBackground, 0); + if (colorBackground != 0 && Color.alpha(colorBackground) == 0xFF) { + mTaskDescription.setBackgroundColor(colorBackground); + } + + final int statusBarColor = a.getColor( + com.android.internal.R.styleable.ActivityTaskDescription_statusBarColor, 0); + if (statusBarColor != 0) { + mTaskDescription.setStatusBarColor(statusBarColor); } + + final int navigationBarColor = a.getColor( + com.android.internal.R.styleable.ActivityTaskDescription_navigationBarColor, 0); + if (navigationBarColor != 0) { + mTaskDescription.setNavigationBarColor(navigationBarColor); + } + a.recycle(); setTaskDescription(mTaskDescription); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4004bd6686b1..aede1bb67f80 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1145,6 +1145,8 @@ public class ActivityManager { private String mIconFilename; private int mColorPrimary; private int mColorBackground; + private int mStatusBarColor; + private int mNavigationBarColor; /** * Creates the TaskDescription to the specified values. @@ -1155,7 +1157,7 @@ public class ActivityManager { * opaque. */ public TaskDescription(String label, Bitmap icon, int colorPrimary) { - this(label, icon, null, colorPrimary, 0); + this(label, icon, null, colorPrimary, 0, 0, 0); if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) { throw new RuntimeException("A TaskDescription's primary color should be opaque"); } @@ -1168,7 +1170,7 @@ public class ActivityManager { * @param icon An icon that represents the current state of this activity. */ public TaskDescription(String label, Bitmap icon) { - this(label, icon, null, 0, 0); + this(label, icon, null, 0, 0, 0, 0); } /** @@ -1177,24 +1179,26 @@ public class ActivityManager { * @param label A label and description of the current state of this activity. */ public TaskDescription(String label) { - this(label, null, null, 0, 0); + this(label, null, null, 0, 0, 0, 0); } /** * Creates an empty TaskDescription. */ public TaskDescription() { - this(null, null, null, 0, 0); + this(null, null, null, 0, 0, 0, 0); } /** @hide */ public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary, - int colorBackground) { + int colorBackground, int statusBarColor, int navigationBarColor) { mLabel = label; mIcon = icon; mIconFilename = iconFilename; mColorPrimary = colorPrimary; mColorBackground = colorBackground; + mStatusBarColor = statusBarColor; + mNavigationBarColor = navigationBarColor; } /** @@ -1214,6 +1218,8 @@ public class ActivityManager { mIconFilename = other.mIconFilename; mColorPrimary = other.mColorPrimary; mColorBackground = other.mColorBackground; + mStatusBarColor = other.mStatusBarColor; + mNavigationBarColor = other.mNavigationBarColor; } private TaskDescription(Parcel source) { @@ -1253,6 +1259,20 @@ public class ActivityManager { } /** + * @hide + */ + public void setStatusBarColor(int statusBarColor) { + mStatusBarColor = statusBarColor; + } + + /** + * @hide + */ + public void setNavigationBarColor(int navigationBarColor) { + mNavigationBarColor = navigationBarColor; + } + + /** * Sets the icon for this task description. * @hide */ @@ -1325,6 +1345,20 @@ public class ActivityManager { return mColorBackground; } + /** + * @hide + */ + public int getStatusBarColor() { + return mStatusBarColor; + } + + /** + * @hide + */ + public int getNavigationBarColor() { + return mNavigationBarColor; + } + /** @hide */ public void saveToXml(XmlSerializer out) throws IOException { if (mLabel != null) { @@ -1377,6 +1411,8 @@ public class ActivityManager { } dest.writeInt(mColorPrimary); dest.writeInt(mColorBackground); + dest.writeInt(mStatusBarColor); + dest.writeInt(mNavigationBarColor); if (mIconFilename == null) { dest.writeInt(0); } else { @@ -1390,6 +1426,8 @@ public class ActivityManager { mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null; mColorPrimary = source.readInt(); mColorBackground = source.readInt(); + mStatusBarColor = source.readInt(); + mNavigationBarColor = source.readInt(); mIconFilename = source.readInt() > 0 ? source.readString() : null; } @@ -1407,7 +1445,9 @@ public class ActivityManager { public String toString() { return "TaskDescription Label: " + mLabel + " Icon: " + mIcon + " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary + - " colorBackground: " + mColorBackground; + " colorBackground: " + mColorBackground + + " statusBarColor: " + mColorBackground + + " navigationBarColor: " + mNavigationBarColor; } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e49aad2cd607..d1d462c2fbe8 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -221,6 +221,7 @@ public final class ActivityThread { private long mNetworkBlockSeq = INVALID_PROC_STATE_SEQ; private ContextImpl mSystemContext; + private ContextImpl mSystemUiContext; static volatile IPackageManager sPackageManager; @@ -2190,9 +2191,19 @@ public final class ActivityThread { } } + public ContextImpl getSystemUiContext() { + synchronized (this) { + if (mSystemUiContext == null) { + mSystemUiContext = ContextImpl.createSystemUiContext(this); + } + return mSystemUiContext; + } + } + public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { synchronized (this) { getSystemContext().installSystemApplicationInfo(info, classLoader); + getSystemUiContext().installSystemApplicationInfo(info, classLoader); // give ourselves a default profiler mProfiler = new Profiler(); @@ -5031,6 +5042,11 @@ public final class ActivityThread { if ((systemTheme.getChangingConfigurations() & configDiff) != 0) { systemTheme.rebase(); } + + final Theme systemUiTheme = getSystemUiContext().getTheme(); + if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) { + systemUiTheme.rebase(); + } } ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config); @@ -5086,9 +5102,10 @@ public final class ActivityThread { // Trigger a regular Configuration change event, only with a different assetsSeq number // so that we actually call through to all components. + // TODO(adamlesinski): Change this to make use of ActivityManager's upcoming ability to + // store configurations per-process. Configuration newConfig = new Configuration(); - newConfig.unset(); - newConfig.assetsSeq = mConfiguration.assetsSeq + 1; + newConfig.assetsSeq = (mConfiguration != null ? mConfiguration.assetsSeq : 0) + 1; handleConfigurationChanged(newConfig, null); // Schedule all activities to reload diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 461f9cc35125..a6838f8bbf0a 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1352,7 +1352,7 @@ public class ApplicationPackageManager extends PackageManager { public Resources getResourcesForApplication(@NonNull ApplicationInfo app) throws NameNotFoundException { if (app.packageName.equals("system")) { - return mContext.mMainThread.getSystemContext().getResources(); + return mContext.mMainThread.getSystemUiContext().getResources(); } final boolean sameUid = (app.uid == Process.myUid()); final Resources r = mContext.mMainThread.getTopLevelResources( @@ -1383,7 +1383,7 @@ public class ApplicationPackageManager extends PackageManager { "Call does not support special user #" + userId); } if ("system".equals(appPackageName)) { - return mContext.mMainThread.getSystemContext().getResources(); + return mContext.mMainThread.getSystemUiContext().getResources(); } try { ApplicationInfo ai = mPM.getApplicationInfo(appPackageName, sDefaultFlags, userId); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5a7246a4ed38..75f9d671fbad 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2218,6 +2218,18 @@ class ContextImpl extends Context { return context; } + /** + * System Context to be used for UI. This Context has resources that can be themed. + */ + static ContextImpl createSystemUiContext(ActivityThread mainThread) { + LoadedApk packageInfo = new LoadedApk(mainThread); + ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, + null); + context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null, + packageInfo.getCompatibilityInfo())); + return context; + } + static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 279b9003f8c0..399987fe1c41 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1128,8 +1128,13 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate newState = Fragment.CREATED; } if (f.mRemoving && newState > f.mState) { - // While removing a fragment, we can't change it to a higher state. - newState = f.mState; + if (f.mState == Fragment.INITIALIZING && f.isInBackStack()) { + // Allow the fragment to be created so that it can be saved later. + newState = Fragment.CREATED; + } else { + // While removing a fragment, we can't change it to a higher state. + newState = f.mState; + } } // Defer start if requested; don't allow it to move to STARTED or higher // if it's not already started. diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index b42df5e2e0fb..489a0f0975ae 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -984,7 +984,7 @@ public class ResourcesManager { final ResourcesKey key = mResourceImpls.keyAt(i); final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i); final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null; - if (impl != null && key.mResDir != null && key.mResDir.equals(baseCodePath)) { + if (impl != null && (key.mResDir == null || key.mResDir.equals(baseCodePath))) { updatedResourceKeys.put(impl, new ResourcesKey( key.mResDir, key.mSplitResDirs, diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index f719749cc9f9..6b05bdff5d1e 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -125,7 +125,6 @@ import android.telecom.TelecomManager; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import android.text.FontManager; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -141,7 +140,6 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; import com.android.internal.app.ISoundTriggerService; import com.android.internal.appwidget.IAppWidgetService; -import com.android.internal.font.IFontManager; import com.android.internal.os.IDropBoxManagerService; import com.android.internal.policy.PhoneLayoutInflater; @@ -820,14 +818,6 @@ final class SystemServiceRegistry { return new IncidentManager(ctx); }}); - registerService(Context.FONT_SERVICE, FontManager.class, - new CachedServiceFetcher<FontManager>() { - @Override - public FontManager createService(ContextImpl ctx) - throws ServiceNotFoundException { - IBinder b = ServiceManager.getServiceOrThrow(Context.FONT_SERVICE); - return new FontManager(IFontManager.Stub.asInterface(b)); - }}); registerService(Context.AUTOFILL_MANAGER_SERVICE, AutofillManager.class, new CachedServiceFetcher<AutofillManager>() { @Override diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index dbbfe308868c..368c7b8106b3 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3870,11 +3870,6 @@ public abstract class Context { public static final String DEVICE_IDENTIFIERS_SERVICE = "device_identifiers"; /** - * Service that provides System font data. - */ - public static final String FONT_SERVICE = "font"; - - /** * Service to report a system health "incident" * @hide */ @@ -4631,4 +4626,18 @@ public abstract class Context { public Handler getMainThreadHandler() { throw new RuntimeException("Not implemented. Must override in a subclass."); } + + /** + * Throws an exception if the Context is using system resources, + * which are non-runtime-overlay-themable and may show inconsistent UI. + * @hide + */ + public void assertRuntimeOverlayThemable() { + // Resources.getSystem() is a singleton and the only Resources not managed by + // ResourcesManager; therefore Resources.getSystem() is not themable. + if (getResources() == Resources.getSystem()) { + throw new IllegalArgumentException("Non-UI context used to display UI; " + + "get a UI context from ActivityThread#getSystemUiContext()"); + } + } } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index da887af52b2a..81081f838f74 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1505,22 +1505,38 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE"; /** - * Activity Action: Launch ephemeral installer. - * <p> - * Input: The data must be a http: URI that the ephemeral application is registered - * to handle. + * @hide + * @deprecated Do not use. This will go away. + * Replace with {@link #ACTION_INSTALL_INSTANT_APP_PACKAGE}. + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_INSTALL_EPHEMERAL_PACKAGE + = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE"; + /** + * Activity Action: Launch instant application installer. * <p class="note"> * This is a protected intent that can only be sent by the system. * </p> * * @hide */ + @SystemApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_INSTALL_EPHEMERAL_PACKAGE - = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE"; + public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE + = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE"; /** - * Service Action: Resolve ephemeral application. + * @hide + * @deprecated Do not use. This will go away. + * Replace with {@link #ACTION_RESOLVE_INSTANT_APP_PACKAGE}. + */ + @SystemApi + @SdkConstant(SdkConstantType.SERVICE_ACTION) + public static final String ACTION_RESOLVE_EPHEMERAL_PACKAGE + = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE"; + /** + * Service Action: Resolve instant application. * <p> * The system will have a persistent connection to this service. * This is a protected intent that can only be sent by the system. @@ -1528,12 +1544,22 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ + @SystemApi @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String ACTION_RESOLVE_EPHEMERAL_PACKAGE - = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE"; + public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE + = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE"; /** - * Activity Action: Launch ephemeral settings. + * @hide + * @deprecated Do not use. This will go away. + * Replace with {@link #ACTION_INSTANT_APP_RESOLVER_SETTINGS}. + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_EPHEMERAL_RESOLVER_SETTINGS + = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS"; + /** + * Activity Action: Launch instant app settings. * * <p class="note"> * This is a protected intent that can only be sent by the system. @@ -1541,9 +1567,10 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ + @SystemApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_EPHEMERAL_RESOLVER_SETTINGS - = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS"; + public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS + = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS"; /** * Used as a string extra field with {@link #ACTION_INSTALL_PACKAGE} to install a diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 912ce3d49ba1..4d767555e3b3 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -419,7 +419,7 @@ public class LauncherApps { */ private void logErrorForInvalidProfileAccess(@NonNull UserHandle target) { if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile()) { - Log.e(TAG, "Accessing other profiles/users from managed profile is no longer allowed."); + Log.w(TAG, "Accessing other profiles/users from managed profile is no longer allowed."); } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 136c13b6f37d..92a6484c4fe4 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2158,7 +2158,6 @@ public abstract class PackageManager { * {@link #hasSystemFeature}: The device supports only leanback UI. Only * applications designed for this experience should be run, though this is * not enforced by the system. - * @hide */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; @@ -6244,7 +6243,7 @@ public abstract class PackageManager { * Return the {@link ComponentName} of the activity providing Settings for the Instant App * resolver. * - * @see {@link android.content.intent#ACTION_EPHEMERAL_RESOLVER_SETTINGS} + * @see {@link android.content.intent#ACTION_INSTANT_APP_RESOLVER_SETTINGS} * @hide */ @SystemApi diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index b27600800cc6..e845359a35c4 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -102,6 +102,16 @@ public abstract class DisplayManagerInternal { int displayId, DisplayInfo info); /** + * Get current display info without override from WindowManager. + * Current implementation of LogicalDisplay#getDisplayInfoLocked() always returns display info + * with overrides from WM if set. This method can be used for getting real display size without + * overrides to determine if real changes to display metrics happened. + * @param displayId Id of the target display. + * @param outInfo {@link DisplayInfo} to fill. + */ + public abstract void getNonOverrideDisplayInfo(int displayId, DisplayInfo outInfo); + + /** * Called by the window manager to perform traversals while holding a * surface flinger transaction. */ diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java index badb344aabad..239a2df5e1c3 100644 --- a/core/java/android/hardware/usb/UsbRequest.java +++ b/core/java/android/hardware/usb/UsbRequest.java @@ -60,9 +60,11 @@ public class UsbRequest { // Prevent the connection from being finalized private UsbDeviceConnection mConnection; - /** Whether this buffer was {@link #enqueue enqueued (new behavior)} or {@link #queue queued - * (deprecared behavior)}. */ - private boolean mIsUsingEnqueue; + /** + * Whether this buffer was {@link #queue(ByteBuffer) queued using the new behavior} or + * {@link #queue(ByteBuffer, int) queued using the deprecated behavior}. + */ + private boolean mIsUsingNewQueue; /** Temporary buffer than might be used while buffer is enqueued */ private ByteBuffer mTempBuffer; @@ -172,7 +174,7 @@ public class UsbRequest { * * @return true if the queueing operation succeeded * - * @deprecated Use {@link #enqueue(ByteBuffer)} instead. + * @deprecated Use {@link #queue(ByteBuffer)} instead. */ @Deprecated public boolean queue(ByteBuffer buffer, int length) { @@ -219,23 +221,23 @@ public class UsbRequest { * * @return true if the queueing operation succeeded */ - public boolean enqueue(@Nullable ByteBuffer buffer) { + public boolean queue(@Nullable ByteBuffer buffer) { // Request need to be initialized Preconditions.checkState(mNativeContext != 0, "request is not initialized"); - // Request can not be currently enqueued - Preconditions.checkState(!mIsUsingEnqueue, "request is currently enqueued"); + // Request can not be currently queued + Preconditions.checkState(!mIsUsingNewQueue, "this request is currently queued"); boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); - boolean wasEnqueued; + boolean wasQueued; synchronized (mLock) { mBuffer = buffer; if (buffer == null) { // Null buffers enqueue empty USB requests which is supported - mIsUsingEnqueue = true; - wasEnqueued = native_enqueue(null, 0, 0); + mIsUsingNewQueue = true; + wasQueued = native_queue(null, 0, 0); } else { // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, @@ -260,18 +262,18 @@ public class UsbRequest { buffer = mTempBuffer; } - mIsUsingEnqueue = true; - wasEnqueued = native_enqueue(buffer, buffer.position(), buffer.remaining()); + mIsUsingNewQueue = true; + wasQueued = native_queue(buffer, buffer.position(), buffer.remaining()); } } - if (!wasEnqueued) { - mIsUsingEnqueue = false; + if (!wasQueued) { + mIsUsingNewQueue = false; mTempBuffer = null; mBuffer = null; } - return wasEnqueued; + return wasQueued; } /* package */ void dequeue() { @@ -279,9 +281,9 @@ public class UsbRequest { int bytesTransferred; synchronized (mLock) { - if (mIsUsingEnqueue) { + if (mIsUsingNewQueue) { bytesTransferred = native_dequeue_direct(); - mIsUsingEnqueue = false; + mIsUsingNewQueue = false; if (mBuffer == null) { // Nothing to do @@ -332,7 +334,7 @@ public class UsbRequest { private native boolean native_init(UsbDeviceConnection connection, int ep_address, int ep_attributes, int ep_max_packet_size, int ep_interval); private native void native_close(); - private native boolean native_enqueue(ByteBuffer buffer, int offset, int length); + private native boolean native_queue(ByteBuffer buffer, int offset, int length); private native boolean native_queue_array(byte[] buffer, int length, boolean out); private native int native_dequeue_array(byte[] buffer, int length, boolean out); private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out); diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 495340dc7222..63bbd96bd01d 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -38,9 +38,6 @@ interface INetworkPolicyManager { boolean isUidForeground(int uid); - /** Higher priority listener before general event dispatch */ - void setConnectivityListener(INetworkPolicyListener listener); - void registerListener(INetworkPolicyListener listener); void unregisterListener(INetworkPolicyListener listener); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e2100bd43898..660d53a91b99 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10324,6 +10324,10 @@ public final class Settings { INSTANT_APP_SETTINGS.add(DEVELOPMENT_FORCE_RTL); INSTANT_APP_SETTINGS.add(EPHEMERAL_COOKIE_MAX_SIZE_BYTES); INSTANT_APP_SETTINGS.add(AIRPLANE_MODE_ON); + INSTANT_APP_SETTINGS.add(WINDOW_ANIMATION_SCALE); + INSTANT_APP_SETTINGS.add(TRANSITION_ANIMATION_SCALE); + INSTANT_APP_SETTINGS.add(ANIMATOR_DURATION_SCALE); + INSTANT_APP_SETTINGS.add(DEBUG_VIEW_ATTRIBUTES); } /** diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index e27fa06a9df7..b7a04206a4c6 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -78,11 +78,6 @@ public final class Dataset implements Parcelable { } /** @hide */ - public @Nullable RemoteViews getPresentation() { - return mPresentation; - } - - /** @hide */ public @Nullable IntentSender getAuthentication() { return mAuthentication; } diff --git a/core/java/android/service/resolver/IResolverRankerResult.aidl b/core/java/android/service/resolver/IResolverRankerResult.aidl new file mode 100644 index 000000000000..bda315420b7b --- /dev/null +++ b/core/java/android/service/resolver/IResolverRankerResult.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.resolver; + +import android.service.resolver.ResolverTarget; + +/** + * @hide + */ +oneway interface IResolverRankerResult +{ + void sendResult(in List<ResolverTarget> results); +}
\ No newline at end of file diff --git a/core/java/android/service/resolver/IResolverRankerService.aidl b/core/java/android/service/resolver/IResolverRankerService.aidl new file mode 100644 index 000000000000..f0d747d974a7 --- /dev/null +++ b/core/java/android/service/resolver/IResolverRankerService.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.resolver; + +import android.service.resolver.IResolverRankerResult; +import android.service.resolver.ResolverTarget; + +/** + * @hide + */ +oneway interface IResolverRankerService +{ + void predict(in List<ResolverTarget> targets, IResolverRankerResult result); + void train(in List<ResolverTarget> targets, int selectedPosition); +}
\ No newline at end of file diff --git a/core/java/android/service/resolver/ResolverRankerService.java b/core/java/android/service/resolver/ResolverRankerService.java new file mode 100644 index 000000000000..05067479bf45 --- /dev/null +++ b/core/java/android/service/resolver/ResolverRankerService.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.resolver; + +import android.annotation.SdkConstant; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.ComponentName; +import android.content.Intent; +import android.os.IBinder; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.RemoteException; +import android.service.resolver.ResolverTarget; +import android.util.Log; + +import java.util.List; +import java.util.Map; + +/** + * A service to rank apps according to usage stats of apps, when the system is resolving targets for + * an Intent. + * + * <p>To extend this class, you must declare the service in your manifest file with the + * {@link android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE} permission, and include an + * intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> + * <pre> + * <service android:name=".MyResolverRankerService" + * android:exported="true" + * android:priority="100" + * android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE"> + * <intent-filter> + * <action android:name="android.service.resolver.ResolverRankerService" /> + * </intent-filter> + * </service> + * </pre> + * @hide + */ +@SystemApi +public abstract class ResolverRankerService extends Service { + + private static final String TAG = "ResolverRankerService"; + + private static final boolean DEBUG = false; + + /** + * The Intent action that a service must respond to. Add it to the intent filter of the service + * in its manifest. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService"; + + /** + * The permission that a service must require to ensure that only Android system can bind to it. + * If this permission is not enforced in the AndroidManifest of the service, the system will + * skip that service. + */ + public static final String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; + + private ResolverRankerServiceWrapper mWrapper = null; + + /** + * Called by the system to retrieve a list of probabilities to rank apps/options. To implement + * it, set selectProbability of each input {@link ResolverTarget}. The higher the + * selectProbability is, the more likely the {@link ResolverTarget} will be selected by the + * user. Override this function to provide prediction results. + * + * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked. + * + * @throws Exception when the prediction task fails. + */ + public void onPredictSharingProbabilities(final List<ResolverTarget> targets) {} + + /** + * Called by the system to train/update a ranking service, after the user makes a selection from + * the ranked list of apps. Override this function to enable model updates. + * + * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked. + * @param selectedPosition the position of the selected app in the list. + * + * @throws Exception when the training task fails. + */ + public void onTrainRankingModel( + final List<ResolverTarget> targets, final int selectedPosition) {} + + private static final String HANDLER_THREAD_NAME = "RESOLVER_RANKER_SERVICE"; + private volatile Handler mHandler; + private HandlerThread mHandlerThread; + + @Override + public IBinder onBind(Intent intent) { + if (DEBUG) Log.d(TAG, "onBind " + intent); + if (!SERVICE_INTERFACE.equals(intent.getAction())) { + if (DEBUG) Log.d(TAG, "bad intent action " + intent.getAction() + "; returning null"); + return null; + } + if (mHandlerThread == null) { + mHandlerThread = new HandlerThread(HANDLER_THREAD_NAME); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + } + if (mWrapper == null) { + mWrapper = new ResolverRankerServiceWrapper(); + } + return mWrapper; + } + + @Override + public void onDestroy() { + mHandler = null; + if (mHandlerThread != null) { + mHandlerThread.quitSafely(); + } + super.onDestroy(); + } + + private static void sendResult(List<ResolverTarget> targets, IResolverRankerResult result) { + try { + result.sendResult(targets); + } catch (Exception e) { + Log.e(TAG, "failed to send results: " + e); + } + } + + private class ResolverRankerServiceWrapper extends IResolverRankerService.Stub { + + @Override + public void predict(final List<ResolverTarget> targets, final IResolverRankerResult result) + throws RemoteException { + Runnable predictRunnable = new Runnable() { + @Override + public void run() { + try { + if (DEBUG) { + Log.d(TAG, "predict calls onPredictSharingProbabilities."); + } + onPredictSharingProbabilities(targets); + sendResult(targets, result); + } catch (Exception e) { + Log.e(TAG, "onPredictSharingProbabilities failed; send null results: " + e); + sendResult(null, result); + } + } + }; + final Handler h = mHandler; + if (h != null) { + h.post(predictRunnable); + } + } + + @Override + public void train(final List<ResolverTarget> targets, final int selectedPosition) + throws RemoteException { + Runnable trainRunnable = new Runnable() { + @Override + public void run() { + try { + if (DEBUG) { + Log.d(TAG, "train calls onTranRankingModel"); + } + onTrainRankingModel(targets, selectedPosition); + } catch (Exception e) { + Log.e(TAG, "onTrainRankingModel failed; skip train: " + e); + } + } + }; + final Handler h = mHandler; + if (h != null) { + h.post(trainRunnable); + } + } + } +} diff --git a/core/java/com/android/internal/font/IFontManager.aidl b/core/java/android/service/resolver/ResolverTarget.aidl index 52a626288caf..6cab2d4df908 100644 --- a/core/java/com/android/internal/font/IFontManager.aidl +++ b/core/java/android/service/resolver/ResolverTarget.aidl @@ -14,14 +14,9 @@ * limitations under the License. */ -package com.android.internal.font; - -import android.text.FontConfig; +package android.service.resolver; /** - * Interface to the font manager. * @hide */ -interface IFontManager { - FontConfig getSystemFonts(); -} +parcelable ResolverTarget; diff --git a/core/java/android/service/resolver/ResolverTarget.java b/core/java/android/service/resolver/ResolverTarget.java new file mode 100644 index 000000000000..fb3e2d738469 --- /dev/null +++ b/core/java/android/service/resolver/ResolverTarget.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.resolver; + +import android.annotation.SystemApi; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; + +import java.util.Map; + +/** + * A ResolverTarget contains features by which an app or option will be ranked, in + * {@link ResolverRankerService}. + * @hide + */ +@SystemApi +public final class ResolverTarget implements Parcelable { + private static final String TAG = "ResolverTarget"; + + /** + * a float score for recency of last use. + */ + private float mRecencyScore; + + /** + * a float score for total time spent. + */ + private float mTimeSpentScore; + + /** + * a float score for number of launches. + */ + private float mLaunchScore; + + /** + * a float score for number of selected. + */ + private float mChooserScore; + + /** + * a float score for the probability to be selected. + */ + private float mSelectProbability; + + // constructor for the class. + public ResolverTarget() {} + + ResolverTarget(Parcel in) { + mRecencyScore = in.readFloat(); + mTimeSpentScore = in.readFloat(); + mLaunchScore = in.readFloat(); + mChooserScore = in.readFloat(); + mSelectProbability = in.readFloat(); + } + + /** + * Gets the score for how recently the target was used in the foreground. + * + * @return a float score whose range is [0, 1]. The higher the score is, the more recently the + * target was used. + */ + public float getRecencyScore() { + return mRecencyScore; + } + + /** + * Sets the score for how recently the target was used in the foreground. + * + * @param recencyScore a float score whose range is [0, 1]. The higher the score is, the more + * recently the target was used. + */ + public void setRecencyScore(float recencyScore) { + this.mRecencyScore = recencyScore; + } + + /** + * Gets the score for how long the target has been used in the foreground. + * + * @return a float score whose range is [0, 1]. The higher the score is, the longer the target + * has been used for. + */ + public float getTimeSpentScore() { + return mTimeSpentScore; + } + + /** + * Sets the score for how long the target has been used in the foreground. + * + * @param timeSpentScore a float score whose range is [0, 1]. The higher the score is, the + * longer the target has been used for. + */ + public void setTimeSpentScore(float timeSpentScore) { + this.mTimeSpentScore = timeSpentScore; + } + + /** + * Gets the score for how many times the target has been launched to the foreground. + * + * @return a float score whose range is [0, 1]. The higher the score is, the more times the + * target has been launched. + */ + public float getLaunchScore() { + return mLaunchScore; + } + + /** + * Sets the score for how many times the target has been launched to the foreground. + * + * @param launchScore a float score whose range is [0, 1]. The higher the score is, the more + * times the target has been launched. + */ + public void setLaunchScore(float launchScore) { + this.mLaunchScore = launchScore; + } + + /** + * Gets the score for how many times the target has been selected by the user to share the same + * types of content. + * + * @return a float score whose range is [0, 1]. The higher the score is, the + * more times the target has been selected by the user to share the same types of content for. + */ + public float getChooserScore() { + return mChooserScore; + } + + /** + * Sets the score for how many times the target has been selected by the user to share the same + * types of content. + * + * @param chooserScore a float score whose range is [0, 1]. The higher the score is, the more + * times the target has been selected by the user to share the same types + * of content for. + */ + public void setChooserScore(float chooserScore) { + this.mChooserScore = chooserScore; + } + + /** + * Gets the probability of how likely this target will be selected by the user. + * + * @return a float score whose range is [0, 1]. The higher the score is, the more likely the + * user is going to select this target. + */ + public float getSelectProbability() { + return mSelectProbability; + } + + /** + * Sets the probability for how like this target will be selected by the user. + * + * @param selectProbability a float score whose range is [0, 1]. The higher the score is, the + * more likely tht user is going to select this target. + */ + public void setSelectProbability(float selectProbability) { + this.mSelectProbability = selectProbability; + } + + // serialize the class to a string. + @Override + public String toString() { + return "ResolverTarget{" + + mRecencyScore + ", " + + mTimeSpentScore + ", " + + mLaunchScore + ", " + + mChooserScore + ", " + + mSelectProbability + "}"; + } + + // describes the kinds of special objects contained in this Parcelable instance's marshaled + // representation. + @Override + public int describeContents() { + return 0; + } + + // flattens this object in to a Parcel. + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(mRecencyScore); + dest.writeFloat(mTimeSpentScore); + dest.writeFloat(mLaunchScore); + dest.writeFloat(mChooserScore); + dest.writeFloat(mSelectProbability); + } + + // creator definition for the class. + public static final Creator<ResolverTarget> CREATOR + = new Creator<ResolverTarget>() { + @Override + public ResolverTarget createFromParcel(Parcel source) { + return new ResolverTarget(source); + } + + @Override + public ResolverTarget[] newArray(int size) { + return new ResolverTarget[size]; + } + }; +} diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 1e9deebd8dcf..6208c532295a 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -85,7 +85,7 @@ public class DynamicLayout extends Layout this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, spacingmult, spacingadd, includepad, StaticLayout.BREAK_STRATEGY_SIMPLE, StaticLayout.HYPHENATION_FREQUENCY_NONE, - false /* justify */, ellipsize, ellipsizedWidth); + Layout.JUSTIFICATION_MODE_NONE, ellipsize, ellipsizedWidth); } /** @@ -102,7 +102,7 @@ public class DynamicLayout extends Layout int width, Alignment align, TextDirectionHeuristic textDir, float spacingmult, float spacingadd, boolean includepad, int breakStrategy, int hyphenationFrequency, - boolean justify, TextUtils.TruncateAt ellipsize, + int justificationMode, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { super((ellipsize == null) ? display @@ -128,7 +128,7 @@ public class DynamicLayout extends Layout mIncludePad = includepad; mBreakStrategy = breakStrategy; - mJustify = justify; + mJustificationMode = justificationMode; mHyphenationFrequency = hyphenationFrequency; /* @@ -303,7 +303,7 @@ public class DynamicLayout extends Layout .setEllipsize(mEllipsizeAt) .setBreakStrategy(mBreakStrategy) .setHyphenationFrequency(mHyphenationFrequency) - .setJustify(mJustify); + .setJustificationMode(mJustificationMode); reflowed.generate(b, false, true); int n = reflowed.getLineCount(); // If the new layout has a blank line at the end, but it is not @@ -811,7 +811,7 @@ public class DynamicLayout extends Layout private TextUtils.TruncateAt mEllipsizeAt; private int mBreakStrategy; private int mHyphenationFrequency; - private boolean mJustify; + private int mJustificationMode; private PackedIntVector mInts; private PackedObjectVector<Directions> mObjects; diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index 14d3ad790b1d..8537d8f1ea84 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -21,7 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.graphics.FontListParser; +import android.graphics.fonts.FontVariationAxis; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -31,6 +31,7 @@ import java.lang.annotation.Retention; /** * Font configuration descriptions for System fonts. + * @hide */ public final class FontConfig implements Parcelable { private final @NonNull Family[] mFamilies; @@ -84,78 +85,12 @@ public final class FontConfig implements Parcelable { }; /** - * Class that holds information about a Font axis. - */ - public static final class Axis implements Parcelable { - private final int mTag; - private final float mStyleValue; - - public Axis(int tag, float styleValue) { - mTag = tag; - mStyleValue = styleValue; - } - - public Axis(@NonNull String tagString, float styleValue) { - if (!FontListParser.isValidTag(tagString)) { - throw new IllegalArgumentException("Invalid tag pattern: " + tagString); - } - mTag = FontListParser.makeTag(tagString); - mStyleValue = styleValue; - } - - /** - * Returns the variable font axis tag associated to this axis. - */ - public int getTag() { - return mTag; - } - - /** - * Returns the style value associated to the given axis for this font. - */ - public float getStyleValue() { - return mStyleValue; - } - - /** - * @hide - */ - public Axis(Parcel in) { - mTag = in.readInt(); - mStyleValue = in.readFloat(); - } - - @Override - public void writeToParcel(Parcel out, int flag) { - out.writeInt(mTag); - out.writeFloat(mStyleValue); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Creator<Axis> CREATOR = new Creator<Axis>() { - @Override - public Axis createFromParcel(Parcel in) { - return new Axis(in); - } - - @Override - public Axis[] newArray(int size) { - return new Axis[size]; - } - }; - } - - /** * Class that holds information about a Font. */ public static final class Font implements Parcelable { private final @NonNull String mFontName; private final int mTtcIndex; - private final @NonNull Axis[] mAxes; + private final @NonNull FontVariationAxis[] mAxes; private final int mWeight; private final boolean mIsItalic; private Uri mUri; @@ -163,8 +98,8 @@ public final class FontConfig implements Parcelable { /** * @hide */ - public Font(@NonNull String fontName, int ttcIndex, @NonNull Axis[] axes, int weight, - boolean isItalic) { + public Font(@NonNull String fontName, int ttcIndex, @NonNull FontVariationAxis[] axes, + int weight, boolean isItalic) { mFontName = fontName; mTtcIndex = ttcIndex; mAxes = axes; @@ -189,7 +124,7 @@ public final class FontConfig implements Parcelable { /** * Returns the list of axes associated to this font. */ - public @NonNull Axis[] getAxes() { + public @NonNull FontVariationAxis[] getAxes() { return mAxes; } @@ -230,7 +165,7 @@ public final class FontConfig implements Parcelable { public Font(Parcel in) { mFontName = in.readString(); mTtcIndex = in.readInt(); - mAxes = in.createTypedArray(Axis.CREATOR); + mAxes = in.createTypedArray(FontVariationAxis.CREATOR); mWeight = in.readInt(); mIsItalic = in.readInt() == 1; mUri = in.readTypedObject(Uri.CREATOR); diff --git a/core/java/android/text/FontManager.java b/core/java/android/text/FontManager.java deleted file mode 100644 index b61cbf3018ef..000000000000 --- a/core/java/android/text/FontManager.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.text; - -import android.os.RemoteException; - -import com.android.internal.font.IFontManager; - -/** - * Interact with the Font service. - */ -public final class FontManager { - private static final String TAG = "FontManager"; - - private final IFontManager mService; - - /** - * @hide - */ - public FontManager(IFontManager service) { - mService = service; - } - - /** - * Retrieve the system fonts data. This loads the fonts.xml data if needed and loads all system - * fonts in to memory, providing file descriptors for them. - */ - public FontConfig getSystemFonts() { - try { - return mService.getSystemFonts(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } -} diff --git a/core/java/android/text/FontVariationAxis.aidl b/core/java/android/text/FontVariationAxis.aidl new file mode 100644 index 000000000000..d5d52c26024f --- /dev/null +++ b/core/java/android/text/FontVariationAxis.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +parcelable FontVariationAxis; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 53564f093b37..b47fce89b0ff 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -103,6 +103,21 @@ public abstract class Layout { private static final ParagraphStyle[] NO_PARA_SPANS = ArrayUtils.emptyArray(ParagraphStyle.class); + /** @hide */ + @IntDef({JUSTIFICATION_MODE_NONE, JUSTIFICATION_MODE_INTER_WORD}) + @Retention(RetentionPolicy.SOURCE) + public @interface JustificationMode {} + + /** + * Value for justification mode indicating no justification. + */ + public static final int JUSTIFICATION_MODE_NONE = 0; + + /** + * Value for justification mode indicating the text is justified by stretching word spacing. + */ + public static final int JUSTIFICATION_MODE_INTER_WORD = 1; + /** * Return how wide a layout must be in order to display the specified text with one line per * paragraph. @@ -219,8 +234,8 @@ public abstract class Layout { } /** @hide */ - protected void setJustify(boolean justify) { - mJustify = justify; + protected void setJustificationMode(@JustificationMode int justificationMode) { + mJustificationMode = justificationMode; } /** @@ -272,7 +287,7 @@ public abstract class Layout { } private boolean isJustificationRequired(int lineNum) { - if (!mJustify) return false; + if (mJustificationMode == JUSTIFICATION_MODE_NONE) return false; final int lineEnd = getLineEnd(lineNum); return lineEnd < mText.length() && mText.charAt(lineEnd - 1) != '\n'; } @@ -2229,7 +2244,7 @@ public abstract class Layout { private boolean mSpannedText; private TextDirectionHeuristic mTextDir; private SpanSet<LineBackgroundSpan> mLineBackgroundSpans; - private boolean mJustify; + private int mJustificationMode; public static final int DIR_LEFT_TO_RIGHT = 1; public static final int DIR_RIGHT_TO_LEFT = -1; diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 9a2e0bb9a0b8..74ff6dc0ec7f 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -94,7 +94,7 @@ public class StaticLayout extends Layout { b.mMaxLines = Integer.MAX_VALUE; b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; - b.mJustify = false; + b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE; b.mMeasuredText = MeasuredText.obtain(); return b; @@ -321,15 +321,15 @@ public class StaticLayout extends Layout { } /** - * Enables or disables paragraph justification. The default value is disabled (false). - * If the last line is too short for justification, the last line will be displayed with - * the alignment set by {@link #setAlignment}. + * Set paragraph justification mode. The default value is + * {@link Layout#JUSTIFICATION_MODE_NONE}. If the last line is too short for justification, + * the last line will be displayed with the alignment set by {@link #setAlignment}. * - * @param justify true for enabling and false for disabling paragraph justification. + * @param justificationMode justification mode for the paragraph. * @return this builder, useful for chaining. */ - public Builder setJustify(boolean justify) { - mJustify = justify; + public Builder setJustificationMode(@JustificationMode int justificationMode) { + mJustificationMode = justificationMode; return this; } @@ -418,7 +418,7 @@ public class StaticLayout extends Layout { int mHyphenationFrequency; int[] mLeftIndents; int[] mRightIndents; - boolean mJustify; + int mJustificationMode; Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); @@ -572,7 +572,7 @@ public class StaticLayout extends Layout { mLeftIndents = b.mLeftIndents; mRightIndents = b.mRightIndents; - setJustify(b.mJustify); + setJustificationMode(b.mJustificationMode); generate(b, b.mIncludePad, b.mIncludePad); } @@ -693,7 +693,8 @@ public class StaticLayout extends Layout { nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency, - b.mJustify); + // TODO: Support more justification mode, e.g. letter spacing, stretching. + b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE); if (mLeftIndents != null || mRightIndents != null) { // TODO(raph) performance: it would be better to do this once per layout rather // than once per paragraph, but that would require a change to the native diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 3316f3aeb60b..aac5baacdb11 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -529,6 +529,18 @@ public final class Choreographer { } } + /** + * Like {@link #getLastFrameTimeNanos}, but always returns the last frame time, not matter + * whether callbacks are currently running. + * @return The frame start time of the last frame, in the {@link System#nanoTime()} time base. + * @hide + */ + public long getLastFrameTimeNanos() { + synchronized (mLock) { + return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime(); + } + } + private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 5494377ceebd..6dedbde01995 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -50,7 +50,7 @@ import java.util.Arrays; * <li>The real display area specifies the part of the display that contains content * including the system decorations. Even so, the real display area may be smaller than the * physical size of the display if the window manager is emulating a smaller display - * using (adb shell am display-size). Use the following methods to query the + * using (adb shell wm size). Use the following methods to query the * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li> * </ul> * </p><p> @@ -947,7 +947,7 @@ public final class Display { * The size is adjusted based on the current rotation of the display. * </p><p> * The real size may be smaller than the physical size of the screen when the - * window manager is emulating a smaller display (using adb shell am display-size). + * window manager is emulating a smaller display (using adb shell wm size). * </p> * * @param outSize Set to the real size of the display. diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index ae1ee42c8720..f3c2421c604a 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -54,7 +54,9 @@ public class FocusFinder { final Rect mOtherRect = new Rect(); final Rect mBestCandidateRect = new Rect(); private final UserSpecifiedFocusComparator mUserSpecifiedFocusComparator = - new UserSpecifiedFocusComparator(); + new UserSpecifiedFocusComparator((v) -> v.getNextFocusForwardId()); + private final UserSpecifiedFocusComparator mUserSpecifiedClusterComparator = + new UserSpecifiedFocusComparator((v) -> v.getNextClusterForwardId()); private final FocusComparator mFocusComparator = new FocusComparator(); private final ArrayList<View> mTempList = new ArrayList<View>(); @@ -118,7 +120,7 @@ public class FocusFinder { * @return the "effective" root of {@param focused} */ private ViewGroup getEffectiveRoot(ViewGroup root, View focused) { - if (focused == null) { + if (focused == null || focused == root) { return root; } ViewParent effective = focused.getParent(); @@ -150,6 +152,12 @@ public class FocusFinder { @Nullable View currentCluster, @View.FocusDirection int direction) { View next = null; + if (currentCluster != null) { + next = findNextUserSpecifiedKeyboardNavigationCluster(root, currentCluster, direction); + if (next != null) { + return next; + } + } final ArrayList<View> clusters = mTempList; try { @@ -165,6 +173,16 @@ public class FocusFinder { return next; } + private View findNextUserSpecifiedKeyboardNavigationCluster(View root, View currentCluster, + int direction) { + View userSetNextCluster = + currentCluster.findUserSetNextKeyboardNavigationCluster(root, direction); + if (userSetNextCluster != null && userSetNextCluster.hasFocusable()) { + return userSetNextCluster; + } + return null; + } + private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) { // check for user specified next focus View userSetNextFocus = focused.findUserSetNextFocus(root, direction); @@ -238,6 +256,13 @@ public class FocusFinder { View currentCluster, List<View> clusters, @View.FocusDirection int direction) { + try { + // Note: This sort is stable. + mUserSpecifiedClusterComparator.setFocusables(clusters); + Collections.sort(clusters, mUserSpecifiedClusterComparator); + } finally { + mUserSpecifiedClusterComparator.recycle(); + } final int count = clusters.size(); switch (direction) { @@ -802,6 +827,15 @@ public class FocusFinder { private final SparseBooleanArray mIsConnectedTo = new SparseBooleanArray(); private final ArrayMap<View, View> mHeadsOfChains = new ArrayMap<View, View>(); private final ArrayMap<View, Integer> mOriginalOrdinal = new ArrayMap<>(); + private final NextIdGetter mNextIdGetter; + + public interface NextIdGetter { + int get(View view); + } + + UserSpecifiedFocusComparator(NextIdGetter nextIdGetter) { + mNextIdGetter = nextIdGetter; + } public void recycle() { mFocusables.clear(); @@ -810,14 +844,14 @@ public class FocusFinder { mOriginalOrdinal.clear(); } - public void setFocusables(ArrayList<View> focusables) { + public void setFocusables(List<View> focusables) { for (int i = focusables.size() - 1; i >= 0; i--) { final View view = focusables.get(i); final int id = view.getId(); if (isValidId(id)) { mFocusables.put(id, view); } - final int nextId = view.getNextFocusForwardId(); + final int nextId = mNextIdGetter.get(view); if (isValidId(nextId)) { mIsConnectedTo.put(nextId, true); } @@ -825,7 +859,7 @@ public class FocusFinder { for (int i = focusables.size() - 1; i >= 0; i--) { final View view = focusables.get(i); - final int nextId = view.getNextFocusForwardId(); + final int nextId = mNextIdGetter.get(view); if (isValidId(nextId) && !mIsConnectedTo.get(view.getId())) { setHeadOfChain(view); } @@ -838,7 +872,7 @@ public class FocusFinder { private void setHeadOfChain(View head) { for (View view = head; view != null; - view = mFocusables.get(view.getNextFocusForwardId())) { + view = mFocusables.get(mNextIdGetter.get(view))) { final View otherHead = mHeadsOfChains.get(view); if (otherHead != null) { if (otherHead == head) { @@ -866,7 +900,7 @@ public class FocusFinder { return -1; // first is the head, it should be first } else if (second == firstHead) { return 1; // second is the head, it should be first - } else if (isValidId(first.getNextFocusForwardId())) { + } else if (isValidId(mNextIdGetter.get(first))) { return -1; // first is not the end of the chain } else { return 1; // first is end of chain diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b12a7676dd7f..0221040501ff 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -707,13 +707,16 @@ import java.util.function.Predicate; * @attr ref android.R.styleable#View_isScrollContainer * @attr ref android.R.styleable#View_focusable * @attr ref android.R.styleable#View_focusableInTouchMode + * @attr ref android.R.styleable#View_focusedByDefault * @attr ref android.R.styleable#View_hapticFeedbackEnabled * @attr ref android.R.styleable#View_keepScreenOn + * @attr ref android.R.styleable#View_keyboardNavigationCluster * @attr ref android.R.styleable#View_layerType * @attr ref android.R.styleable#View_layoutDirection * @attr ref android.R.styleable#View_longClickable * @attr ref android.R.styleable#View_minHeight * @attr ref android.R.styleable#View_minWidth + * @attr ref android.R.styleable#View_nextClusterForward * @attr ref android.R.styleable#View_nextFocusDown * @attr ref android.R.styleable#View_nextFocusLeft * @attr ref android.R.styleable#View_nextFocusRight @@ -4076,7 +4079,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mNextFocusForwardId = View.NO_ID; /** - * User-specified next keyboard navigation cluster. + * User-specified next keyboard navigation cluster in the {@link #FOCUS_FORWARD} direction. + * + * @see #findUserSetNextKeyboardNavigationCluster(View, int) */ int mNextClusterForwardId = View.NO_ID; @@ -9968,6 +9973,29 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return null; } + /** + * If a user manually specified the next keyboard-navigation cluster for a particular direction, + * use the root to look up the view. + * + * @param root the root view of the hierarchy containing this view + * @param direction {@link #FOCUS_FORWARD} or {@link #FOCUS_BACKWARD} + * @return the user-specified next cluster, or {@code null} if there is none + */ + View findUserSetNextKeyboardNavigationCluster(View root, @FocusDirection int direction) { + switch (direction) { + case FOCUS_FORWARD: + if (mNextClusterForwardId == View.NO_ID) return null; + return findViewInsideOutShouldExist(root, mNextClusterForwardId); + case FOCUS_BACKWARD: { + if (mID == View.NO_ID) return null; + final int id = mID; + return root.findViewByPredicateInsideOut(this, + (Predicate<View>) t -> t.mNextClusterForwardId == id); + } + } + return null; + } + private View findViewInsideOutShouldExist(View root, int id) { if (mMatchIdPredicate == null) { mMatchIdPredicate = new MatchIdPredicate(); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index c250226ae713..9e1ceee60cd5 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1258,15 +1258,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return; } - final int count = mChildrenCount; - final View[] children = mChildren; - - for (int i = 0; i < count; i++) { - final View child = children[i]; + int count = 0; + final View[] visibleChildren = new View[mChildrenCount]; + for (int i = 0; i < mChildrenCount; ++i) { + final View child = mChildren[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { - child.addKeyboardNavigationClusters(views, direction); + visibleChildren[count++] = child; } } + Arrays.sort(visibleChildren, 0, count, FocusFinder.getFocusComparator(this, false)); + for (int i = 0; i < count; ++i) { + visibleChildren[i].addKeyboardNavigationClusters(views, direction); + } } /** @@ -7344,6 +7347,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** @hide */ protected void onSetLayoutParams(View child, LayoutParams layoutParams) { + requestLayout(); } /** @hide */ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2e201bf108f9..58ef0af3fe79 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -4660,6 +4660,7 @@ public final class ViewRootImpl implements ViewParent, if (cluster != null && cluster.isRootNamespace()) { // the default cluster. Try to find a non-clustered view to focus. if (cluster.restoreFocusNotInCluster()) { + playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); return true; } // otherwise skip to next actual cluster @@ -4667,6 +4668,7 @@ public final class ViewRootImpl implements ViewParent, } if (cluster != null && cluster.restoreFocusInCluster(realDirection)) { + playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); return true; } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 636fa7de98f0..61920bd5cee4 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -411,8 +411,16 @@ public abstract class WebSettings { public abstract boolean getUseWebViewBackgroundForOverscrollBackground(); /** - * Sets whether the WebView should save form data. The default is true. + * Sets whether the WebView should save form data. In Android O, the + * platform has implemented a fully functional Autofill feature to store + * form data. Therefore, the Webview form data save feature is disabled. + * + * Note that the feature will continue to be supported on older versions of + * Android as before. + * + * This function does not have any effect. */ + @Deprecated public abstract void setSaveFormData(boolean save); /** @@ -421,6 +429,7 @@ public abstract class WebSettings { * @return whether the WebView saves form data * @see #setSaveFormData */ + @Deprecated public abstract boolean getSaveFormData(); /** diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index 87d3c7b32abb..982c57b76b51 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -143,6 +143,7 @@ public abstract class WebViewDatabase { * @return whether there is any saved data for web forms * @see #clearFormData */ + @Deprecated public abstract boolean hasFormData(); /** @@ -150,5 +151,6 @@ public abstract class WebViewDatabase { * * @see #hasFormData */ + @Deprecated public abstract void clearFormData(); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 6f8df36b7bf5..1b60ebc6fc27 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -56,6 +56,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.graphics.fonts.FontVariationAxis; import android.os.AsyncTask; import android.os.Bundle; import android.os.LocaleList; @@ -609,7 +610,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mBreakStrategy; private int mHyphenationFrequency; - private boolean mJustify; + private int mJustificationMode; private int mMaximum = Integer.MAX_VALUE; private int mMaxMode = LINES; @@ -820,7 +821,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener String fontFeatureSettings = null; mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; - mJustify = false; + mJustificationMode = Layout.JUSTIFICATION_MODE_NONE; final Resources.Theme theme = context.getTheme(); @@ -3737,14 +3738,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Enables or disables full justification. The default value is false. If the last line is too - * short for justification, the last line will be displayed with the alignment set by - * {@link android.view.View#setTextAlignment}. + * Set justification mode. The default value is {@link Layout#JUSTIFICATION_MODE_NONE}. If the + * last line is too short for justification, the last line will be displayed with the + * alignment set by {@link android.view.View#setTextAlignment}. * - * @see #getJustify() + * @see #getJustificationMode() */ - public void setJustify(boolean justify) { - mJustify = justify; + @Layout.JustificationMode + public void setJustificationMode(@Layout.JustificationMode int justificationMode) { + mJustificationMode = justificationMode; if (mLayout != null) { nullLayouts(); requestLayout(); @@ -3753,12 +3755,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * @return true if currently paragraph justification is enabled. + * @return true if currently paragraph justification mode. * - * @see #setJustify(boolean) + * @see #setJustificationMode(int) */ - public boolean getJustify() { - return mJustify; + public @Layout.JustificationMode int getJustificationMode() { + return mJustificationMode; } /** @@ -3808,10 +3810,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * TextView. This function also returns true for empty settings string. Otherwise * returns false. * + * @throws FontVariationAxis.InvalidFormatException + * If given string is not a valid font variation settings format. + * * @see #getFontVariationSettings() * @see Paint#getFontVariationSettings() Paint.getFontVariationSettings() */ - public boolean setFontVariationSettings(@Nullable String fontVariationSettings) { + public boolean setFontVariationSettings(@Nullable String fontVariationSettings) + throws FontVariationAxis.InvalidFormatException { final String existingSettings = mTextPaint.getFontVariationSettings(); if (fontVariationSettings == existingSettings || (fontVariationSettings != null @@ -7678,7 +7684,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setIncludePad(mIncludePad) .setBreakStrategy(mBreakStrategy) .setHyphenationFrequency(mHyphenationFrequency) - .setJustify(mJustify) + .setJustificationMode(mJustificationMode) .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); if (shouldEllipsize) { builder.setEllipsize(mEllipsize) @@ -7720,7 +7726,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mText instanceof Spannable) { result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, - mBreakStrategy, mHyphenationFrequency, mJustify, + mBreakStrategy, mHyphenationFrequency, mJustificationMode, getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth); } else { if (boring == UNKNOWN_BORING) { @@ -7770,7 +7776,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setIncludePad(mIncludePad) .setBreakStrategy(mBreakStrategy) .setHyphenationFrequency(mHyphenationFrequency) - .setJustify(mJustify) + .setJustificationMode(mJustificationMode) .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); if (shouldEllipsize) { builder.setEllipsize(effectiveEllipsize) @@ -8110,7 +8116,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setIncludePad(getIncludeFontPadding()) .setBreakStrategy(getBreakStrategy()) .setHyphenationFrequency(getHyphenationFrequency()) - .setJustify(getJustify()) + .setJustificationMode(getJustificationMode()) .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE) .setTextDirection(getTextDirectionHeuristic()); diff --git a/core/java/com/android/internal/app/LRResolverRankerService.java b/core/java/com/android/internal/app/LRResolverRankerService.java new file mode 100644 index 000000000000..1cad7c770b7c --- /dev/null +++ b/core/java/com/android/internal/app/LRResolverRankerService.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Environment; +import android.os.IBinder; +import android.os.storage.StorageManager; +import android.service.resolver.ResolverRankerService; +import android.service.resolver.ResolverTarget; +import android.util.ArrayMap; +import android.util.Log; + +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * A Logistic Regression based {@link android.service.resolver.ResolverRankerService}, to be used + * in {@link ResolverComparator}. + */ +public final class LRResolverRankerService extends ResolverRankerService { + private static final String TAG = "LRResolverRankerService"; + + private static final boolean DEBUG = false; + + private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params"; + private static final String BIAS_PREF_KEY = "bias"; + private static final String VERSION_PREF_KEY = "version"; + + private static final String LAUNCH_SCORE = "launch"; + private static final String TIME_SPENT_SCORE = "timeSpent"; + private static final String RECENCY_SCORE = "recency"; + private static final String CHOOSER_SCORE = "chooser"; + + // parameters for a pre-trained model, to initialize the app ranker. When updating the + // pre-trained model, please update these params, as well as initModel(). + private static final int CURRENT_VERSION = 1; + private static final float LEARNING_RATE = 0.0001f; + private static final float REGULARIZER_PARAM = 0.0001f; + + private SharedPreferences mParamSharedPref; + private ArrayMap<String, Float> mFeatureWeights; + private float mBias; + + @Override + public IBinder onBind(Intent intent) { + initModel(); + return super.onBind(intent); + } + + @Override + public void onPredictSharingProbabilities(List<ResolverTarget> targets) { + final int size = targets.size(); + for (int i = 0; i < size; ++i) { + ResolverTarget target = targets.get(i); + ArrayMap<String, Float> features = getFeatures(target); + target.setSelectProbability(predict(features)); + } + } + + @Override + public void onTrainRankingModel(List<ResolverTarget> targets, int selectedPosition) { + final int size = targets.size(); + if (selectedPosition < 0 || selectedPosition >= size) { + if (DEBUG) { + Log.d(TAG, "Invalid Position of Selected App " + selectedPosition); + } + return; + } + final ArrayMap<String, Float> positive = getFeatures(targets.get(selectedPosition)); + final float positiveProbability = targets.get(selectedPosition).getSelectProbability(); + final int targetSize = targets.size(); + for (int i = 0; i < targetSize; ++i) { + if (i == selectedPosition) { + continue; + } + final ArrayMap<String, Float> negative = getFeatures(targets.get(i)); + final float negativeProbability = targets.get(i).getSelectProbability(); + if (negativeProbability > positiveProbability) { + update(negative, negativeProbability, false); + update(positive, positiveProbability, true); + } + } + commitUpdate(); + } + + private void initModel() { + mParamSharedPref = getParamSharedPref(); + mFeatureWeights = new ArrayMap<>(4); + if (mParamSharedPref == null || + mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) { + // Initializing the app ranker to a pre-trained model. When updating the pre-trained + // model, please increment CURRENT_VERSION, and update LEARNING_RATE and + // REGULARIZER_PARAM. + mBias = -1.6568f; + mFeatureWeights.put(LAUNCH_SCORE, 2.5543f); + mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f); + mFeatureWeights.put(RECENCY_SCORE, 0.269f); + mFeatureWeights.put(CHOOSER_SCORE, 4.2222f); + } else { + mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f); + mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f)); + mFeatureWeights.put( + TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f)); + mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f)); + mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f)); + } + } + + private ArrayMap<String, Float> getFeatures(ResolverTarget target) { + ArrayMap<String, Float> features = new ArrayMap<>(4); + features.put(RECENCY_SCORE, target.getRecencyScore()); + features.put(TIME_SPENT_SCORE, target.getTimeSpentScore()); + features.put(LAUNCH_SCORE, target.getLaunchScore()); + features.put(CHOOSER_SCORE, target.getChooserScore()); + return features; + } + + private float predict(ArrayMap<String, Float> target) { + if (target == null) { + return 0.0f; + } + final int featureSize = target.size(); + float sum = 0.0f; + for (int i = 0; i < featureSize; i++) { + String featureName = target.keyAt(i); + float weight = mFeatureWeights.getOrDefault(featureName, 0.0f); + sum += weight * target.valueAt(i); + } + return (float) (1.0 / (1.0 + Math.exp(-mBias - sum))); + } + + private void update(ArrayMap<String, Float> target, float predict, boolean isSelected) { + if (target == null) { + return; + } + final int featureSize = target.size(); + float error = isSelected ? 1.0f - predict : -predict; + for (int i = 0; i < featureSize; i++) { + String featureName = target.keyAt(i); + float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f); + mBias += LEARNING_RATE * error; + currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight + + LEARNING_RATE * error * target.valueAt(i); + mFeatureWeights.put(featureName, currentWeight); + } + if (DEBUG) { + Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias); + } + } + + private void commitUpdate() { + try { + SharedPreferences.Editor editor = mParamSharedPref.edit(); + editor.putFloat(BIAS_PREF_KEY, mBias); + final int size = mFeatureWeights.size(); + for (int i = 0; i < size; i++) { + editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i)); + } + editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION); + editor.apply(); + } catch (Exception e) { + Log.e(TAG, "Failed to commit update" + e); + } + } + + private SharedPreferences getParamSharedPref() { + // The package info in the context isn't initialized in the way it is for normal apps, + // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we + // build the path manually below using the same policy that appears in ContextImpl. + if (DEBUG) { + Log.d(TAG, "Context Package Name: " + getPackageName()); + } + final File prefsFile = new File(new File( + Environment.getDataUserCePackageDirectory( + StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()), + "shared_prefs"), + PARAM_SHARED_PREF_NAME + ".xml"); + return getSharedPreferences(prefsFile, Context.MODE_PRIVATE); + } +}
\ No newline at end of file diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 3f1c9adb1b68..622b70843cc2 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -530,6 +530,9 @@ public class ResolverActivity extends Activity { getMainThreadHandler().removeCallbacks(mPostListReadyRunnable); mPostListReadyRunnable = null; } + if (mAdapter != null && mAdapter.mResolverListController != null) { + mAdapter.mResolverListController.destroy(); + } } @Override diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java index 096fcb83e755..73b62a5fe60d 100644 --- a/core/java/com/android/internal/app/ResolverComparator.java +++ b/core/java/com/android/internal/app/ResolverComparator.java @@ -26,20 +26,34 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.SharedPreferences; +import android.content.ServiceConnection; import android.os.Environment; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; import android.os.storage.StorageManager; import android.os.UserHandle; +import android.service.resolver.IResolverRankerService; +import android.service.resolver.IResolverRankerResult; +import android.service.resolver.ResolverRankerService; +import android.service.resolver.ResolverTarget; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; import java.io.File; +import java.lang.InterruptedException; import java.text.Collator; import java.util.ArrayList; import java.util.Comparator; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -61,11 +75,15 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { private static final float RECENCY_MULTIPLIER = 2.f; - // feature names used in ranking. - private static final String LAUNCH_SCORE = "launch"; - private static final String TIME_SPENT_SCORE = "timeSpent"; - private static final String RECENCY_SCORE = "recency"; - private static final String CHOOSER_SCORE = "chooser"; + // message types + private static final int RESOLVER_RANKER_SERVICE_RESULT = 0; + private static final int RESOLVER_RANKER_RESULT_TIMEOUT = 1; + + // timeout for establishing connections with a ResolverRankerService. + private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200; + // timeout for establishing connections with a ResolverRankerService, collecting features and + // predicting ranking scores. + private static final int WATCHDOG_TIMEOUT_MILLIS = 500; private final Collator mCollator; private final boolean mHttp; @@ -74,18 +92,74 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { private final Map<String, UsageStats> mStats; private final long mCurrentTime; private final long mSinceTime; - private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>(); + private final LinkedHashMap<ComponentName, ResolverTarget> mTargetsDict = new LinkedHashMap<>(); private final String mReferrerPackage; + private final Object mLock = new Object(); + private ArrayList<ResolverTarget> mTargets; private String mContentType; private String[] mAnnotations; private String mAction; - private LogisticRegressionAppRanker mRanker; + private IResolverRankerService mRanker; + private ResolverRankerServiceConnection mConnection; + private AfterCompute mAfterCompute; + private Context mContext; + private CountDownLatch mConnectSignal; + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + public void handleMessage(Message msg) { + switch (msg.what) { + case RESOLVER_RANKER_SERVICE_RESULT: + if (DEBUG) { + Log.d(TAG, "RESOLVER_RANKER_SERVICE_RESULT"); + } + if (mHandler.hasMessages(RESOLVER_RANKER_RESULT_TIMEOUT)) { + if (msg.obj != null) { + final List<ResolverTarget> receivedTargets = + (List<ResolverTarget>) msg.obj; + if (receivedTargets != null && mTargets != null + && receivedTargets.size() == mTargets.size()) { + final int size = mTargets.size(); + for (int i = 0; i < size; ++i) { + mTargets.get(i).setSelectProbability( + receivedTargets.get(i).getSelectProbability()); + } + } else { + Log.e(TAG, "Sizes of sent and received ResolverTargets diff."); + } + } else { + Log.e(TAG, "Receiving null prediction results."); + } + mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT); + mAfterCompute.afterCompute(); + } + break; + + case RESOLVER_RANKER_RESULT_TIMEOUT: + if (DEBUG) { + Log.d(TAG, "RESOLVER_RANKER_RESULT_TIMEOUT; unbinding services"); + } + mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT); + mAfterCompute.afterCompute(); + break; - public ResolverComparator(Context context, Intent intent, String referrerPackage) { + default: + super.handleMessage(msg); + } + } + }; + + public interface AfterCompute { + public void afterCompute (); + } + + public ResolverComparator(Context context, Intent intent, String referrerPackage, + AfterCompute afterCompute) { mCollator = Collator.getInstance(context.getResources().getConfiguration().locale); String scheme = intent.getScheme(); mHttp = "http".equals(scheme) || "https".equals(scheme); mReferrerPackage = referrerPackage; + mAfterCompute = afterCompute; + mContext = context; mPm = context.getPackageManager(); mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); @@ -96,9 +170,9 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { mContentType = intent.getType(); getContentAnnotations(intent); mAction = intent.getAction(); - mRanker = new LogisticRegressionAppRanker(context); } + // get annotations of content from intent. public void getContentAnnotations(Intent intent) { ArrayList<String> annotations = intent.getStringArrayListExtra( Intent.EXTRA_CONTENT_ANNOTATIONS); @@ -114,20 +188,24 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { } } + public void setCallBack(AfterCompute afterCompute) { + mAfterCompute = afterCompute; + } + + // compute features for each target according to usage stats of targets. public void compute(List<ResolvedComponentInfo> targets) { - mScoredTargets.clear(); + reset(); final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD; - long mostRecentlyUsedTime = recentSinceTime + 1; - long mostTimeSpent = 1; - int mostLaunched = 1; - int mostSelected = 1; + float mostRecencyScore = 1.0f; + float mostTimeSpentScore = 1.0f; + float mostLaunchScore = 1.0f; + float mostChooserScore = 1.0f; for (ResolvedComponentInfo target : targets) { - final ScoredTarget scoredTarget - = new ScoredTarget(target.getResolveInfoAt(0).activityInfo); - mScoredTargets.put(target.name, scoredTarget); + final ResolverTarget resolverTarget = new ResolverTarget(); + mTargetsDict.put(target.name, resolverTarget); final UsageStats pkStats = mStats.get(target.name.getPackageName()); if (pkStats != null) { // Only count recency for apps that weren't the caller @@ -135,31 +213,33 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { // Persistent processes muck this up, so omit them too. if (!target.name.getPackageName().equals(mReferrerPackage) && !isPersistentProcess(target)) { - final long lastTimeUsed = pkStats.getLastTimeUsed(); - scoredTarget.lastTimeUsed = lastTimeUsed; - if (lastTimeUsed > mostRecentlyUsedTime) { - mostRecentlyUsedTime = lastTimeUsed; + final float recencyScore = + (float) Math.max(pkStats.getLastTimeUsed() - recentSinceTime, 0); + resolverTarget.setRecencyScore(recencyScore); + if (recencyScore > mostRecencyScore) { + mostRecencyScore = recencyScore; } } - final long timeSpent = pkStats.getTotalTimeInForeground(); - scoredTarget.timeSpent = timeSpent; - if (timeSpent > mostTimeSpent) { - mostTimeSpent = timeSpent; + final float timeSpentScore = (float) pkStats.getTotalTimeInForeground(); + resolverTarget.setTimeSpentScore(timeSpentScore); + if (timeSpentScore > mostTimeSpentScore) { + mostTimeSpentScore = timeSpentScore; } - final int launched = pkStats.mLaunchCount; - scoredTarget.launchCount = launched; - if (launched > mostLaunched) { - mostLaunched = launched; + final float launchScore = (float) pkStats.mLaunchCount; + resolverTarget.setLaunchScore(launchScore); + if (launchScore > mostLaunchScore) { + mostLaunchScore = launchScore; } - int selected = 0; + float chooserScore = 0.0f; if (pkStats.mChooserCounts != null && mAction != null && pkStats.mChooserCounts.get(mAction) != null) { - selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0); + chooserScore = (float) pkStats.mChooserCounts.get(mAction) + .getOrDefault(mContentType, 0); if (mAnnotations != null) { final int size = mAnnotations.length; for (int i = 0; i < size; i++) { - selected += pkStats.mChooserCounts.get(mAction) + chooserScore += (float) pkStats.mChooserCounts.get(mAction) .getOrDefault(mAnnotations[i], 0); } } @@ -169,44 +249,37 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { Log.d(TAG, "Action type is null"); } else { Log.d(TAG, "Chooser Count of " + mAction + ":" + - target.name.getPackageName() + " is " + Integer.toString(selected)); + target.name.getPackageName() + " is " + + Float.toString(chooserScore)); } } - scoredTarget.chooserCount = selected; - if (selected > mostSelected) { - mostSelected = selected; + resolverTarget.setChooserScore(chooserScore); + if (chooserScore > mostChooserScore) { + mostChooserScore = chooserScore; } } } - if (DEBUG) { - Log.d(TAG, "compute - mostRecentlyUsedTime: " + mostRecentlyUsedTime - + " mostTimeSpent: " + mostTimeSpent - + " recentSinceTime: " + recentSinceTime - + " mostLaunched: " + mostLaunched); + Log.d(TAG, "compute - mostRecencyScore: " + mostRecencyScore + + " mostTimeSpentScore: " + mostTimeSpentScore + + " mostLaunchScore: " + mostLaunchScore + + " mostChooserScore: " + mostChooserScore); } - for (ScoredTarget target : mScoredTargets.values()) { - final float recency = (float) Math.max(target.lastTimeUsed - recentSinceTime, 0) - / (mostRecentlyUsedTime - recentSinceTime); - target.setFeatures((float) target.launchCount / mostLaunched, - (float) target.timeSpent / mostTimeSpent, - recency * recency * RECENCY_MULTIPLIER, - (float) target.chooserCount / mostSelected); - target.selectProb = mRanker.predict(target.getFeatures()); + mTargets = new ArrayList<>(mTargetsDict.values()); + for (ResolverTarget target : mTargets) { + final float recency = target.getRecencyScore() / mostRecencyScore; + setFeatures(target, recency * recency * RECENCY_MULTIPLIER, + target.getLaunchScore() / mostLaunchScore, + target.getTimeSpentScore() / mostTimeSpentScore, + target.getChooserScore() / mostChooserScore); + addDefaultSelectProbability(target); if (DEBUG) { Log.d(TAG, "Scores: " + target); } } - } - - static boolean isPersistentProcess(ResolvedComponentInfo rci) { - if (rci != null && rci.getCount() > 0) { - return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags & - ApplicationInfo.FLAG_PERSISTENT) != 0; - } - return false; + predictSelectProbabilities(mTargets); } @Override @@ -245,16 +318,16 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { // Pinned items stay stable within a normal lexical sort and ignore scoring. if (!lPinned && !rPinned) { if (mStats != null) { - final ScoredTarget lhsTarget = mScoredTargets.get(new ComponentName( + final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName( lhs.activityInfo.packageName, lhs.activityInfo.name)); - final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName( + final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName( rhs.activityInfo.packageName, rhs.activityInfo.name)); - final int selectProbDiff = Float.compare( - rhsTarget.selectProb, lhsTarget.selectProb); + final int selectProbabilityDiff = Float.compare( + rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability()); - if (selectProbDiff != 0) { - return selectProbDiff > 0 ? 1 : -1; + if (selectProbabilityDiff != 0) { + return selectProbabilityDiff > 0 ? 1 : -1; } } } @@ -268,177 +341,234 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { } public float getScore(ComponentName name) { - final ScoredTarget target = mScoredTargets.get(name); + final ResolverTarget target = mTargetsDict.get(name); if (target != null) { - return target.selectProb; + return target.getSelectProbability(); } return 0; } - static class ScoredTarget { - public final ComponentInfo componentInfo; - public long lastTimeUsed; - public long timeSpent; - public long launchCount; - public long chooserCount; - public ArrayMap<String, Float> features; - public float selectProb; - - public ScoredTarget(ComponentInfo ci) { - componentInfo = ci; - features = new ArrayMap<>(5); + public void updateChooserCounts(String packageName, int userId, String action) { + if (mUsm != null) { + mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action); } + } - @Override - public String toString() { - return "ScoredTarget{" + componentInfo - + " lastTimeUsed: " + lastTimeUsed - + " timeSpent: " + timeSpent - + " launchCount: " + launchCount - + " chooserCount: " + chooserCount - + " selectProb: " + selectProb - + "}"; + // update ranking model when the connection to it is valid. + public void updateModel(ComponentName componentName) { + synchronized (mLock) { + if (mRanker != null) { + try { + int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet()) + .indexOf(componentName); + if (selectedPos > 0) { + mRanker.train(mTargets, selectedPos); + } else { + if (DEBUG) { + Log.d(TAG, "Selected a unknown component: " + componentName); + } + } + } catch (RemoteException e) { + Log.e(TAG, "Error in Train: " + e); + } + } else { + if (DEBUG) { + Log.d(TAG, "Ranker is null; skip updateModel."); + } + } } + } - public void setFeatures(float launchCountScore, float usageTimeScore, float recencyScore, - float chooserCountScore) { - features.put(LAUNCH_SCORE, launchCountScore); - features.put(TIME_SPENT_SCORE, usageTimeScore); - features.put(RECENCY_SCORE, recencyScore); - features.put(CHOOSER_SCORE, chooserCountScore); + // unbind the service and clear unhandled messges. + public void destroy() { + mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT); + mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT); + if (mConnection != null) { + mContext.unbindService(mConnection); + mConnection.destroy(); } - - public ArrayMap<String, Float> getFeatures() { - return features; + if (DEBUG) { + Log.d(TAG, "Unbinded Resolver Ranker."); } } - public void updateChooserCounts(String packageName, int userId, String action) { - if (mUsm != null) { - mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action); + // connect to a ranking service. + private void initRanker(Context context) { + synchronized (mLock) { + if (mConnection != null && mRanker != null) { + if (DEBUG) { + Log.d(TAG, "Ranker still exists; reusing the existing one."); + } + return; + } } - } - - public void updateModel(ComponentName componentName) { - if (mScoredTargets == null || componentName == null || - !mScoredTargets.containsKey(componentName)) { + Intent intent = resolveRankerService(); + if (intent == null) { return; } - ScoredTarget selected = mScoredTargets.get(componentName); - for (ComponentName targetComponent : mScoredTargets.keySet()) { - if (targetComponent.equals(componentName)) { + mConnectSignal = new CountDownLatch(1); + mConnection = new ResolverRankerServiceConnection(mConnectSignal); + context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM); + } + + // resolve the service for ranking. + private Intent resolveRankerService() { + Intent intent = new Intent(ResolverRankerService.SERVICE_INTERFACE); + final List<ResolveInfo> resolveInfos = mPm.queryIntentServices(intent, 0); + for (ResolveInfo resolveInfo : resolveInfos) { + if (resolveInfo == null || resolveInfo.serviceInfo == null + || resolveInfo.serviceInfo.applicationInfo == null) { + if (DEBUG) { + Log.d(TAG, "Failed to retrieve a ranker: " + resolveInfo); + } continue; } - ScoredTarget target = mScoredTargets.get(targetComponent); - // A potential point of optimization. Save updates or derive a closed form for the - // positive case, to avoid calculating them repeatedly. - if (target.selectProb >= selected.selectProb) { - mRanker.update(target.getFeatures(), target.selectProb, false); - mRanker.update(selected.getFeatures(), selected.selectProb, true); + ComponentName componentName = new ComponentName( + resolveInfo.serviceInfo.applicationInfo.packageName, + resolveInfo.serviceInfo.name); + try { + final String perm = mPm.getServiceInfo(componentName, 0).permission; + if (!ResolverRankerService.BIND_PERMISSION.equals(perm)) { + Log.w(TAG, "ResolverRankerService " + componentName + " does not require" + + " permission " + ResolverRankerService.BIND_PERMISSION + + " - this service will not be queried for ResolverComparator." + + " add android:permission=\"" + + ResolverRankerService.BIND_PERMISSION + "\"" + + " to the <service> tag for " + componentName + + " in the manifest."); + continue; + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not look up service " + componentName + + "; component name not found"); + continue; + } + if (DEBUG) { + Log.d(TAG, "Succeeded to retrieve a ranker: " + componentName); } + intent.setComponent(componentName); + return intent; } - mRanker.commitUpdate(); + return null; } - class LogisticRegressionAppRanker { - private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params"; - private static final String BIAS_PREF_KEY = "bias"; - private static final String VERSION_PREF_KEY = "version"; - - // parameters for a pre-trained model, to initialize the app ranker. When updating the - // pre-trained model, please update these params, as well as initModel(). - private static final int CURRENT_VERSION = 1; - private static final float LEARNING_RATE = 0.0001f; - private static final float REGULARIZER_PARAM = 0.0001f; + // set a watchdog, to avoid waiting for ranking service for too long. + private void startWatchDog(int timeOutLimit) { + if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + timeOutLimit + "ms"); + if (mHandler == null) { + Log.d(TAG, "Error: Handler is Null; Needs to be initialized."); + } + mHandler.sendEmptyMessageDelayed(RESOLVER_RANKER_RESULT_TIMEOUT, timeOutLimit); + } - private SharedPreferences mParamSharedPref; - private ArrayMap<String, Float> mFeatureWeights; - private float mBias; + private class ResolverRankerServiceConnection implements ServiceConnection { + private final CountDownLatch mConnectSignal; - public LogisticRegressionAppRanker(Context context) { - mParamSharedPref = getParamSharedPref(context); - initModel(); + public ResolverRankerServiceConnection(CountDownLatch connectSignal) { + mConnectSignal = connectSignal; } - public float predict(ArrayMap<String, Float> target) { - if (target == null) { - return 0.0f; - } - final int featureSize = target.size(); - float sum = 0.0f; - for (int i = 0; i < featureSize; i++) { - String featureName = target.keyAt(i); - float weight = mFeatureWeights.getOrDefault(featureName, 0.0f); - sum += weight * target.valueAt(i); + public final IResolverRankerResult resolverRankerResult = + new IResolverRankerResult.Stub() { + @Override + public void sendResult(List<ResolverTarget> targets) throws RemoteException { + if (DEBUG) { + Log.d(TAG, "Sending Result back to Resolver: " + targets); + } + synchronized (mLock) { + final Message msg = Message.obtain(); + msg.what = RESOLVER_RANKER_SERVICE_RESULT; + msg.obj = targets; + mHandler.sendMessage(msg); + } } - return (float) (1.0 / (1.0 + Math.exp(-mBias - sum))); - } + }; - public void update(ArrayMap<String, Float> target, float predict, boolean isSelected) { - if (target == null) { - return; + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) { + Log.d(TAG, "onServiceConnected: " + name); } - final int featureSize = target.size(); - float error = isSelected ? 1.0f - predict : -predict; - for (int i = 0; i < featureSize; i++) { - String featureName = target.keyAt(i); - float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f); - mBias += LEARNING_RATE * error; - currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight + - LEARNING_RATE * error * target.valueAt(i); - mFeatureWeights.put(featureName, currentWeight); + synchronized (mLock) { + mRanker = IResolverRankerService.Stub.asInterface(service); + mConnectSignal.countDown(); } + } + + @Override + public void onServiceDisconnected(ComponentName name) { if (DEBUG) { - Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias); + Log.d(TAG, "onServiceDisconnected: " + name); + } + synchronized (mLock) { + destroy(); } } - public void commitUpdate() { - SharedPreferences.Editor editor = mParamSharedPref.edit(); - editor.putFloat(BIAS_PREF_KEY, mBias); - final int size = mFeatureWeights.size(); - for (int i = 0; i < size; i++) { - editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i)); + public void destroy() { + synchronized (mLock) { + mRanker = null; } - editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION); - editor.apply(); } + } - private SharedPreferences getParamSharedPref(Context context) { - // The package info in the context isn't initialized in the way it is for normal apps, - // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we - // build the path manually below using the same policy that appears in ContextImpl. + private void reset() { + mTargetsDict.clear(); + mTargets = null; + startWatchDog(WATCHDOG_TIMEOUT_MILLIS); + initRanker(mContext); + } + + // predict select probabilities if ranking service is valid. + private void predictSelectProbabilities(List<ResolverTarget> targets) { + if (mConnection == null) { if (DEBUG) { - Log.d(TAG, "Context Package Name: " + context.getPackageName()); + Log.d(TAG, "Has not found valid ResolverRankerService; Skip Prediction"); + } + return; + } else { + try { + mConnectSignal.await(CONNECTION_COST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + synchronized (mLock) { + if (mRanker != null) { + mRanker.predict(targets, mConnection.resolverRankerResult); + return; + } else { + if (DEBUG) { + Log.d(TAG, "Ranker has not been initialized; skip predict."); + } + } + } + } catch (InterruptedException e) { + Log.e(TAG, "Error in Wait for Service Connection."); + } catch (RemoteException e) { + Log.e(TAG, "Error in Predict: " + e); } - final File prefsFile = new File(new File( - Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL, - context.getUserId(), context.getPackageName()), - "shared_prefs"), - PARAM_SHARED_PREF_NAME + ".xml"); - return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE); } + mAfterCompute.afterCompute(); + } - private void initModel() { - mFeatureWeights = new ArrayMap<>(4); - if (mParamSharedPref == null || - mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) { - // Initializing the app ranker to a pre-trained model. When updating the pre-trained - // model, please increment CURRENT_VERSION, and update LEARNING_RATE and - // REGULARIZER_PARAM. - mBias = -1.6568f; - mFeatureWeights.put(LAUNCH_SCORE, 2.5543f); - mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f); - mFeatureWeights.put(RECENCY_SCORE, 0.269f); - mFeatureWeights.put(CHOOSER_SCORE, 4.2222f); - } else { - mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f); - mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f)); - mFeatureWeights.put( - TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f)); - mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f)); - mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f)); - } + // adds select prob as the default values, according to a pre-trained Logistic Regression model. + private void addDefaultSelectProbability(ResolverTarget target) { + float sum = 2.5543f * target.getLaunchScore() + 2.8412f * target.getTimeSpentScore() + + 0.269f * target.getRecencyScore() + 4.2222f * target.getChooserScore(); + target.setSelectProbability((float) (1.0 / (1.0 + Math.exp(1.6568f - sum)))); + } + + // sets features for each target + private void setFeatures(ResolverTarget target, float recencyScore, float launchScore, + float timeSpentScore, float chooserScore) { + target.setRecencyScore(recencyScore); + target.setLaunchScore(launchScore); + target.setTimeSpentScore(timeSpentScore); + target.setChooserScore(chooserScore); + } + + static boolean isPersistentProcess(ResolvedComponentInfo rci) { + if (rci != null && rci.getCount() > 0) { + return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags & + ApplicationInfo.FLAG_PERSISTENT) != 0; } + return false; } } diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 4071ff4ebd5a..e8bebb74bdd0 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -32,8 +32,10 @@ import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import java.lang.InterruptedException; import java.util.ArrayList; import java.util.Collections; +import java.util.concurrent.CountDownLatch; import java.util.List; /** @@ -205,14 +207,42 @@ public class ResolverListController { return listToReturn; } + private class ComputeCallback implements ResolverComparator.AfterCompute { + + private CountDownLatch mFinishComputeSignal; + + public ComputeCallback(CountDownLatch finishComputeSignal) { + mFinishComputeSignal = finishComputeSignal; + } + + public void afterCompute () { + mFinishComputeSignal.countDown(); + } + } + @VisibleForTesting @WorkerThread public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) { + final CountDownLatch finishComputeSignal = new CountDownLatch(1); + ComputeCallback callback = new ComputeCallback(finishComputeSignal); if (mResolverComparator == null) { - mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage); + mResolverComparator = + new ResolverComparator(mContext, mTargetIntent, mReferrerPackage, callback); + } else { + mResolverComparator.setCallBack(callback); + } + try { + long beforeRank = System.currentTimeMillis(); + mResolverComparator.compute(inputList); + finishComputeSignal.await(); + Collections.sort(inputList, mResolverComparator); + long afterRank = System.currentTimeMillis(); + if (DEBUG) { + Log.d(TAG, "Time Cost: " + Long.toString(afterRank - beforeRank)); + } + } catch (InterruptedException e) { + Log.e(TAG, "Compute & Sort was interrupted: " + e); } - mResolverComparator.compute(inputList); - Collections.sort(inputList, mResolverComparator); } private static boolean isSameResolvedComponent(ResolveInfo a, @@ -233,7 +263,7 @@ public class ResolverListController { @VisibleForTesting public float getScore(ResolverActivity.DisplayResolveInfo target) { if (mResolverComparator == null) { - mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage); + return 0.0f; } return mResolverComparator.getScore(target.getResolvedComponentName()); } @@ -249,4 +279,10 @@ public class ResolverListController { mResolverComparator.updateChooserCounts(packageName, userId, action); } } + + public void destroy() { + if (mResolverComparator != null) { + mResolverComparator.destroy(); + } + } } diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java index 1abb59b006dd..a70209c705c0 100644 --- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java +++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java @@ -74,6 +74,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame private final Rect mOldStableInsets = new Rect(); private final Rect mSystemInsets = new Rect(); private final Rect mStableInsets = new Rect(); + private final Rect mTmpRect = new Rect(); public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds, Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable, @@ -370,12 +371,6 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame DisplayListCanvas canvas = mSystemBarBackgroundNode.start(width, height); mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height); final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top); - final int bottomInset = DecorView.getColorViewBottomInset(stableInsets.bottom, - systemInsets.bottom); - final int rightInset = DecorView.getColorViewRightInset(stableInsets.right, - systemInsets.right); - final int leftInset = DecorView.getColorViewLeftInset(stableInsets.left, - systemInsets.left); if (mStatusBarColor != null) { mStatusBarColor.setBounds(0, 0, left + width, topInset); mStatusBarColor.draw(canvas); @@ -385,14 +380,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame // don't want the navigation bar background be moving around when resizing in docked mode. // However, we need it for the transitions into/out of docked mode. if (mNavigationBarColor != null && fullscreen) { - final int size = DecorView.getNavBarSize(bottomInset, rightInset, leftInset); - if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) { - mNavigationBarColor.setBounds(width - size, 0, width, height); - } else if (DecorView.isNavBarToLeftEdge(bottomInset, leftInset)) { - mNavigationBarColor.setBounds(0, 0, size, height); - } else { - mNavigationBarColor.setBounds(0, height - size, width, height); - } + DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect); + mNavigationBarColor.setBounds(mTmpRect); mNavigationBarColor.draw(canvas); } mSystemBarBackgroundNode.end(canvas); diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index a8e16c96acfa..653796dc39e7 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -119,6 +119,21 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // The height of a window which has not in DIP. private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5; + public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES = + new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, + Gravity.TOP, Gravity.LEFT, Gravity.RIGHT, + Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, + com.android.internal.R.id.statusBarBackground, + FLAG_FULLSCREEN); + + public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES = + new ColorViewAttributes( + SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION, + Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT, + Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, + com.android.internal.R.id.navigationBarBackground, + 0 /* hideWindowFlag */); + // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer // size calculation takes the shadow size into account. We set the elevation currently // to max until the first layout command has been executed. @@ -162,18 +177,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // View added at runtime to draw under the navigation bar area private View mNavigationGuard; - private final ColorViewState mStatusColorViewState = new ColorViewState( - SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, - Gravity.TOP, Gravity.LEFT, Gravity.RIGHT, - Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, - com.android.internal.R.id.statusBarBackground, - FLAG_FULLSCREEN); - private final ColorViewState mNavigationColorViewState = new ColorViewState( - SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION, - Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT, - Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, - com.android.internal.R.id.navigationBarBackground, - 0 /* hideWindowFlag */); + private final ColorViewState mStatusColorViewState = + new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES); + private final ColorViewState mNavigationColorViewState = + new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES); private final Interpolator mShowInterpolator; private final Interpolator mHideInterpolator; @@ -983,35 +990,50 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return false; } - static int getColorViewTopInset(int stableTop, int systemTop) { + public static int getColorViewTopInset(int stableTop, int systemTop) { return Math.min(stableTop, systemTop); } - static int getColorViewBottomInset(int stableBottom, int systemBottom) { + public static int getColorViewBottomInset(int stableBottom, int systemBottom) { return Math.min(stableBottom, systemBottom); } - static int getColorViewRightInset(int stableRight, int systemRight) { + public static int getColorViewRightInset(int stableRight, int systemRight) { return Math.min(stableRight, systemRight); } - static int getColorViewLeftInset(int stableLeft, int systemLeft) { + public static int getColorViewLeftInset(int stableLeft, int systemLeft) { return Math.min(stableLeft, systemLeft); } - static boolean isNavBarToRightEdge(int bottomInset, int rightInset) { + public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) { return bottomInset == 0 && rightInset > 0; } - static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) { + public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) { return bottomInset == 0 && leftInset > 0; } - static int getNavBarSize(int bottomInset, int rightInset, int leftInset) { + public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) { return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset; } + public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets, + Rect contentInsets, Rect outRect) { + final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom); + final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left); + final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right); + final int size = getNavBarSize(bottomInset, rightInset, leftInset); + if (isNavBarToRightEdge(bottomInset, rightInset)) { + outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight); + } else if (isNavBarToLeftEdge(bottomInset, leftInset)) { + outRect.set(0, 0, size, canvasHeight); + } else { + outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight); + } + } + WindowInsets updateColorViews(WindowInsets insets, boolean animate) { WindowManager.LayoutParams attrs = mWindow.getAttributes(); int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility(); @@ -1131,9 +1153,14 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } private int calculateStatusBarColor() { - int flags = mWindow.getAttributes().flags; - return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? mSemiTransparentStatusBarColor - : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? mWindow.mStatusBarColor + return calculateStatusBarColor(mWindow.getAttributes().flags, + mSemiTransparentStatusBarColor, mWindow.mStatusBarColor); + } + + public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor, + int statusBarColor) { + return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor + : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor : Color.BLACK; } @@ -1160,13 +1187,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color, int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate, boolean force) { - state.present = (sysUiVis & state.systemUiHideFlag) == 0 - && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0 - && ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 - || force); - boolean show = state.present - && (color & Color.BLACK) != 0 - && ((mWindow.getAttributes().flags & state.translucentFlag) == 0 || force); + state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force); + boolean show = state.attributes.isVisible(state.present, color, + mWindow.getAttributes().flags, force); boolean showView = show && !isResizing() && size > 0; boolean visibilityChanged = false; @@ -1175,15 +1198,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size; int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT; int resolvedGravity = verticalBar - ? (seascape ? state.seascapeGravity : state.horizontalGravity) - : state.verticalGravity; + ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity) + : state.attributes.verticalGravity; if (view == null) { if (showView) { state.view = view = new View(mContext); view.setBackgroundColor(color); - view.setTransitionName(state.transitionName); - view.setId(state.id); + view.setTransitionName(state.attributes.transitionName); + view.setId(state.attributes.id); visibilityChanged = true; view.setVisibility(INVISIBLE); state.targetVisibility = VISIBLE; @@ -2269,6 +2292,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind boolean visible; int color; + final ColorViewAttributes attributes; + + ColorViewState(ColorViewAttributes attributes) { + this.attributes = attributes; + } + } + + public static class ColorViewAttributes { + final int id; final int systemUiHideFlag; final int translucentFlag; @@ -2278,9 +2310,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind final String transitionName; final int hideWindowFlag; - ColorViewState(int systemUiHideFlag, - int translucentFlag, int verticalGravity, int horizontalGravity, - int seascapeGravity, String transitionName, int id, int hideWindowFlag) { + private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity, + int horizontalGravity, int seascapeGravity, String transitionName, int id, + int hideWindowFlag) { this.id = id; this.systemUiHideFlag = systemUiHideFlag; this.translucentFlag = translucentFlag; @@ -2290,6 +2322,24 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind this.transitionName = transitionName; this.hideWindowFlag = hideWindowFlag; } + + public boolean isPresent(int sysUiVis, int windowFlags, boolean force) { + return (sysUiVis & systemUiHideFlag) == 0 + && (windowFlags & hideWindowFlag) == 0 + && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 + || force); + } + + public boolean isVisible(boolean present, int color, int windowFlags, boolean force) { + return present + && (color & Color.BLACK) != 0 + && ((windowFlags & translucentFlag) == 0 || force); + } + + public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) { + final boolean present = isPresent(sysUiVis, windowFlags, force); + return isVisible(present, color, windowFlags, force); + } } /** diff --git a/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java b/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java new file mode 100644 index 000000000000..e40090f63a76 --- /dev/null +++ b/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.internal.view; + +import android.os.Handler; +import android.os.Message; +import android.view.Choreographer; +import android.view.Display; + +/** + * Utility class to schedule things at vsync-sf instead of vsync-app + * @hide + */ +public class SurfaceFlingerVsyncChoreographer { + + private static final long ONE_MS_IN_NS = 1000000; + private static final long ONE_S_IN_NS = ONE_MS_IN_NS * 1000; + + private final Handler mHandler; + private final Choreographer mChoreographer = Choreographer.getInstance(); + + /** + * The offset between vsync-app and vsync-surfaceflinger. See + * {@link #calculateAppSurfaceFlingerVsyncOffsetMs} why this is necessary. + */ + private long mSurfaceFlingerOffsetMs; + + public SurfaceFlingerVsyncChoreographer(Handler handler, Display display) { + mHandler = handler; + mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs(display); + } + + public long getSurfaceFlingerOffsetMs() { + return mSurfaceFlingerOffsetMs; + } + + /** + * This method calculates the offset between vsync-surfaceflinger and vsync-app. If vsync-app + * is a couple of milliseconds before vsync-sf, a touch or animation event that causes a surface + * flinger transaction are sometimes processed before the vsync-sf tick, and sometimes after, + * which leads to jank. Figure out this difference here and then post all the touch/animation + * events to start being processed at vsync-sf. + * + * @return The offset between vsync-app and vsync-sf, or 0 if vsync app happens after vsync-sf. + */ + private long calculateAppSurfaceFlingerVsyncOffsetMs(Display display) { + + // Calculate vsync offset from SurfaceFlinger. + // See frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:getDisplayConfigs + long vsyncPeriod = (long) (ONE_S_IN_NS / display.getRefreshRate()); + long sfVsyncOffset = vsyncPeriod - (display.getPresentationDeadlineNanos() - ONE_MS_IN_NS); + return Math.max(0, (sfVsyncOffset - display.getAppVsyncOffsetNanos()) / ONE_MS_IN_NS); + } + + public void scheduleAtSfVsync(Runnable r) { + final long delay = calculateDelay(); + if (delay <= 0) { + r.run(); + } else { + mHandler.postDelayed(r, delay); + } + } + + public void scheduleAtSfVsync(Handler h, Message m) { + final long delay = calculateDelay(); + if (delay <= 0) { + h.handleMessage(m); + } else { + m.setAsynchronous(true); + h.sendMessageDelayed(m, delay); + } + } + + private long calculateDelay() { + final long sinceFrameStart = System.nanoTime() - mChoreographer.getLastFrameTimeNanos(); + return mSurfaceFlingerOffsetMs - sinceFrameStart / 1000000; + } +} diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 4e68602b7712..3010dc1699e1 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -245,8 +245,7 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b } sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); - addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); - return true; + return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } static void FontFamily_addAxisValue(jlong builderPtr, jint tag, jfloat value) { diff --git a/core/jni/android/graphics/FontUtils.cpp b/core/jni/android/graphics/FontUtils.cpp index 91fec2a75e2c..3bcf0c7fb354 100644 --- a/core/jni/android/graphics/FontUtils.cpp +++ b/core/jni/android/graphics/FontUtils.cpp @@ -55,7 +55,7 @@ void init_FontUtils(JNIEnv* env) { gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;"); gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I"); - jclass axisClass = FindClassOrDie(env, "android/text/FontConfig$Axis"); + jclass axisClass = FindClassOrDie(env, "android/graphics/fonts/FontVariationAxis"); gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "mTag", "I"); gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "mStyleValue", "F"); } diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index a77ed626d0e8..1a353302d7d8 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -10,6 +10,14 @@ using namespace android::uirenderer; +/** + * By default Skia gradients will interpolate their colors in unpremul space + * and then premultiply each of the results. We must set this flag to preserve + * backwards compatiblity by premultiplying the colors of the gradient first, + * and then interpolating between them. + */ +static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag; + static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) { if (NULL == ptr) { doThrowIAE(env); @@ -89,7 +97,7 @@ static jlong LinearGradient_create1(JNIEnv* env, jobject o, jlong matrixPtr, SkShader* shader = SkGradientShader::MakeLinear(pts, reinterpret_cast<const SkColor*>(colorValues), pos, count, - static_cast<SkShader::TileMode>(tileMode), /* flags */ 0, matrix).release(); + static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release(); env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT); ThrowIAE_IfNull(env, shader); @@ -109,7 +117,7 @@ static jlong LinearGradient_create2(JNIEnv* env, jobject o, jlong matrixPtr, colors[1] = color1; SkShader* s = SkGradientShader::MakeLinear(pts, colors, NULL, 2, - (SkShader::TileMode)tileMode, /* flags */ 0, matrix).release(); + static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release(); ThrowIAE_IfNull(env, s); return reinterpret_cast<jlong>(s); @@ -135,7 +143,7 @@ static jlong RadialGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloa SkShader* shader = SkGradientShader::MakeRadial(center, radius, reinterpret_cast<const SkColor*>(colorValues), pos, count, - static_cast<SkShader::TileMode>(tileMode), /* flags */ 0, matrix).release(); + static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release(); env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT); @@ -154,7 +162,7 @@ static jlong RadialGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloa colors[1] = color1; SkShader* s = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2, - (SkShader::TileMode)tileMode, /* flags */ 0, matrix).release(); + static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release(); ThrowIAE_IfNull(env, s); return reinterpret_cast<jlong>(s); } @@ -174,8 +182,8 @@ static jlong SweepGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloat #error Need to convert float array to SkScalar array before calling the following function. #endif - SkShader* shader = SkGradientShader::MakeSweep(x, y, - reinterpret_cast<const SkColor*>(colors), pos, count, /* flags */ 0, matrix).release(); + SkShader* shader = SkGradientShader::MakeSweep(x, y, reinterpret_cast<const SkColor*>(colors), + pos, count, sGradientShaderFlags, matrix).release(); env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors), JNI_ABORT); ThrowIAE_IfNull(env, shader); @@ -189,7 +197,7 @@ static jlong SweepGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat colors[0] = color0; colors[1] = color1; SkShader* s = SkGradientShader::MakeSweep(x, y, colors, NULL, 2, - /* flags */ 0, matrix).release(); + sGradientShaderFlags, matrix).release(); ThrowIAE_IfNull(env, s); return reinterpret_cast<jlong>(s); } diff --git a/core/jni/android_hardware_UsbRequest.cpp b/core/jni/android_hardware_UsbRequest.cpp index 4b7e0dd562b2..01fe078f6f16 100644 --- a/core/jni/android_hardware_UsbRequest.cpp +++ b/core/jni/android_hardware_UsbRequest.cpp @@ -167,7 +167,7 @@ android_hardware_UsbRequest_queue_direct(JNIEnv *env, jobject thiz, } static jboolean -android_hardware_UsbRequest_enqueue(JNIEnv *env, jobject thiz, jobject buffer, jint offset, +android_hardware_UsbRequest_queue(JNIEnv *env, jobject thiz, jobject buffer, jint offset, jint length) { struct usb_request* request = get_request_from_object(env, thiz); @@ -226,8 +226,8 @@ static const JNINativeMethod method_table[] = { {"native_init", "(Landroid/hardware/usb/UsbDeviceConnection;IIII)Z", (void *)android_hardware_UsbRequest_init}, {"native_close", "()V", (void *)android_hardware_UsbRequest_close}, - {"native_enqueue", "(Ljava/nio/ByteBuffer;II)Z", - (void *)android_hardware_UsbRequest_enqueue}, + {"native_queue", "(Ljava/nio/ByteBuffer;II)Z", + (void *)android_hardware_UsbRequest_queue}, {"native_queue_array", "([BIZ)Z", (void *)android_hardware_UsbRequest_queue_array}, {"native_dequeue_array", "([BIZ)I", (void *)android_hardware_UsbRequest_dequeue_array}, {"native_queue_direct", "(Ljava/nio/ByteBuffer;IZ)Z", diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 713287e4cd7a..5839fd50d79a 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -101,17 +101,7 @@ sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) { return sur; } -jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env, - const sp<IGraphicBufferProducer>& bufferProducer) { - if (bufferProducer == NULL) { - return NULL; - } - - sp<Surface> surface(new Surface(bufferProducer, true)); - if (surface == NULL) { - return NULL; - } - +jobject android_view_Surface_createFromSurface(JNIEnv* env, const sp<Surface>& surface) { jobject surfaceObj = env->NewObject(gSurfaceClassInfo.clazz, gSurfaceClassInfo.ctor, (jlong)surface.get()); if (surfaceObj == NULL) { @@ -126,6 +116,16 @@ jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env, return surfaceObj; } +jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env, + const sp<IGraphicBufferProducer>& bufferProducer) { + if (bufferProducer == NULL) { + return NULL; + } + + sp<Surface> surface(new Surface(bufferProducer, true)); + return android_view_Surface_createFromSurface(env, surface); +} + int android_view_Surface_mapPublicFormatToHalFormat(PublicFormat f) { switch(f) { diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 0ab27f2e0f8d..b95258bca49c 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -346,6 +346,11 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, return false; } + // Handle force_mount_namespace with MOUNT_EXTERNAL_NONE. + if (mount_mode == MOUNT_EXTERNAL_NONE) { + return true; + } + if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) { ALOGW("Failed to mount %s to /storage: %s", storageSource.string(), strerror(errno)); diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h index 3f1bdff81aef..2641ab8f0337 100644 --- a/core/jni/include/android_runtime/android_view_Surface.h +++ b/core/jni/include/android_runtime/android_view_Surface.h @@ -69,6 +69,10 @@ extern bool android_view_Surface_isInstanceOf(JNIEnv* env, jobject obj); /* Gets the underlying Surface from a Surface Java object. */ extern sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj); +/* Creates a Surface from an android::Surface. */ +extern jobject android_view_Surface_createFromSurface(JNIEnv* env, + const sp<Surface>& surface); + /* Creates a Surface from an IGraphicBufferProducer. */ extern jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env, const sp<IGraphicBufferProducer>& bufferProducer); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ffcb84762eb5..0a50048c5149 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3129,6 +3129,15 @@ <permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE" android:protectionLevel="signature" /> + <!-- @SystemApi Must be required by services that extend + {@link android.service.resolver.ResolverRankerService}, to ensure that only the system can + bind to them. + <p>Protection level: signature + @hide + --> + <permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE" + android:protectionLevel="signature" /> + <!-- Must be required by a {@link android.service.notification.ConditionProviderService}, to ensure that only the system can bind to it. @@ -3346,7 +3355,8 @@ android:documentLaunchMode="never" android:relinquishTaskIdentity="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> <intent-filter> <action android:name="android.intent.action.CHOOSER" /> <category android:name="android.intent.category.DEFAULT" /> @@ -3360,7 +3370,8 @@ android:documentLaunchMode="never" android:relinquishTaskIdentity="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> <intent-filter> <action android:name="android.intent.action.CHOOSE_ACCESSIBILITY_BUTTON" /> <category android:name="android.intent.category.DEFAULT" /> @@ -3421,7 +3432,8 @@ android:exported="true" android:theme="@style/Theme.DeviceDefault.Light.Dialog" android:label="@string/choose_account_label" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> </activity> <activity android:name="android.accounts.ChooseTypeAndAccountActivity" @@ -3429,14 +3441,16 @@ android:exported="true" android:theme="@style/Theme.DeviceDefault.Light.Dialog" android:label="@string/choose_account_label" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> </activity> <activity android:name="android.accounts.ChooseAccountTypeActivity" android:excludeFromRecents="true" android:theme="@style/Theme.DeviceDefault.Light.Dialog" android:label="@string/choose_account_label" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> </activity> <activity android:name="android.accounts.CantAddAccountActivity" @@ -3450,7 +3464,8 @@ android:excludeFromRecents="true" android:exported="true" android:theme="@style/Theme.DeviceDefault.Light.DialogWhenLarge" - android:process=":ui"> + android:process=":ui" + android:visibleToInstantApps="true"> </activity> <activity android:name="android.content.SyncActivityTooManyDeletes" @@ -3634,6 +3649,14 @@ android:permission="android.permission.BIND_JOB_SERVICE" > </service> + <service android:name="com.android.internal.app.LRResolverRankerService" + android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE" + android:exported="false" + android:priority="-1" > + <intent-filter> + <action android:name="android.service.resolver.ResolverRankerService" /> + </intent-filter> + </service> </application> </manifest> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 554f50c23daa..ee73b6983888 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8566,6 +8566,11 @@ <!-- @hide From Theme.colorBackground, used for the TaskDescription background color. --> <attr name="colorBackground" /> + <!-- @hide From Theme.statusBarColor, used for the TaskDescription status bar color. --> + <attr name="statusBarColor"/> + <!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar + color. --> + <attr name="navigationBarColor"/> </declare-styleable> <declare-styleable name="Shortcut"> diff --git a/core/tests/coretests/src/android/graphics/VariationParserTest.java b/core/tests/coretests/src/android/graphics/VariationParserTest.java deleted file mode 100644 index fdabb130115c..000000000000 --- a/core/tests/coretests/src/android/graphics/VariationParserTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.graphics; - -import android.test.suitebuilder.annotation.SmallTest; -import android.text.FontConfig; - -import junit.framework.TestCase; - -import java.util.List; - - -public class VariationParserTest extends TestCase { - - @SmallTest - public void testParseFontVariationSetting() { - int tag = FontListParser.makeTag("wdth"); - List<FontConfig.Axis> axes = FontListParser.parseFontVariationSettings("'wdth' 1"); - assertEquals(tag, axes.get(0).getTag()); - assertEquals(1.0f, axes.get(0).getStyleValue()); - - axes = FontListParser.parseFontVariationSettings("\"wdth\" 100"); - assertEquals(tag, axes.get(0).getTag()); - assertEquals(100.0f, axes.get(0).getStyleValue()); - - axes = FontListParser.parseFontVariationSettings(" 'wdth' 100"); - assertEquals(tag, axes.get(0).getTag()); - assertEquals(100.0f, axes.get(0).getStyleValue()); - - axes = FontListParser.parseFontVariationSettings("\t'wdth' 0.5"); - assertEquals(tag, axes.get(0).getTag()); - assertEquals(0.5f, axes.get(0).getStyleValue()); - - tag = FontListParser.makeTag("AX "); - axes = FontListParser.parseFontVariationSettings("'AX ' 1"); - assertEquals(tag, axes.get(0).getTag()); - assertEquals(1.0f, axes.get(0).getStyleValue()); - - axes = FontListParser.parseFontVariationSettings("'AX '\t1"); - assertEquals(tag, axes.get(0).getTag()); - assertEquals(1.0f, axes.get(0).getStyleValue()); - - axes = FontListParser.parseFontVariationSettings("'AX '\n1"); - assertEquals(tag, axes.get(0).getTag()); - assertEquals(1.0f, axes.get(0).getStyleValue()); - - axes = FontListParser.parseFontVariationSettings("'AX '\r1"); - assertEquals(tag, axes.get(0).getTag()); - assertEquals(1.0f, axes.get(0).getStyleValue()); - - axes = FontListParser.parseFontVariationSettings("'AX '\r\t\n 1"); - assertEquals(tag, axes.get(0).getTag()); - assertEquals(1.0f, axes.get(0).getStyleValue()); - - // Test for invalid input - axes = FontListParser.parseFontVariationSettings(""); - assertEquals(0, axes.size()); - axes = FontListParser.parseFontVariationSettings("invalid_form"); - assertEquals(0, axes.size()); - - // Test with invalid tag - axes = FontListParser.parseFontVariationSettings("'' 1"); - assertEquals(0, axes.size()); - axes = FontListParser.parseFontVariationSettings("'invalid' 1"); - assertEquals(0, axes.size()); - - // Test with invalid styleValue - axes = FontListParser.parseFontVariationSettings("'wdth' "); - assertEquals(0, axes.size()); - axes = FontListParser.parseFontVariationSettings("'wdth' x"); - assertEquals(0, axes.size()); - axes = FontListParser.parseFontVariationSettings("'wdth' \t"); - assertEquals(0, axes.size()); - axes = FontListParser.parseFontVariationSettings("'wdth' \n\r"); - assertEquals(0, axes.size()); - } - - @SmallTest - public void testParseFontVariationStyleSettings() { - List<FontConfig.Axis> axes = - FontListParser.parseFontVariationSettings("'wdth' 10,'AX '\r1"); - int tag1 = FontListParser.makeTag("wdth"); - int tag2 = FontListParser.makeTag("AX "); - assertEquals(tag1, axes.get(0).getTag()); - assertEquals(10.0f, axes.get(0).getStyleValue()); - assertEquals(tag2, axes.get(1).getTag()); - assertEquals(1.0f, axes.get(1).getStyleValue()); - - // Test only spacers are allowed before tag - axes = FontListParser.parseFontVariationSettings(" 'wdth' 10,ab'wdth' 1"); - tag1 = FontListParser.makeTag("wdth"); - assertEquals(tag1, axes.get(0).getTag()); - assertEquals(10.0f, axes.get(0).getStyleValue()); - assertEquals(1, axes.size()); - } - - @SmallTest - public void testInvalidTagCharacters() { - List<FontConfig.Axis> axes = - FontListParser.parseFontVariationSettings("'\u0000\u0000\u0000\u0000' 10"); - assertEquals(0, axes.size()); - axes = FontListParser.parseFontVariationSettings("'\u3042\u3044\u3046\u3048' 10"); - assertEquals(0, axes.size()); - } - - @SmallTest - public void testMakeTag() { - assertEquals(0x77647468, FontListParser.makeTag("wdth")); - assertEquals(0x41582020, FontListParser.makeTag("AX ")); - assertEquals(0x20202020, FontListParser.makeTag(" ")); - } -} diff --git a/core/tests/coretests/src/android/text/VariationParserTest.java b/core/tests/coretests/src/android/text/VariationParserTest.java new file mode 100644 index 000000000000..bcc47e1fc201 --- /dev/null +++ b/core/tests/coretests/src/android/text/VariationParserTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import android.graphics.fonts.FontVariationAxis; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +public class VariationParserTest extends TestCase { + private static final String[] INVALID_STYLE_VALUES = { + "", "x", "\t", "\n" + }; + + @SmallTest + public void testFromFontVariationSetting_InvalidStyleValue() { + // Test with invalid styleValue + for (String invalidStyle : INVALID_STYLE_VALUES) { + try { + FontVariationAxis.fromFontVariationSettings("'wdth' " + invalidStyle); + fail(); + } catch (FontVariationAxis.InvalidFormatException e) { + // pass + } + } + for (String invalidStyle : INVALID_STYLE_VALUES) { + try { + FontVariationAxis.fromFontVariationSettings("'wght' 1, 'wdth' " + invalidStyle); + fail(); + } catch (FontVariationAxis.InvalidFormatException e) { + // pass + } + } + } + + @SmallTest + public void testOpenTypeTagValue() throws FontVariationAxis.InvalidFormatException { + assertEquals(0x77647468, (new FontVariationAxis("wdth", 0).getOpenTypeTagValue())); + assertEquals(0x41582020, (new FontVariationAxis("AX ", 0).getOpenTypeTagValue())); + assertEquals(0x20202020, (new FontVariationAxis(" ", 0).getOpenTypeTagValue())); + } +} diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 344f3c83ba6c..86ab3dc227f4 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -181,6 +181,9 @@ <allow-in-power-save package="com.android.cellbroadcastreceiver" /> <allow-in-power-save package="com.android.shell" /> + <!-- STOPSHIP(b/36856786): Revert this once it is fixed properly --> + <allow-in-power-save package="com.google.android.apps.enterprise.dmagent" /> + <!-- These are the packages that are white-listed to be able to run as system user --> <system-user-whitelisted-app package="com.android.settings" /> diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 76c29a9bf971..3dab1f7e20e1 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -3,15 +3,10 @@ WARNING: Parsing of this file by third-party apps is not supported. The file, and the font files it refers to, will be renamed and/or moved out from their respective location in the next Android release, and/or the - format or syntax of the file may change significantly. You must not - parse this file for information about system fonts. Instead, you must - call android.text.FontManager#getSystemFonts(). For example, it can be - called as context.getSystemService(FontManager.class).getSystemFonts(). - Note that the returned FontConfig includes data on all the defined font - families and all the details about weight, style, etc. It also provides - an open file descriptor to each font file. Note that callers of the API - should ensure they close the given file descriptors once they are done - using them. + format or syntax of the file may change significantly. If you parse this + file for information about system fonts, do it at your own risk. Your + application will almost certainly break with the next major Android + release. In this file, all fonts without names are added to the default list. Fonts are chosen based on a match: full BCP-47 language tag including diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index 6214ba6741c9..4b1b0c641807 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -17,6 +17,7 @@ package android.graphics; import android.content.res.AssetManager; +import android.graphics.fonts.FontVariationAxis; import android.text.FontConfig; import android.util.Log; import dalvik.annotation.optimization.CriticalNative; @@ -81,7 +82,7 @@ public class FontFamily { } } - public boolean addFont(String path, int ttcIndex, FontConfig.Axis[] axes, int weight, + public boolean addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight, int italic) { if (mBuilderPtr == 0) { throw new IllegalStateException("Unable to call addFont after freezing."); @@ -91,8 +92,8 @@ public class FontFamily { long fontSize = fileChannel.size(); ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); if (axes != null) { - for (FontConfig.Axis axis : axes) { - nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue()); + for (FontVariationAxis axis : axes) { + nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue()); } } return nAddFont(mBuilderPtr, fontBuffer, ttcIndex, weight, italic); @@ -102,14 +103,14 @@ public class FontFamily { } } - public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontConfig.Axis[] axes, + public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes, int weight, int italic) { if (mBuilderPtr == 0) { throw new IllegalStateException("Unable to call addFontWeightStyle after freezing."); } if (axes != null) { - for (FontConfig.Axis axis : axes) { - nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue()); + for (FontVariationAxis axis : axes) { + nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue()); } } return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, weight, italic); @@ -129,13 +130,13 @@ public class FontFamily { */ public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic, - FontConfig.Axis[] axes) { + FontVariationAxis[] axes) { if (mBuilderPtr == 0) { throw new IllegalStateException("Unable to call addFontFromAsset after freezing."); } if (axes != null) { - for (FontConfig.Axis axis : axes) { - nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue()); + for (FontVariationAxis axis : axes) { + nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue()); } } return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight, diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index ff9f11dae4dc..1a4f225c6267 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -17,6 +17,7 @@ package android.graphics; import android.text.FontConfig; +import android.graphics.fonts.FontVariationAxis; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; @@ -50,72 +51,6 @@ public class FontListParser { } } - // Note that a well-formed variation contains a four-character tag and a float as styleValue, - // with spacers in between. The tag is enclosd either by double quotes or single quotes. - @VisibleForTesting - public static ArrayList<FontConfig.Axis> parseFontVariationSettings(@Nullable String settings) { - ArrayList<FontConfig.Axis> axisList = new ArrayList<>(); - if (settings == null) { - return axisList; - } - String[] settingList = settings.split(","); - settingLoop: - for (String setting : settingList) { - int pos = 0; - while (pos < setting.length()) { - char c = setting.charAt(pos); - if (c == '\'' || c == '"') { - break; - } else if (!isSpacer(c)) { - continue settingLoop; // Only spacers are allowed before tag appeared. - } - pos++; - } - if (pos + 7 > setting.length()) { - continue; // 7 is the minimum length of tag-style value pair text. - } - if (setting.charAt(pos) != setting.charAt(pos + 5)) { - continue; // Tag should be wrapped with double or single quote. - } - String tagString = setting.substring(pos + 1, pos + 5); - if (!TAG_PATTERN.matcher(tagString).matches()) { - continue; // Skip incorrect format tag. - } - pos += 6; - while (pos < setting.length()) { - if (!isSpacer(setting.charAt(pos++))) { - break; // Skip spacers between the tag and the styleValue. - } - } - // Skip invalid styleValue - float styleValue; - String valueString = setting.substring(pos - 1); - if (!STYLE_VALUE_PATTERN.matcher(valueString).matches()) { - continue; // Skip incorrect format styleValue. - } - try { - styleValue = Float.parseFloat(valueString); - } catch (NumberFormatException e) { - continue; // ignoreing invalid number format - } - int tag = makeTag(tagString); - axisList.add(new FontConfig.Axis(tag, styleValue)); - } - return axisList; - } - - public static int makeTag(String tagString) { - char c1 = tagString.charAt(0); - char c2 = tagString.charAt(1); - char c3 = tagString.charAt(2); - char c4 = tagString.charAt(3); - return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; - } - - private static boolean isSpacer(char c) { - return c == ' ' || c == '\r' || c == '\t' || c == '\n'; - } - private static FontConfig readFamilies(XmlPullParser parser) throws XmlPullParserException, IOException { List<FontConfig.Family> families = new ArrayList<>(); @@ -172,7 +107,7 @@ public class FontListParser { throws XmlPullParserException, IOException { String indexStr = parser.getAttributeValue(null, "index"); int index = indexStr == null ? 0 : Integer.parseInt(indexStr); - List<FontConfig.Axis> axes = new ArrayList<FontConfig.Axis>(); + List<FontVariationAxis> axes = new ArrayList<FontVariationAxis>(); String weightStr = parser.getAttributeValue(null, "weight"); int weight = weightStr == null ? 400 : Integer.parseInt(weightStr); boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style")); @@ -191,47 +126,20 @@ public class FontListParser { } String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); return new FontConfig.Font(sanitizedName, index, - axes.toArray(new FontConfig.Axis[axes.size()]), weight, isItalic); + axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic); } - /** The 'tag' attribute value is read as four character values between U+0020 and U+007E - * inclusive. - */ - private static final Pattern TAG_PATTERN = Pattern.compile("[\\x20-\\x7E]{4}"); - - public static boolean isValidTag(String tagString) { - if (tagString == null || tagString.length() != 4) { - return false; - } - return TAG_PATTERN.matcher(tagString).matches(); - } - - /** The 'styleValue' attribute has an optional leading '-', followed by '<digits>', - * '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9]. - */ - private static final Pattern STYLE_VALUE_PATTERN = - Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))"); - - private static FontConfig.Axis readAxis(XmlPullParser parser) + private static FontVariationAxis readAxis(XmlPullParser parser) throws XmlPullParserException, IOException { - int tag = 0; String tagStr = parser.getAttributeValue(null, "tag"); - if (isValidTag(tagStr)) { - tag = makeTag(tagStr); - } else { - throw new XmlPullParserException("Invalid tag attribute value.", parser, null); - } - - float styleValue = 0; String styleValueStr = parser.getAttributeValue(null, "stylevalue"); - if (styleValueStr != null && STYLE_VALUE_PATTERN.matcher(styleValueStr).matches()) { - styleValue = Float.parseFloat(styleValueStr); - } else { - throw new XmlPullParserException("Invalid styleValue attribute value.", parser, null); - } - skip(parser); // axis tag is empty, ignore any contents and consume end tag - return new FontConfig.Axis(tag, styleValue); + try { + return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr)); + } catch (FontVariationAxis.InvalidFormatException e) { + // Treat as system failure since system preinstalled font setting has invalid format. + throw new RuntimeException(e); + } } private static FontConfig.Alias readAlias(XmlPullParser parser) diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index c4f7dc39b4bd..828729a064e9 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -20,6 +20,7 @@ import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Size; import android.graphics.FontListParser; +import android.graphics.fonts.FontVariationAxis; import android.os.LocaleList; import android.text.FontConfig; import android.text.GraphicsOperations; @@ -1551,8 +1552,11 @@ public class Paint { * @return true if the given settings is effective to at least one font file underlying this * typeface. This function also returns true for empty settings string. Otherwise * returns false + * @throws FontVariationAxis.InvalidFormatException + * If given string is not a valid font variation settings format. */ - public boolean setFontVariationSettings(String settings) { + public boolean setFontVariationSettings(String settings) + throws FontVariationAxis.InvalidFormatException { settings = TextUtils.nullIfEmpty(settings); if (settings == mFontVariationSettings || (settings != null && settings.equals(mFontVariationSettings))) { @@ -1566,11 +1570,10 @@ public class Paint { return true; } - final ArrayList<FontConfig.Axis> axes = FontListParser.parseFontVariationSettings(settings); - final ArrayList<FontConfig.Axis> filteredAxes = new ArrayList<FontConfig.Axis>(); - for (int i = 0; i < axes.size(); ++i) { - final FontConfig.Axis axis = axes.get(i); - if (mTypeface.isSupportedAxes(axis.getTag())) { + FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(settings); + final ArrayList<FontVariationAxis> filteredAxes = new ArrayList<FontVariationAxis>(); + for (final FontVariationAxis axis : axes) { + if (mTypeface.isSupportedAxes(axis.getOpenTypeTagValue())) { filteredAxes.add(axis); } } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 8c3a2e8068ca..560d29f1934d 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -32,6 +32,7 @@ import android.content.res.AssetManager; import android.graphics.FontListParser; import android.graphics.fonts.FontRequest; import android.graphics.fonts.FontResult; +import android.graphics.fonts.FontVariationAxis; import android.os.Bundle; import android.os.Handler; import android.os.ParcelFileDescriptor; @@ -358,11 +359,15 @@ public class Typeface { FileChannel.MapMode.READ_ONLY, 0, fontSize); int style = result.getStyle(); int weight = (style & BOLD) != 0 ? 700 : 400; - final ArrayList<FontConfig.Axis> axes = FontListParser.parseFontVariationSettings( - result.getFontVariationSettings()); - if (!fontFamily.addFontFromBuffer(fontBuffer, result.getTtcIndex(), - axes.toArray(new FontConfig.Axis[axes.size()]), weight, - (style & ITALIC) == 0 ? Builder.NORMAL : Builder.ITALIC)) { + FontVariationAxis[] axes = null; + try { + axes = FontVariationAxis.fromFontVariationSettings( + result.getFontVariationSettings()); + } catch (FontVariationAxis.InvalidFormatException e) { + // TODO: Nice to pass FontVariationAxis[] directly instead of string. + } + if (!fontFamily.addFontFromBuffer(fontBuffer, result.getTtcIndex(), axes, weight, + (style & ITALIC) == 0 ? Builder.NORMAL : Builder.ITALIC)) { Log.e(TAG, "Error creating font " + request.getQuery()); callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); @@ -514,7 +519,7 @@ public class Typeface { public static final int ITALIC = 1; private int mTtcIndex; - private FontConfig.Axis[] mAxes; + private FontVariationAxis[] mAxes; private AssetManager mAssetManager; private String mPath; @@ -682,15 +687,16 @@ public class Typeface { * Sets a font variation settings. * * @param variationSettings See {@link android.widget.TextView#setFontVariationSettings}. + * @throws FontVariationAxis.InvalidFormatException If given string is not a valid font + * variation settings format. */ - public Builder setFontVariationSettings(@Nullable String variationSettings) { + public Builder setFontVariationSettings(@Nullable String variationSettings) + throws FontVariationAxis.InvalidFormatException { checkNotRecycled(); if (mAxes != null) { throw new IllegalStateException("Font variation settings are already set."); } - final List<FontConfig.Axis> axesList = FontListParser.parseFontVariationSettings( - variationSettings); - mAxes = axesList.toArray(new FontConfig.Axis[axesList.size()]); + mAxes = FontVariationAxis.fromFontVariationSettings(variationSettings); return this; } @@ -699,7 +705,7 @@ public class Typeface { * * @param axes An array of font variation axis tag-value pairs. */ - public Builder setFontVariationSettings(@Nullable FontConfig.Axis[] axes) { + public Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) { checkNotRecycled(); if (mAxes != null) { throw new IllegalStateException("Font variation settings are already set."); @@ -718,7 +724,7 @@ public class Typeface { * @return Unique id for a given AssetManager and asset path. */ private static String createAssetUid(final AssetManager mgr, String path, int ttcIndex, - @Nullable FontConfig.Axis[] axes) { + @Nullable FontVariationAxis[] axes) { final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers(); final StringBuilder builder = new StringBuilder(); final int size = pkgs.size(); @@ -731,8 +737,8 @@ public class Typeface { builder.append(Integer.toString(ttcIndex)); builder.append("-"); if (axes != null) { - for (FontConfig.Axis axis : axes) { - builder.append(Integer.toHexString(axis.getTag())); + for (FontVariationAxis axis : axes) { + builder.append(axis.getTag()); builder.append("-"); builder.append(Float.toString(axis.getStyleValue())); } @@ -865,7 +871,7 @@ public class Typeface { /** @hide */ public static Typeface createFromTypefaceWithVariation(Typeface family, - List<FontConfig.Axis> axes) { + List<FontVariationAxis> axes) { final long ni = family == null ? 0 : family.native_instance; return new Typeface(nativeCreateFromTypefaceWithVariation(ni, axes)); } @@ -1162,9 +1168,9 @@ public class Typeface { } private static native long nativeCreateFromTypeface(long native_instance, int style); - // TODO: clean up: change List<FontConfig.Axis> to FontConfig.Axis[] + // TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[] private static native long nativeCreateFromTypefaceWithVariation( - long native_instance, List<FontConfig.Axis> axes); + long native_instance, List<FontVariationAxis> axes); private static native long nativeCreateWeightAlias(long native_instance, int weight); private static native void nativeUnref(long native_instance); private static native int nativeGetStyle(long native_instance); diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index d88aee93fae2..a491d7e771dc 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -210,6 +210,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { /** * Change the global fade duration when a new drawable is entering * the scene. + * * @param ms The amount of time to fade in milliseconds. */ public void setEnterFadeDuration(int ms) { @@ -219,6 +220,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { /** * Change the global fade duration when a new drawable is leaving * the scene. + * * @param ms The amount of time to fade in milliseconds. */ public void setExitFadeDuration(int ms) { @@ -375,6 +377,13 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { @Override public void invalidateDrawable(@NonNull Drawable who) { + // This may have been called as the result of a tint changing, in + // which case we may need to refresh the cached statefulness or + // opacity. + if (mDrawableContainerState != null) { + mDrawableContainerState.invalidateCache(); + } + if (who == mCurrDrawable && getCallback() != null) { getCallback().invalidateDrawable(this); } @@ -822,8 +831,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mDrawables[pos] = dr; mNumChildren++; mChildrenChangingConfigurations |= dr.getChangingConfigurations(); - mCheckedStateful = false; - mCheckedOpacity = false; + + invalidateCache(); mConstantPadding = null; mCheckedPadding = false; @@ -833,6 +842,14 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { return pos; } + /** + * Invalidates the cached opacity and statefulness. + */ + void invalidateCache() { + mCheckedOpacity = false; + mCheckedStateful = false; + } + final int getCapacity() { return mDrawables.length; } diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index 355e45e70a6b..b159f0f4a2be 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -32,6 +32,7 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.LayoutDirection; +import android.util.Log; import android.view.Gravity; import android.view.View; @@ -66,6 +67,8 @@ import java.io.IOException; * @attr ref android.R.styleable#LayerDrawableItem_id */ public class LayerDrawable extends Drawable implements Drawable.Callback { + private static final String LOG_TAG = "LayerDrawable"; + /** * Padding mode used to nest each layer inside the padding of the previous * layer. @@ -89,6 +92,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { */ public static final int INSET_UNDEFINED = Integer.MIN_VALUE; + @NonNull LayerState mLayerState; private int[] mPaddingL; @@ -170,13 +174,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { throws XmlPullParserException, IOException { super.inflate(r, parser, attrs, theme); - final LayerState state = mLayerState; - if (state == null) { - return; - } - // The density may have changed since the last update. This will // apply scaling to any existing constant state properties. + final LayerState state = mLayerState; final int density = Drawable.resolveDensity(r, 0); state.setDensity(density); @@ -202,10 +202,6 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { super.applyTheme(t); final LayerState state = mLayerState; - if (state == null) { - return; - } - final int density = Drawable.resolveDensity(t.getResources(), 0); state.setDensity(density); @@ -403,7 +399,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { @Override public boolean canApplyTheme() { - return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme(); + return mLayerState.canApplyTheme() || super.canApplyTheme(); } /** @@ -986,6 +982,11 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { if (mSuspendChildInvalidation) { mChildRequestedInvalidation = true; } else { + // This may have been called as the result of a tint changing, in + // which case we may need to refresh the cached statefulness or + // opacity. + mLayerState.invalidateCache(); + invalidateSelf(); } } @@ -1836,15 +1837,24 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { final ConstantState cs = dr.getConstantState(); if (cs == null) { clone = dr; + if (dr.getCallback() != null) { + // This drawable already has an owner. + Log.w(LOG_TAG, "Invalid drawable added to LayerDrawable! Drawable already " + + "belongs to another owner but does not expose a constant state.", + new RuntimeException()); + } } else if (res != null) { clone = cs.newDrawable(res); } else { clone = cs.newDrawable(); } - clone.setCallback(owner); clone.setLayoutDirection(dr.getLayoutDirection()); clone.setBounds(dr.getBounds()); clone.setLevel(dr.getLevel()); + + // Set the callback last to prevent invalidation from + // propagating before the constant state has been set. + clone.setCallback(owner); } else { clone = null; } @@ -2121,7 +2131,10 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { return true; } - public void invalidateCache() { + /** + * Invalidates the cached opacity and statefulness. + */ + void invalidateCache() { mCheckedOpacity = false; mCheckedStateful = false; } diff --git a/graphics/java/android/graphics/fonts/FontVariationAxis.java b/graphics/java/android/graphics/fonts/FontVariationAxis.java new file mode 100644 index 000000000000..91ec0e8df912 --- /dev/null +++ b/graphics/java/android/graphics/fonts/FontVariationAxis.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.fonts; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.regex.Pattern; + +/** + * Class that holds information about single font variation axis. + */ +public final class FontVariationAxis implements Parcelable { + private final int mTag; + private final String mTagString; + private final float mStyleValue; + + /** + * Construct FontVariationAxis. + * + * The axis tag must contain four ASCII characters. Tag string that are longer or shorter than + * four characters, or contains characters outside of U+0020..U+007E are invalid. + * + * @throws {@link InvalidFormatException} If given tag string is invalid. + */ + public FontVariationAxis(@NonNull String tagString, float styleValue) + throws InvalidFormatException { + if (!isValidTag(tagString)) { + throw new InvalidFormatException("Invalid tag pattern: " + tagString); + } + mTag = makeTag(tagString); + mTagString = tagString; + mStyleValue = styleValue; + } + + /** + * Returns the OpenType style tag value. + * @hide + */ + public int getOpenTypeTagValue() { + return mTag; + } + + /** + * Returns the variable font axis tag associated to this axis. + */ + public String getTag() { + return mTagString; + } + + /** + * Returns the style value associated to the given axis for this font. + */ + public float getStyleValue() { + return mStyleValue; + } + + /** + * @hide + */ + public FontVariationAxis(Parcel in) { + mTag = in.readInt(); + mTagString = in.readString(); + mStyleValue = in.readFloat(); + } + + @Override + public void writeToParcel(Parcel out, int flag) { + out.writeInt(mTag); + out.writeString(mTagString); + out.writeFloat(mStyleValue); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<FontVariationAxis> CREATOR = new Creator<FontVariationAxis>() { + @Override + public FontVariationAxis createFromParcel(Parcel in) { + return new FontVariationAxis(in); + } + + @Override + public FontVariationAxis[] newArray(int size) { + return new FontVariationAxis[size]; + } + }; + + /** + * Returns a valid font variation setting string for this object. + */ + @Override + public @NonNull String toString() { + return "'" + mTagString + "' " + Float.toString(mStyleValue); + } + + /** + * The 'tag' attribute value is read as four character values between U+0020 and U+007E + * inclusive. + */ + private static final Pattern TAG_PATTERN = Pattern.compile("[\u0020-\u007E]{4}"); + + /** + * Returns true if 'tagString' is valid for font variation axis tag. + */ + private static boolean isValidTag(String tagString) { + return tagString != null && TAG_PATTERN.matcher(tagString).matches(); + } + + /** + * The 'styleValue' attribute has an optional leading '-', followed by '<digits>', + * '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9]. + */ + private static final Pattern STYLE_VALUE_PATTERN = + Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))"); + + private static boolean isValidValueFormat(String valueString) { + return valueString != null && STYLE_VALUE_PATTERN.matcher(valueString).matches(); + } + + /** @hide */ + public static int makeTag(String tagString) { + final char c1 = tagString.charAt(0); + final char c2 = tagString.charAt(1); + final char c3 = tagString.charAt(2); + final char c4 = tagString.charAt(3); + return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; + } + + /** + * An exception indicates that the format of font variation settings is invalid. + */ + public static class InvalidFormatException extends Exception { + public InvalidFormatException(String message) { + super(message); + } + }; + + /** + * Construct FontVariationAxis array from font variation settings. + * + * The settings string is constructed from multiple pairs of axis tag and style values. The axis + * tag must contain four ASCII characters and must be wrapped with single quotes (U+0027) or + * double quotes (U+0022). Axis strings that are longer or shorter than four characters, or + * contain characters outside of U+0020..U+007E are invalid. If a specified axis name is not + * defined in the font, the settings will be ignored. + * + * <pre> + * FontVariationAxis.fromFontVariationSettings("'wdth' 1.0"); + * FontVariationAxis.fromFontVariationSettings("'AX ' 1.0, 'FB ' 2.0"); + * </pre> + * + * @param settings font variation settings. + * @return FontVariationAxis[] the array of parsed font variation axis. {@code null} if settings + * has no font variation settings. + * @throws InvalidFormatException If given string is not a valid font variation settings format. + */ + public static @Nullable FontVariationAxis[] fromFontVariationSettings(@Nullable String settings) + throws InvalidFormatException { + if (settings == null || settings.isEmpty()) { + return null; + } + final ArrayList<FontVariationAxis> axisList = new ArrayList<>(); + final int length = settings.length(); + for (int i = 0; i < length; i++) { + final char c = settings.charAt(i); + if (Character.isWhitespace(c)) { + continue; + } + if (!(c == '\'' || c == '"') || length < i + 6 || settings.charAt(i + 5) != c) { + throw new InvalidFormatException( + "Tag should be wrapped with double or single quote: " + settings); + } + final String tagString = settings.substring(i + 1, i + 5); + + i += 6; // Move to end of tag. + int endOfValueString = settings.indexOf(',', i); + if (endOfValueString == -1) { + endOfValueString = length; + } + final float value; + try { + // Float.parseFloat ignores leading/trailing whitespaces. + value = Float.parseFloat(settings.substring(i, endOfValueString)); + } catch (NumberFormatException e) { + throw new InvalidFormatException("Failed to parse float string: " + e.getMessage()); + } + axisList.add(new FontVariationAxis(tagString, value)); + i = endOfValueString; + } + if (axisList.isEmpty()) { + return null; + } + return axisList.toArray(new FontVariationAxis[0]); + } + + /** + * Stringify the array of FontVariationAxis. + * + * @param axes an array of FontVariationAxis. + * @return String a valid font variation settings string. + */ + public static @NonNull String toFontVariationSettings(@Nullable FontVariationAxis[] axes) { + if (axes == null || axes.length == 0) { + return ""; + } + return TextUtils.join(",", axes); + } +} + diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index dceb28518db3..d4d0c997be11 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -189,9 +189,9 @@ void GradientCache::mixBytes(const FloatColor& start, const FloatColor& end, float amount, uint8_t*& dst) const { float oppAmount = 1.0f - amount; float a = start.a * oppAmount + end.a * amount; - *dst++ = uint8_t(a * OECF(start.r * oppAmount + end.r * amount) * 255.0f); - *dst++ = uint8_t(a * OECF(start.g * oppAmount + end.g * amount) * 255.0f); - *dst++ = uint8_t(a * OECF(start.b * oppAmount + end.b * amount) * 255.0f); + *dst++ = uint8_t(OECF(start.r * oppAmount + end.r * amount) * 255.0f); + *dst++ = uint8_t(OECF(start.g * oppAmount + end.g * amount) * 255.0f); + *dst++ = uint8_t(OECF(start.b * oppAmount + end.b * amount) * 255.0f); *dst++ = uint8_t(a * 255.0f); } @@ -202,13 +202,13 @@ void GradientCache::mixFloats(const FloatColor& start, const FloatColor& end, float* d = (float*) dst; #ifdef ANDROID_ENABLE_LINEAR_BLENDING // We want to stay linear - *d++ = a * (start.r * oppAmount + end.r * amount); - *d++ = a * (start.g * oppAmount + end.g * amount); - *d++ = a * (start.b * oppAmount + end.b * amount); + *d++ = (start.r * oppAmount + end.r * amount); + *d++ = (start.g * oppAmount + end.g * amount); + *d++ = (start.b * oppAmount + end.b * amount); #else - *d++ = a * OECF(start.r * oppAmount + end.r * amount); - *d++ = a * OECF(start.g * oppAmount + end.g * amount); - *d++ = a * OECF(start.b * oppAmount + end.b * amount); + *d++ = OECF(start.r * oppAmount + end.r * amount); + *d++ = OECF(start.g * oppAmount + end.g * amount); + *d++ = OECF(start.b * oppAmount + end.b * amount); #endif *d++ = a; dst += 4 * sizeof(float); @@ -229,10 +229,10 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, ChannelMixer mix = gMixers[mUseFloatTexture]; FloatColor start; - start.setUnPreMultiplied(colors[0]); + start.set(colors[0]); FloatColor end; - end.setUnPreMultiplied(colors[1]); + end.set(colors[1]); int currentPos = 1; float startPos = positions[0]; @@ -247,7 +247,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, currentPos++; - end.setUnPreMultiplied(colors[currentPos]); + end.set(colors[currentPos]); distance = positions[currentPos] - startPos; } diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 1f78e09b5a58..d0f0949d5e78 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -295,10 +295,6 @@ const char* gFS_GradientPreamble[2] = { vec4 dither(const vec4 color) { return color + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0); } - vec4 gradientMix(const vec4 a, const vec4 b, float v) { - vec4 c = mix(a, b, v); - return vec4(c.rgb * c.a, c.a); - } )__SHADER__", // sRGB framebuffer R"__SHADER__( @@ -306,10 +302,6 @@ const char* gFS_GradientPreamble[2] = { vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0); return vec4(dithered * dithered, color.a); } - vec4 gradientMixMix(const vec4 a, const vec4 b, float v) { - vec4 c = mix(a, b, v); - return vec4(c.rgb * c.a, c.a); - } )__SHADER__", }; @@ -364,19 +356,19 @@ const char* gFS_Main_FetchGradient[6] = { // Linear " vec4 gradientColor = texture2D(gradientSampler, linear);\n", - " vec4 gradientColor = gradientMix(startColor, endColor, clamp(linear, 0.0, 1.0));\n", + " vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n", // Circular " vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n", - " vec4 gradientColor = gradientMix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n", + " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n", // Sweep " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n" " vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n", " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n" - " vec4 gradientColor = gradientMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n" + " vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n" }; const char* gFS_Main_FetchBitmap = " vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, outBitmapTexCoords));\n"; diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 8a504d4431c4..5c5378bf22ad 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -173,8 +173,8 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode outData->gradientSampler = 0; outData->gradientTexture = nullptr; - outData->startColor.setUnPreMultiplied(gradInfo.fColors[0]); - outData->endColor.setUnPreMultiplied(gradInfo.fColors[1]); + outData->startColor.set(gradInfo.fColors[0]); + outData->endColor.set(gradInfo.fColors[1]); } return true; diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk index 7f06421b02a0..8826cfcc3100 100644 --- a/libs/hwui/hwui_static_deps.mk +++ b/libs/hwui/hwui_static_deps.mk @@ -27,5 +27,7 @@ LOCAL_SHARED_LIBRARIES += \ libft2 \ libminikin \ libandroidfw \ - libRScpp \ + libRScpp + +LOCAL_STATIC_LIBRARIES += \ libplatformprotos diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index 2ead5c5897d2..d26eb59a039a 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -162,7 +162,7 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* SkAutoCanvasRestore acr(canvas, true); SkMatrix shadowMatrix; - mat4 hwuiMatrix(caster->getRecordedMatrix()); + mat4 hwuiMatrix; // TODO we don't pass the optional boolean to treat it as a 4x4 matrix caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix); hwuiMatrix.copyTo(shadowMatrix); diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 652954bd213e..686d06f9cd85 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -941,3 +941,67 @@ TEST(RenderNodeDrawable, renderNode) { EXPECT_EQ(2, canvas.mDrawCounter); } + +TEST(ReorderBarrierDrawable, testShadowMatrix) { + static const int CANVAS_WIDTH = 100; + static const int CANVAS_HEIGHT = 100; + static const float TRANSLATE_X = 11.0f; + static const float TRANSLATE_Y = 22.0f; + static const float CASTER_X = 40.0f; + static const float CASTER_Y = 40.0f; + static const float CASTER_WIDTH = 20.0f; + static const float CASTER_HEIGHT = 20.0f; + + + class ShadowTestCanvas : public SkCanvas { + public: + ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {} + int getIndex() { return mDrawCounter; } + + virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { + // expect to draw 2 RenderNodeDrawable, 1 StartReorderBarrierDrawable, + // 1 EndReorderBarrierDrawable + mDrawCounter++; + SkCanvas::onDrawDrawable(drawable, matrix); + } + + virtual void didTranslate(SkScalar dx, SkScalar dy) override { + mDrawCounter++; + EXPECT_EQ(dx, TRANSLATE_X); + EXPECT_EQ(dy, TRANSLATE_Y); + } + + virtual void didConcat(const SkMatrix& matrix) override { + // This function is invoked by EndReorderBarrierDrawable::drawShadow to apply shadow + // matrix. + mDrawCounter++; + EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X, CASTER_Y), matrix); + EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X+TRANSLATE_X, CASTER_Y+TRANSLATE_Y), + getTotalMatrix()); + } + protected: + int mDrawCounter = 0; + }; + + auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + canvas.translate(TRANSLATE_X, TRANSLATE_Y); + canvas.insertReorderBarrier(true); + + auto node = TestUtils::createSkiaNode(CASTER_X, CASTER_Y, CASTER_X + CASTER_WIDTH, + CASTER_Y + CASTER_HEIGHT, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + props.setElevation(42); + props.mutableOutline().setRoundRect(0, 0, 20, 20, 5, 1); + props.mutableOutline().setShouldClip(true); + }); + canvas.drawRenderNode(node.get()); + canvas.insertReorderBarrier(false); + }); + + //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection + ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT); + RenderNodeDrawable drawable(parent.get(), &canvas, false); + canvas.drawDrawable(&drawable); + EXPECT_EQ(6, canvas.getIndex()); +}
\ No newline at end of file diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index c7796cdd4cc4..4adbf79d562c 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2540,6 +2540,44 @@ public class AudioManager { /** * @hide + * Notifies an application with a focus listener of gain or loss of audio focus. + * This method can only be used by owners of an {@link AudioPolicy} configured with + * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to true. + * @param afi the recipient of the focus change, that has previously requested audio focus, and + * that was received by the {@code AudioPolicy} through + * {@link AudioPolicy.AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}. + * @param focusChange one of focus gain types ({@link #AUDIOFOCUS_GAIN}, + * {@link #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or + * {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}) + * or one of the focus loss types ({@link AudioManager#AUDIOFOCUS_LOSS}, + * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}, + * or {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}). + * <br>For the focus gain, the change type should be the same as the app requested. + * @param ap a valid registered {@link AudioPolicy} configured as a focus policy. + * @return {@link #AUDIOFOCUS_REQUEST_GRANTED} if the dispatch was successfully sent, or + * {@link #AUDIOFOCUS_REQUEST_FAILED} if the focus client didn't have a listener, or + * if there was an error sending the request. + * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null. + */ + @SystemApi + public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange, + @NonNull AudioPolicy ap) { + if (afi == null) { + throw new NullPointerException("Illegal null AudioFocusInfo"); + } + if (ap == null) { + throw new NullPointerException("Illegal null AudioPolicy"); + } + final IAudioService service = getService(); + try { + return service.dispatchFocusChange(afi, focusChange, ap.cb()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide * Used internally by telephony package to abandon audio focus, typically after a call or * when ringing ends and the call is rejected or not answered. * Should match one or more calls to {@link #requestAudioFocusForCall(int, int)}. @@ -2548,7 +2586,7 @@ public class AudioManager { final IAudioService service = getService(); try { service.abandonAudioFocus(null, AudioSystem.IN_VOICE_COMM_FOCUS_ID, - null /*AudioAttributes, legacy behavior*/); + null /*AudioAttributes, legacy behavior*/, getContext().getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2579,7 +2617,7 @@ public class AudioManager { final IAudioService service = getService(); try { status = service.abandonAudioFocus(mAudioFocusDispatcher, - getIdForAudioFocusListener(l), aa); + getIdForAudioFocusListener(l), aa, getContext().getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2787,7 +2825,7 @@ public class AudioManager { final IAudioService service = getService(); try { String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(), - policy.hasFocusListener()); + policy.hasFocusListener(), policy.isFocusPolicy()); if (regId == null) { return ERROR; } else { diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 58559843f1f9..884d41e59ada 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -20,6 +20,7 @@ import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.media.AudioAttributes; +import android.media.AudioFocusInfo; import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; @@ -122,7 +123,8 @@ interface IAudioService { IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, IAudioPolicyCallback pcb); - int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa); + int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa, + in String callingPackageName); void unregisterAudioFocusClient(String clientId); @@ -164,7 +166,7 @@ interface IAudioService { boolean isHdmiSystemAudioSupported(); String registerAudioPolicy(in AudioPolicyConfig policyConfig, - in IAudioPolicyCallback pcb, boolean hasFocusListener); + in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy); oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb); @@ -196,5 +198,8 @@ interface IAudioService { int getFocusRampTimeMs(in int focusGain, in AudioAttributes attr); + int dispatchFocusChange(in AudioFocusInfo afi, in int focusChange, + in IAudioPolicyCallback pcb); + // WARNING: read warning at top of file, it is recommended to add new methods at the end } diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index cdc1d60fa789..4675e327ce25 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -746,14 +746,19 @@ public class MediaRecorder } /** - * Sets the video encoding profile for recording. Call this method before prepare(). - * Prepare() may perform additional checks on the parameter to make sure whether the - * specified profile and level are applicable, and sometimes the passed profile or - * level will be discarded due to codec capablity or to ensure the video recording - * can proceed smoothly based on the capabilities of the platform. - * @hide + * Sets the desired video encoding profile and level for recording. The profile and level + * must be valid for the video encoder set by {@link #setVideoEncoder}. This method can + * called before or after {@link #setVideoEncoder} but it must be called before {@link #prepare}. + * {@code prepare()} may perform additional checks on the parameter to make sure that the specified + * profile and level are applicable, and sometimes the passed profile or level will be + * discarded due to codec capablity or to ensure the video recording can proceed smoothly + * based on the capabilities of the platform. <br>Application can also use the + * {@link MediaCodecInfo.CodecCapabilities#profileLevels} to query applicable combination of profile + * and level for the corresponding format. Note that the requested profile/level may not be supported by + * the codec that is actually being used by this MediaRecorder instance. * @param profile declared in {@link MediaCodecInfo.CodecProfileLevel}. * @param level declared in {@link MediaCodecInfo.CodecProfileLevel}. + * @throws IllegalArgumentException when an invalid profile or level value is used. */ public void setVideoEncodingProfileLevel(int profile, int level) { if (profile <= 0) { @@ -1281,3 +1286,4 @@ public class MediaRecorder @Override protected void finalize() { native_finalize(); } } + diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 423b4678441c..61d642f50179 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -68,6 +68,7 @@ public class AudioPolicy { private int mStatus; private String mRegistrationId; private AudioPolicyStatusListener mStatusListener; + private boolean mIsFocusPolicy; /** * The behavior of a policy with regards to audio focus where it relies on the application @@ -96,12 +97,14 @@ public class AudioPolicy { public AudioPolicyConfig getConfig() { return mConfig; } /** @hide */ public boolean hasFocusListener() { return mFocusListener != null; } + /** @hide */ + public boolean isFocusPolicy() { return mIsFocusPolicy; } /** * The parameter is guaranteed non-null through the Builder */ private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper, - AudioPolicyFocusListener fl, AudioPolicyStatusListener sl) { + AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy) { mConfig = config; mStatus = POLICY_STATUS_UNREGISTERED; mContext = context; @@ -116,10 +119,12 @@ public class AudioPolicy { } mFocusListener = fl; mStatusListener = sl; + mIsFocusPolicy = isFocusPolicy; } /** - * Builder class for {@link AudioPolicy} objects + * Builder class for {@link AudioPolicy} objects. + * By default the policy to be created doesn't govern audio focus decisions. */ @SystemApi public static class Builder { @@ -128,6 +133,7 @@ public class AudioPolicy { private Looper mLooper; private AudioPolicyFocusListener mFocusListener; private AudioPolicyStatusListener mStatusListener; + private boolean mIsFocusPolicy = false; /** * Constructs a new Builder with no audio mixes. @@ -179,6 +185,21 @@ public class AudioPolicy { } /** + * Declares whether this policy will grant and deny audio focus through + * the {@link AudioPolicy.AudioPolicyStatusListener}. + * If set to {@code true}, it is mandatory to set an + * {@link AudioPolicy.AudioPolicyStatusListener} in order to successfully build + * an {@code AudioPolicy} instance. + * @param enforce true if the policy will govern audio focus decisions. + * @return the same Builder instance. + */ + @SystemApi + public Builder setIsAudioFocusPolicy(boolean isFocusPolicy) { + mIsFocusPolicy = isFocusPolicy; + return this; + } + + /** * Sets the audio policy status listener. * @param l a {@link AudioPolicy.AudioPolicyStatusListener} */ @@ -187,6 +208,14 @@ public class AudioPolicy { mStatusListener = l; } + /** + * Combines all of the attributes that have been set on this {@code Builder} and returns a + * new {@link AudioPolicy} object. + * @return a new {@code AudioPolicy} object. + * @throws IllegalStateException if there is no + * {@link AudioPolicy.AudioPolicyStatusListener} but the policy was configured + * as an audio focus policy with {@link #setIsAudioFocusPolicy(boolean)}. + */ @SystemApi public AudioPolicy build() { if (mStatusListener != null) { @@ -195,8 +224,12 @@ public class AudioPolicy { mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY; } } + if (mIsFocusPolicy && mFocusListener == null) { + throw new IllegalStateException("Cannot be a focus policy without " + + "an AudioPolicyFocusListener"); + } return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper, - mFocusListener, mStatusListener); + mFocusListener, mStatusListener, mIsFocusPolicy); } } @@ -402,6 +435,24 @@ public class AudioPolicy { public static abstract class AudioPolicyFocusListener { public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {} public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {} + /** + * Called whenever an application requests audio focus. + * Only ever called if the {@link AudioPolicy} was built with + * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}. + * @param afi information about the focus request and the requester + * @param requestResult the result that was returned synchronously by the framework to the + * application, {@link #AUDIOFOCUS_REQUEST_FAILED},or + * {@link #AUDIOFOCUS_REQUEST_DELAYED}. + */ + public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {} + /** + * Called whenever an application abandons audio focus. + * Only ever called if the {@link AudioPolicy} was built with + * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}. + * @param afi information about the focus request being abandoned and the original + * requester. + */ + public void onAudioFocusAbandon(AudioFocusInfo afi) {} } private void onPolicyStatusChange() { @@ -439,6 +490,22 @@ public class AudioPolicy { } } + public void notifyAudioFocusRequest(AudioFocusInfo afi, int requestResult) { + sendMsg(MSG_FOCUS_REQUEST, afi, requestResult); + if (DEBUG) { + Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client=" + + afi.getClientId() + "reqRes=" + requestResult); + } + } + + public void notifyAudioFocusAbandon(AudioFocusInfo afi) { + sendMsg(MSG_FOCUS_ABANDON, afi, 0 /* ignored */); + if (DEBUG) { + Log.v(TAG, "notifyAudioFocusAbandon: pack=" + afi.getPackageName() + " client=" + + afi.getClientId()); + } + } + public void notifyMixStateUpdate(String regId, int state) { for (AudioMix mix : mConfig.getMixes()) { if (mix.getRegistration().equals(regId)) { @@ -459,6 +526,8 @@ public class AudioPolicy { private final static int MSG_FOCUS_GRANT = 1; private final static int MSG_FOCUS_LOSS = 2; private final static int MSG_MIX_STATE_UPDATE = 3; + private final static int MSG_FOCUS_REQUEST = 4; + private final static int MSG_FOCUS_ABANDON = 5; private class EventHandler extends Handler { public EventHandler(AudioPolicy ap, Looper looper) { @@ -488,6 +557,20 @@ public class AudioPolicy { mStatusListener.onMixStateUpdate((AudioMix) msg.obj); } break; + case MSG_FOCUS_REQUEST: + if (mFocusListener != null) { + mFocusListener.onAudioFocusRequest((AudioFocusInfo) msg.obj, msg.arg1); + } else { // should never be null, but don't crash + Log.e(TAG, "Invalid null focus listener for focus request event"); + } + break; + case MSG_FOCUS_ABANDON: + if (mFocusListener != null) { // should never be null + mFocusListener.onAudioFocusAbandon((AudioFocusInfo) msg.obj); + } else { // should never be null, but don't crash + Log.e(TAG, "Invalid null focus listener for focus abandon event"); + } + break; default: Log.e(TAG, "Unknown event " + msg.what); } diff --git a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl index ad8af15b15f2..86abbb4dc8d9 100644 --- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl +++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl @@ -22,9 +22,12 @@ import android.media.AudioFocusInfo; */ oneway interface IAudioPolicyCallback { - // callbacks for audio focus + // callbacks for audio focus listening void notifyAudioFocusGrant(in AudioFocusInfo afi, int requestResult); void notifyAudioFocusLoss(in AudioFocusInfo afi, boolean wasNotified); + // callback for audio focus policy + void notifyAudioFocusRequest(in AudioFocusInfo afi, int requestResult); + void notifyAudioFocusAbandon(in AudioFocusInfo afi); // callback for mix activity status update void notifyMixStateUpdate(in String regId, int state); diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index fc6057132315..0b27d183021d 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -16,6 +16,7 @@ package android.media.tv; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; @@ -567,9 +568,10 @@ public final class TvContract { } /** - * Common base for the tables of TV programs. + * Common columns for the tables of TV programs. + * @hide */ - public interface BaseProgramColumns extends BaseTvColumns { + interface ProgramColumns { /** * The title of this TV program. * @@ -835,12 +837,13 @@ public final class TvContract { } /** - * Common base for the tables of preview programs. + * Common columns for the tables of preview programs. + * @hide */ - public interface BasePreviewProgramColumns extends BaseProgramColumns { + interface PreviewProgramColumns { /** @hide */ - @StringDef({ + @IntDef({ TYPE_MOVIE, TYPE_TV_SERIES, TYPE_TV_SEASON, @@ -862,87 +865,87 @@ public final class TvContract { * * @see #COLUMN_TYPE */ - String TYPE_MOVIE = "TYPE_MOVIE"; + int TYPE_MOVIE = 0; /** * The program type for TV series. * * @see #COLUMN_TYPE */ - String TYPE_TV_SERIES = "TYPE_TV_SERIES"; + int TYPE_TV_SERIES = 1; /** * The program type for TV season. * * @see #COLUMN_TYPE */ - String TYPE_TV_SEASON = "TYPE_TV_SEASON"; + int TYPE_TV_SEASON = 2; /** * The program type for TV episode. * * @see #COLUMN_TYPE */ - String TYPE_TV_EPISODE = "TYPE_TV_EPISODE"; + int TYPE_TV_EPISODE = 3; /** * The program type for clip. * * @see #COLUMN_TYPE */ - String TYPE_CLIP = "TYPE_CLIP"; + int TYPE_CLIP = 4; /** * The program type for event. * * @see #COLUMN_TYPE */ - String TYPE_EVENT = "TYPE_EVENT"; + int TYPE_EVENT = 5; /** * The program type for channel. * * @see #COLUMN_TYPE */ - String TYPE_CHANNEL = "TYPE_CHANNEL"; + int TYPE_CHANNEL = 6; /** * The program type for track. * * @see #COLUMN_TYPE */ - String TYPE_TRACK = "TYPE_TRACK"; + int TYPE_TRACK = 7; /** * The program type for album. * * @see #COLUMN_TYPE */ - String TYPE_ALBUM = "TYPE_ALBUM"; + int TYPE_ALBUM = 8; /** * The program type for artist. * * @see #COLUMN_TYPE */ - String TYPE_ARTIST = "TYPE_ARTIST"; + int TYPE_ARTIST = 9; /** * The program type for playlist. * * @see #COLUMN_TYPE */ - String TYPE_PLAYLIST = "TYPE_PLAYLIST"; + int TYPE_PLAYLIST = 10; /** * The program type for station. * * @see #COLUMN_TYPE */ - String TYPE_STATION = "TYPE_STATION"; + int TYPE_STATION = 11; /** @hide */ - @StringDef({ + @IntDef({ ASPECT_RATIO_16_9, ASPECT_RATIO_3_2, ASPECT_RATIO_1_1, @@ -957,7 +960,7 @@ public final class TvContract { * @see #COLUMN_POSTER_ART_ASPECT_RATIO * @see #COLUMN_THUMBNAIL_ASPECT_RATIO */ - String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9"; + int ASPECT_RATIO_16_9 = 0; /** * The aspect ratio for 3:2. @@ -965,7 +968,7 @@ public final class TvContract { * @see #COLUMN_POSTER_ART_ASPECT_RATIO * @see #COLUMN_THUMBNAIL_ASPECT_RATIO */ - String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2"; + int ASPECT_RATIO_3_2 = 1; /** * The aspect ratio for 1:1. @@ -973,7 +976,7 @@ public final class TvContract { * @see #COLUMN_POSTER_ART_ASPECT_RATIO * @see #COLUMN_THUMBNAIL_ASPECT_RATIO */ - String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1"; + int ASPECT_RATIO_1_1 = 2; /** * The aspect ratio for 2:3. @@ -981,10 +984,10 @@ public final class TvContract { * @see #COLUMN_POSTER_ART_ASPECT_RATIO * @see #COLUMN_THUMBNAIL_ASPECT_RATIO */ - String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3"; + int ASPECT_RATIO_2_3 = 3; /** @hide */ - @StringDef({ + @IntDef({ AVAILABILITY_AVAILABLE, AVAILABILITY_FREE_WITH_SUBSCRIPTION, AVAILABILITY_PAID_CONTENT, @@ -997,15 +1000,14 @@ public final class TvContract { * * @see #COLUMN_AVAILABILITY */ - String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE"; + int AVAILABILITY_AVAILABLE = 0; /** * The availability for "free with subscription". * * @see #COLUMN_AVAILABILITY */ - String AVAILABILITY_FREE_WITH_SUBSCRIPTION = - "AVAILABILITY_FREE_WITH_SUBSCRIPTION"; + int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; /** * The availability for "paid content, either to-own or rental @@ -1013,72 +1015,72 @@ public final class TvContract { * * @see #COLUMN_AVAILABILITY */ - String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT"; + int AVAILABILITY_PAID_CONTENT = 2; /** @hide */ - @StringDef({ + @IntDef({ + INTERACTION_TYPE_VIEWS, INTERACTION_TYPE_LISTENS, INTERACTION_TYPE_FOLLOWERS, INTERACTION_TYPE_FANS, INTERACTION_TYPE_LIKES, INTERACTION_TYPE_THUMBS, - INTERACTION_TYPE_VIEWS, INTERACTION_TYPE_VIEWERS, }) @Retention(RetentionPolicy.SOURCE) public @interface InteractionType {} /** - * The interaction type for "listens". + * The interaction type for "views". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS"; + int INTERACTION_TYPE_VIEWS = 0; /** - * The interaction type for "followers". + * The interaction type for "listens". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; + int INTERACTION_TYPE_LISTENS = 1; /** - * The interaction type for "fans". + * The interaction type for "followers". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; + int INTERACTION_TYPE_FOLLOWERS = 2; /** - * The interaction type for "likes". + * The interaction type for "fans". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES"; + int INTERACTION_TYPE_FANS = 3; /** - * The interaction type for "thumbs". + * The interaction type for "likes". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS"; + int INTERACTION_TYPE_LIKES = 4; /** - * The interaction type for "views". + * The interaction type for "thumbs". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS"; + int INTERACTION_TYPE_THUMBS = 5; /** * The interaction type for "viewers". * * @see #COLUMN_INTERACTION_TYPE */ - String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS"; + int INTERACTION_TYPE_VIEWERS = 6; /** @hide */ - @StringDef({ + @IntDef({ REVIEW_RATING_STYLE_STARS, REVIEW_RATING_STYLE_THUMBS_UP_DOWN, REVIEW_RATING_STYLE_PERCENTAGE, @@ -1091,23 +1093,21 @@ public final class TvContract { * * @see #COLUMN_REVIEW_RATING_STYLE */ - String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS"; + int REVIEW_RATING_STYLE_STARS = 0; /** * The review rating style for thumbs-up and thumbs-down rating. * * @see #COLUMN_REVIEW_RATING_STYLE */ - String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = - "REVIEW_RATING_STYLE_THUMBS_UP_DOWN"; + int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; /** * The review rating style for 0 to 100 point system. * * @see #COLUMN_REVIEW_RATING_STYLE */ - String REVIEW_RATING_STYLE_PERCENTAGE = - "REVIEW_RATING_STYLE_PERCENTAGE"; + int REVIEW_RATING_STYLE_PERCENTAGE = 2; /** * The type of this program content. @@ -1129,7 +1129,7 @@ public final class TvContract { * <p>This is a required field if the program is from a {@link Channels#TYPE_PREVIEW} * channel. * - * <p>Type: TEXT + * <p>Type: INTEGER */ String COLUMN_TYPE = "type"; @@ -1142,7 +1142,7 @@ public final class TvContract { * {@link #ASPECT_RATIO_1_1}, and * {@link #ASPECT_RATIO_2_3}. * - * <p>Type: TEXT + * <p>Type: INTEGER */ String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio"; @@ -1155,7 +1155,7 @@ public final class TvContract { * {@link #ASPECT_RATIO_1_1}, and * {@link #ASPECT_RATIO_2_3}. * - * <p>Type: TEXT + * <p>Type: INTEGER */ String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; @@ -1188,7 +1188,7 @@ public final class TvContract { * {@link #AVAILABILITY_FREE_WITH_SUBSCRIPTION}, and * {@link #AVAILABILITY_PAID_CONTENT}. * - * <p>Type: TEXT + * <p>Type: INTEGER */ String COLUMN_AVAILABILITY = "availability"; @@ -1327,15 +1327,15 @@ public final class TvContract { * The type of interaction for this TV program. * * <p> The value should match one of the followings: + * {@link #INTERACTION_TYPE_VIEWS}, * {@link #INTERACTION_TYPE_LISTENS}, * {@link #INTERACTION_TYPE_FOLLOWERS}, * {@link #INTERACTION_TYPE_FANS}, * {@link #INTERACTION_TYPE_LIKES}, - * {@link #INTERACTION_TYPE_THUMBS}, - * {@link #INTERACTION_TYPE_VIEWS}, and + * {@link #INTERACTION_TYPE_THUMBS}, and * {@link #INTERACTION_TYPE_VIEWERS}. * - * <p>Type: TEXT + * <p>Type: INTEGER * @see #COLUMN_INTERACTION_COUNT */ String COLUMN_INTERACTION_TYPE = "interaction_type"; @@ -1363,7 +1363,7 @@ public final class TvContract { * <p> The value should match one of the followings: {@link #REVIEW_RATING_STYLE_STARS}, * {@link #REVIEW_RATING_STYLE_THUMBS_UP_DOWN}, and {@link #REVIEW_RATING_STYLE_PERCENTAGE}. * - * <p>Type: TEXT + * <p>Type: INTEGER * @see #COLUMN_REVIEW_RATING */ String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; @@ -2229,7 +2229,7 @@ public final class TvContract { * <p>By default, the query results will be sorted by * {@link Programs#COLUMN_START_TIME_UTC_MILLIS} in ascending order. */ - public static final class Programs implements BaseProgramColumns { + public static final class Programs implements BaseTvColumns, ProgramColumns { /** The content:// style URI for this table. */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" @@ -2543,7 +2543,7 @@ public final class TvContract { * <p>By default, the query results will be sorted by {@link #COLUMN_START_TIME_UTC_MILLIS} in * ascending order. */ - public static final class RecordedPrograms implements BaseProgramColumns { + public static final class RecordedPrograms implements BaseTvColumns, ProgramColumns { /** The content:// style URI for this table. */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" @@ -2665,7 +2665,8 @@ public final class TvContract { /** * Column definitions for the preview TV programs table. */ - public static final class PreviewPrograms implements BasePreviewProgramColumns { + public static final class PreviewPrograms implements BaseTvColumns, ProgramColumns, + PreviewProgramColumns { /** The content:// style URI for this table. */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" @@ -2711,7 +2712,8 @@ public final class TvContract { /** * Column definitions for the "watch next" TV programs table. */ - public static final class WatchNextPrograms implements BasePreviewProgramColumns { + public static final class WatchNextPrograms implements BaseTvColumns, ProgramColumns, + PreviewProgramColumns { /** The content:// style URI for this table. */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" @@ -2724,7 +2726,7 @@ public final class TvContract { public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program"; /** @hide */ - @StringDef({ + @IntDef({ WATCH_NEXT_TYPE_CONTINUE, WATCH_NEXT_TYPE_NEXT, WATCH_NEXT_TYPE_NEW, @@ -2739,7 +2741,7 @@ public final class TvContract { * * @see #COLUMN_WATCH_NEXT_TYPE */ - public static final String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE"; + public static final int WATCH_NEXT_TYPE_CONTINUE = 0; /** * The watch next type for NEXT. Use this type when the user has watched one or more @@ -2749,7 +2751,7 @@ public final class TvContract { * * @see #COLUMN_WATCH_NEXT_TYPE */ - public static final String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT"; + public static final int WATCH_NEXT_TYPE_NEXT = 1; /** * The watch next type for NEW. Use this type when the user had watched all of the available @@ -2759,7 +2761,7 @@ public final class TvContract { * * @see #COLUMN_WATCH_NEXT_TYPE */ - public static final String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW"; + public static final int WATCH_NEXT_TYPE_NEW = 2; /** * The watch next type for WATCHLIST. Use this type when the user has elected to explicitly @@ -2768,7 +2770,7 @@ public final class TvContract { * * @see #COLUMN_WATCH_NEXT_TYPE */ - public static final String WATCH_NEXT_TYPE_WATCHLIST = "WATCH_NEXT_TYPE_WATCHLIST"; + public static final int WATCH_NEXT_TYPE_WATCHLIST = 3; /** * The "watch next" type of this program content. @@ -2781,7 +2783,7 @@ public final class TvContract { * * <p>This is a required field. * - * <p>Type: TEXT + * <p>Type: INTEGER */ public static final String COLUMN_WATCH_NEXT_TYPE = "watch_next_type"; diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index c7bed9bc1685..c82a1f6a646a 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -153,6 +153,7 @@ LIBANDROID { ANativeWindow_acquire; ANativeWindow_fromSurface; ANativeWindow_fromSurfaceTexture; # introduced-arm=13 introduced-mips=13 introduced-x86=13 + ANativeWindow_toSurface; # introduced=26 ANativeWindow_getFormat; ANativeWindow_getHeight; ANativeWindow_getWidth; diff --git a/native/android/native_window_jni.cpp b/native/android/native_window_jni.cpp index dc3040533974..859c550db94d 100644 --- a/native/android/native_window_jni.cpp +++ b/native/android/native_window_jni.cpp @@ -20,6 +20,7 @@ #include <android/native_window.h> #include <system/window.h> +#include <gui/Surface.h> #include <utils/StrongPointer.h> #include <android_runtime/android_view_Surface.h> @@ -33,3 +34,11 @@ ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) { } return win.get(); } + +jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window) { + if (window == NULL) { + return NULL; + } + sp<Surface> surface = static_cast<Surface*>(window); + return android_view_Surface_createFromSurface(env, surface); +} diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index f14d0d128e67..ad985c7e5f34 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -928,4 +928,18 @@ [CHAR LIMIT=35] --> <string name="use_system_language_to_select_input_method_subtypes">Use system languages</string> + <!-- Toast that settings for an application is failed to open. --> + <string name="failed_to_open_app_settings_toast">Failed to open settings for <xliff:g id="spell_application_name">%1$s</xliff:g></string> + + <!-- Warning message about security implications of enabling an input method, displayed as a dialog + message when the user selects to enable an IME. --> + <string name="ime_security_warning">This input method may be able to collect + all the text you type, including personal data like passwords and credit + card numbers. It comes from the app + <xliff:g id="ime_application_name">%1$s</xliff:g>. + Use this input method?</string> + + <!-- [CHAR LIMIT=NONE] Dialog body explaining that the app just selected by the user will not work after a reboot until until after the user enters their credentials, such as a PIN or password. --> + <string name="direct_boot_unaware_dialog_message">Note: After a reboot, this app can\'t start until you unlock your phone</string> + </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index c2ce7c9fd5de..8833fb8cab53 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -1394,7 +1394,9 @@ public class ApplicationsState { @Override public boolean filterApp(AppEntry entry) { - if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + if (AppUtils.isInstant(entry.info)) { + return false; + } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { return true; } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { return true; @@ -1407,6 +1409,23 @@ public class ApplicationsState { } }; + /** + * Displays a combined list with "downloaded" and "visible in launcher" apps only. + */ + public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new AppFilter() { + + @Override + public void init() { + } + + @Override + public boolean filterApp(AppEntry entry) { + return AppUtils.isInstant(entry.info) + || FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry); + } + + }; + public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() { @Override public void init() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java index 69b45e5541b2..9ea7a4af8d12 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java @@ -71,14 +71,15 @@ public class BluetoothDiscoverableTimeoutReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { + if (intent.getAction() == null || !intent.getAction().equals(INTENT_DISCOVERABLE_TIMEOUT)) { + return; + } LocalBluetoothAdapter localBluetoothAdapter = LocalBluetoothAdapter.getInstance(); - - if(localBluetoothAdapter != null && + if(localBluetoothAdapter != null && localBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) { Log.d(TAG, "Disable discoverable..."); - localBluetoothAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE); - } else { + } else { Log.e(TAG, "localBluetoothAdapter is NULL!!"); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java index a3ae9269b1f7..abd4e294fecb 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java @@ -103,7 +103,7 @@ public class CachedBluetoothDeviceManager { */ public String getName(BluetoothDevice device) { CachedBluetoothDevice cachedDevice = findDevice(device); - if (cachedDevice != null) { + if (cachedDevice != null && cachedDevice.getName() != null) { return cachedDevice.getName(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java new file mode 100755 index 000000000000..1bbc878b56c9 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.inputmethod; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +import android.app.AlertDialog; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.UserHandle; +import android.support.v7.preference.Preference; +import android.support.v7.preference.Preference.OnPreferenceChangeListener; +import android.support.v7.preference.Preference.OnPreferenceClickListener; +import android.text.TextUtils; +import android.util.Log; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; +import android.widget.Toast; + +import com.android.internal.inputmethod.InputMethodUtils; +import com.android.settingslib.R; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedSwitchPreference; + +import java.text.Collator; +import java.util.List; + +/** + * Input method preference. + * + * This preference represents an IME. It is used for two purposes. 1) An instance with a switch + * is used to enable or disable the IME. 2) An instance without a switch is used to invoke the + * setting activity of the IME. + */ +public class InputMethodPreference extends RestrictedSwitchPreference implements OnPreferenceClickListener, + OnPreferenceChangeListener { + private static final String TAG = InputMethodPreference.class.getSimpleName(); + private static final String EMPTY_TEXT = ""; + private static final int NO_WIDGET = 0; + + public interface OnSavePreferenceListener { + /** + * Called when this preference needs to be saved its state. + * + * Note that this preference is non-persistent and needs explicitly to be saved its state. + * Because changing one IME state may change other IMEs' state, this is a place to update + * other IMEs' state as well. + * + * @param pref This preference. + */ + void onSaveInputMethodPreference(InputMethodPreference pref); + } + + private final InputMethodInfo mImi; + private final boolean mHasPriorityInSorting; + private final OnSavePreferenceListener mOnSaveListener; + private final InputMethodSettingValuesWrapper mInputMethodSettingValues; + private final boolean mIsAllowedByOrganization; + + private AlertDialog mDialog = null; + + /** + * A preference entry of an input method. + * + * @param context The Context this is associated with. + * @param imi The {@link InputMethodInfo} of this preference. + * @param isImeEnabler true if this preference is the IME enabler that has enable/disable + * switches for all available IMEs, not the list of enabled IMEs. + * @param isAllowedByOrganization false if the IME has been disabled by a device or profile + * owner. + * @param onSaveListener The listener called when this preference has been changed and needs + * to save the state to shared preference. + */ + public InputMethodPreference(final Context context, final InputMethodInfo imi, + final boolean isImeEnabler, final boolean isAllowedByOrganization, + final OnSavePreferenceListener onSaveListener) { + super(context); + setPersistent(false); + mImi = imi; + mIsAllowedByOrganization = isAllowedByOrganization; + mOnSaveListener = onSaveListener; + if (!isImeEnabler) { + // Remove switch widget. + setWidgetLayoutResource(NO_WIDGET); + } + // Disable on/off switch texts. + setSwitchTextOn(EMPTY_TEXT); + setSwitchTextOff(EMPTY_TEXT); + setKey(imi.getId()); + setTitle(imi.loadLabel(context.getPackageManager())); + final String settingsActivity = imi.getSettingsActivity(); + if (TextUtils.isEmpty(settingsActivity)) { + setIntent(null); + } else { + // Set an intent to invoke settings activity of an input method. + final Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(imi.getPackageName(), settingsActivity); + setIntent(intent); + } + mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(context); + mHasPriorityInSorting = InputMethodUtils.isSystemIme(imi) + && mInputMethodSettingValues.isValidSystemNonAuxAsciiCapableIme(imi, context); + setOnPreferenceClickListener(this); + setOnPreferenceChangeListener(this); + } + + public InputMethodInfo getInputMethodInfo() { + return mImi; + } + + private boolean isImeEnabler() { + // If this {@link SwitchPreference} doesn't have a widget layout, we explicitly hide the + // switch widget at constructor. + return getWidgetLayoutResource() != NO_WIDGET; + } + + @Override + public boolean onPreferenceChange(final Preference preference, final Object newValue) { + // Always returns false to prevent default behavior. + // See {@link TwoStatePreference#onClick()}. + if (!isImeEnabler()) { + // Prevent disabling an IME because this preference is for invoking a settings activity. + return false; + } + if (isChecked()) { + // Disable this IME. + setCheckedInternal(false); + return false; + } + if (InputMethodUtils.isSystemIme(mImi)) { + // Enable a system IME. No need to show a security warning dialog, + // but we might need to prompt if it's not Direct Boot aware. + // TV doesn't doesn't need to worry about this, but other platforms should show + // a warning. + if (mImi.getServiceInfo().directBootAware || isTv()) { + setCheckedInternal(true); + } else if (!isTv()){ + showDirectBootWarnDialog(); + } + } else { + // Once security is confirmed, we might prompt if the IME isn't + // Direct Boot aware. + showSecurityWarnDialog(); + } + return false; + } + + @Override + public boolean onPreferenceClick(final Preference preference) { + // Always returns true to prevent invoking an intent without catching exceptions. + // See {@link Preference#performClick(PreferenceScreen)}/ + if (isImeEnabler()) { + // Prevent invoking a settings activity because this preference is for enabling and + // disabling an input method. + return true; + } + final Context context = getContext(); + try { + final Intent intent = getIntent(); + if (intent != null) { + // Invoke a settings activity of an input method. + context.startActivity(intent); + } + } catch (final ActivityNotFoundException e) { + Log.d(TAG, "IME's Settings Activity Not Found", e); + final String message = context.getString( + R.string.failed_to_open_app_settings_toast, + mImi.loadLabel(context.getPackageManager())); + Toast.makeText(context, message, Toast.LENGTH_LONG).show(); + } + return true; + } + + public void updatePreferenceViews() { + final boolean isAlwaysChecked = mInputMethodSettingValues.isAlwaysCheckedIme( + mImi, getContext()); + // When this preference has a switch and an input method should be always enabled, + // this preference should be disabled to prevent accidentally disabling an input method. + // This preference should also be disabled in case the admin does not allow this input + // method. + if (isAlwaysChecked && isImeEnabler()) { + setDisabledByAdmin(null); + setEnabled(false); + } else if (!mIsAllowedByOrganization) { + EnforcedAdmin admin = + RestrictedLockUtils.checkIfInputMethodDisallowed(getContext(), + mImi.getPackageName(), UserHandle.myUserId()); + setDisabledByAdmin(admin); + } else { + setEnabled(true); + } + setChecked(mInputMethodSettingValues.isEnabledImi(mImi)); + if (!isDisabledByAdmin()) { + setSummary(getSummaryString()); + } + } + + private InputMethodManager getInputMethodManager() { + return (InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + } + + private String getSummaryString() { + final InputMethodManager imm = getInputMethodManager(); + final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(mImi, true); + return InputMethodAndSubtypeUtil.getSubtypeLocaleNameListAsSentence( + subtypes, getContext(), mImi); + } + + private void setCheckedInternal(boolean checked) { + super.setChecked(checked); + mOnSaveListener.onSaveInputMethodPreference(InputMethodPreference.this); + notifyChanged(); + } + + private void showSecurityWarnDialog() { + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + } + final Context context = getContext(); + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setCancelable(true /* cancelable */); + builder.setTitle(android.R.string.dialog_alert_title); + final CharSequence label = mImi.getServiceInfo().applicationInfo.loadLabel( + context.getPackageManager()); + builder.setMessage(context.getString(R.string.ime_security_warning, label)); + builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { + // The user confirmed to enable a 3rd party IME, but we might + // need to prompt if it's not Direct Boot aware. + // TV doesn't doesn't need to worry about this, but other platforms should show + // a warning. + if (mImi.getServiceInfo().directBootAware || isTv()) { + setCheckedInternal(true); + } else { + showDirectBootWarnDialog(); + } + }); + builder.setNegativeButton(android.R.string.cancel, (dialog, which) -> { + // The user canceled to enable a 3rd party IME. + setCheckedInternal(false); + }); + mDialog = builder.create(); + mDialog.show(); + } + + private boolean isTv() { + return (getContext().getResources().getConfiguration().uiMode + & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION; + } + + private void showDirectBootWarnDialog() { + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + } + final Context context = getContext(); + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setCancelable(true /* cancelable */); + builder.setMessage(context.getText(R.string.direct_boot_unaware_dialog_message)); + builder.setPositiveButton(android.R.string.ok, (dialog, which) -> setCheckedInternal(true)); + builder.setNegativeButton(android.R.string.cancel, + (dialog, which) -> setCheckedInternal(false)); + mDialog = builder.create(); + mDialog.show(); + } + + public int compareTo(final InputMethodPreference rhs, final Collator collator) { + if (this == rhs) { + return 0; + } + if (mHasPriorityInSorting == rhs.mHasPriorityInSorting) { + final CharSequence t0 = getTitle(); + final CharSequence t1 = rhs.getTitle(); + if (TextUtils.isEmpty(t0)) { + return 1; + } + if (TextUtils.isEmpty(t1)) { + return -1; + } + return collator.compare(t0.toString(), t1.toString()); + } + // Prefer always checked system IMEs + return mHasPriorityInSorting ? -1 : 1; + } +} diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java index e204a3a65c72..6a029f0be64d 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java @@ -110,6 +110,70 @@ public class ApplicationsStateTest { } @Test + public void testDownloadAndLauncherAndInstantAcceptsCorrectApps() { + // should include instant apps + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = false; + when(mEntry.info.isInstantApp()).thenReturn(true); + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry)) + .isTrue(); + + // should included updated system apps + when(mEntry.info.isInstantApp()).thenReturn(false); + mEntry.info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry)) + .isTrue(); + + // should not include system apps other than the home app + mEntry.info.flags = ApplicationInfo.FLAG_SYSTEM; + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = false; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry)) + .isFalse(); + + // should include the home app + mEntry.isHomeApp = true; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry)) + .isTrue(); + + // should include any System app with a launcher entry + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = true; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry)) + .isTrue(); + } + + @Test + public void testDownloadAndLauncherAcceptsCorrectApps() { + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = false; + + // should included updated system apps + when(mEntry.info.isInstantApp()).thenReturn(false); + mEntry.info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry)) + .isTrue(); + + // should not include system apps other than the home app + mEntry.info.flags = ApplicationInfo.FLAG_SYSTEM; + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = false; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry)) + .isFalse(); + + // should include the home app + mEntry.isHomeApp = true; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry)) + .isTrue(); + + // should include any System app with a launcher entry + mEntry.isHomeApp = false; + mEntry.hasLauncherEntry = true; + assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry)) + .isTrue(); + } + + @Test public void testInstantFilterAcceptsInstantApp() { when(mEntry.info.isInstantApp()).thenReturn(true); assertThat(ApplicationsState.FILTER_INSTANT.filterApp(mEntry)).isTrue(); diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ad39f5475554..a8cf3daec41e 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1962,4 +1962,31 @@ <!-- Quick settings tile for toggling mobile data [CHAR LIMIT=20] --> <string name="mobile_data">Mobile data</string> + <!-- Label for when wifi is off in QS detail panel [CHAR LIMIT=NONE] --> + <string name="wifi_is_off">Wi-Fi is off</string> + + <!-- Label for when bluetooth is off in QS detail panel [CHAR LIMIT=NONE] --> + <string name="bt_is_off">Bluetooth is off</string> + + <!-- Label for when Do not disturb is off in QS detail panel [CHAR LIMIT=NONE] --> + <string name="dnd_is_off">Do Not Disturb is off</string> + + <!-- Prompt for when Do not disturb is on from automatic rule in QS [CHAR LIMIT=NONE] --> + <string name="qs_dnd_prompt_auto_rule">Do Not Disturb was turned on by an automatic rule (<xliff:g name="rule">%s</xliff:g>). Keep current settings?</string> + + <!-- Prompt for when Do not disturb is on from app in QS [CHAR LIMIT=NONE] --> + <string name="qs_dnd_prompt_app">Do Not Disturb was turned on by an app (<xliff:g name="app">%s</xliff:g>). Keep current settings?</string> + + <!-- Prompt for when Do not disturb is on from automatic rule or app in QS [CHAR LIMIT=NONE] --> + <string name="qs_dnd_prompt_auto_rule_app">Do Not Disturb was turned on by an automatic rule or app. Keep current settings?</string> + + <!-- Description of Do Not Disturb option in QS that ends at the specified time[CHAR LIMIT=20] --> + <string name="qs_dnd_until">Until <xliff:g name="time">%s</xliff:g></string> + + <!-- Do Not Disturb button to keep the current settings [CHAR LIMIT=20] --> + <string name="qs_dnd_keep">Keep</string> + + <!-- Do Not Disturb button to change the current settings [CHAR LIMIT=20] --> + <string name="qs_dnd_replace">Replace</string> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 4b2c20f4f7db..5b9d95d3e2c0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -1,3 +1,4 @@ + /* * Copyright (C) 2014 The Android Open Source Project * @@ -358,9 +359,8 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic @Override public void onClick(View widget) { final Intent intent = new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); mDialog.dismiss(); - mContext.startActivity(intent); + mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); } @Override @@ -373,9 +373,8 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic @Override public void onClick(View widget) { final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); mDialog.dismiss(); - mContext.startActivity(intent); + mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 1c71da007fd0..3a43d304b1ca 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -355,9 +355,10 @@ public class SystemServicesProxy { rti.firstActiveTime = rti.lastActiveTime = i; if (i % 2 == 0) { rti.taskDescription = new ActivityManager.TaskDescription(description, - Bitmap.createBitmap(mDummyIcon), null, - 0xFF000000 | (0xFFFFFF & new Random().nextInt()), - 0xFF000000 | (0xFFFFFF & new Random().nextInt())); + Bitmap.createBitmap(mDummyIcon), null, + 0xFF000000 | (0xFFFFFF & new Random().nextInt()), + 0xFF000000 | (0xFFFFFF & new Random().nextInt()), + 0, 0); } else { rti.taskDescription = new ActivityManager.TaskDescription(); } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 97506e6df1c2..0ee3e1917117 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -58,6 +58,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; import com.android.internal.policy.DockedDividerUtils; +import com.android.internal.view.SurfaceFlingerVsyncChoreographer; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.recents.Recents; @@ -108,9 +109,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, private static final Interpolator IME_ADJUST_INTERPOLATOR = new PathInterpolator(0.2f, 0f, 0.1f, 1f); - private static final long ONE_MS_IN_NS = 1000000; - private static final long ONE_S_IN_NS = ONE_MS_IN_NS * 1000; - private static final int MSG_RESIZE_STACK = 0; private DividerHandleView mHandle; @@ -161,12 +159,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, private boolean mHomeStackResizable; private boolean mAdjustedForIme; private DividerState mState; - - /** - * The offset between vsync-app and vsync-surfaceflinger. See - * {@link #calculateAppSurfaceFlingerVsyncOffsetMs} why this is necessary. - */ - private long mSurfaceFlingerOffsetMs; + private SurfaceFlingerVsyncChoreographer mSfChoreographer; private final Handler mHandler = new Handler() { @Override @@ -319,7 +312,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, protected void onAttachedToWindow() { super.onAttachedToWindow(); EventBus.getDefault().register(this); - mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs(); + mSfChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, getDisplay()); } @Override @@ -328,25 +321,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, EventBus.getDefault().unregister(this); } - /** - * This method calculates the offset between vsync-surfaceflinger and vsync-app. If vsync-app - * is a couple of milliseconds before vsync-sf, a touch or animation event that causes the - * stacks to be resized are sometimes processed before the vsync-sf tick, and sometimes after, - * which leads to jank. Figure out this difference here and then post all the touch/animation - * events to start being processed at vsync-sf. - * - * @return The offset between vsync-app and vsync-sf, or 0 if vsync app happens after vsync-sf. - */ - private long calculateAppSurfaceFlingerVsyncOffsetMs() { - Display display = getDisplay(); - - // Calculate vsync offset from SurfaceFlinger. - // See frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:getDisplayConfigs - long vsyncPeriod = (long) (ONE_S_IN_NS / display.getRefreshRate()); - long sfVsyncOffset = vsyncPeriod - (display.getPresentationDeadlineNanos() - ONE_MS_IN_NS); - return Math.max(0, (sfVsyncOffset - display.getAppVsyncOffsetNanos()) / ONE_MS_IN_NS); - } - @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { if (mStableInsets.left != insets.getStableInsetLeft() @@ -630,8 +604,8 @@ public class DividerView extends FrameLayout implements OnTouchListener, delay = endDelay; } else if (mCancelled) { delay = 0; - } else if (mSurfaceFlingerOffsetMs != 0) { - delay = mSurfaceFlingerOffsetMs; + } else if (mSfChoreographer.getSurfaceFlingerOffsetMs() > 0) { + delay = mSfChoreographer.getSurfaceFlingerOffsetMs(); } if (delay == 0) { endAction.run(); @@ -916,14 +890,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, } public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) { - if (mSurfaceFlingerOffsetMs != 0) { - Message message = mHandler.obtainMessage(MSG_RESIZE_STACK, position, taskPosition, - taskSnapTarget); - message.setAsynchronous(true); - mHandler.sendMessageDelayed(message, mSurfaceFlingerOffsetMs); - } else { - resizeStack(position, taskPosition, taskSnapTarget); - } + Message message = mHandler.obtainMessage(MSG_RESIZE_STACK, position, taskPosition, + taskSnapTarget); + message.setAsynchronous(true); + mSfChoreographer.scheduleAtSfVsync(mHandler, message); } public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) { diff --git a/preloaded-classes b/preloaded-classes index a72a04272cba..892c59394855 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -1817,7 +1817,6 @@ android.text.FontConfig$Family android.text.FontConfig$Family$1 android.text.FontConfig$Font android.text.FontConfig$Font$1 -android.text.FontManager android.text.GetChars android.text.GraphicsOperations android.text.Html diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index f6d91f44b2cf..c4a28313ed0a 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -165,6 +165,14 @@ message SystemMessage { // Package: android NOTE_NET_LIMIT_SNOOZED = 36; + // Inform the user they need to sign in to an account + // Package: android, and others + NOTE_ACCOUNT_REQUIRE_SIGNIN = 37; + + // Inform the user that there has been a permission request for an account + // Package: android + NOTE_ACCOUNT_CREDENTIAL_PERMISSION = 38; + // ADD_NEW_IDS_ABOVE_THIS_LINE // Legacy IDs with arbitrary values appear below // Legacy IDs existed as stable non-conflicting constants prior to the O release @@ -216,9 +224,5 @@ message SystemMessage { // Notify the user that data or apps are being moved to external storage. // Package: com.android.systemui NOTE_STORAGE_MOVE = 0x534d4f56; - - // Account Manager allocates IDs sequentially, starting here. - // Package: android - ACCOUNT_MANAGER_BASE = 0x70000000; } } diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index c532efb79345..0acbb0258944 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -1282,7 +1282,9 @@ nAllocationSetSurface(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobje ANativeWindow *anw = nullptr; if (sur != 0) { + // Connect the native window handle to buffer queue. anw = ANativeWindow_fromSurface(_env, sur); + native_window_api_connect(anw, NATIVE_WINDOW_API_CPU); } rsAllocationSetSurface((RsContext)con, (RsAllocation)alloc, anw); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 087c24866146..1968d2e925aa 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -585,17 +585,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { result = mEnabledServicesForFeedbackTempList; result.clear(); List<Service> services = userState.mBoundServices; - while (feedbackType != 0) { - final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType)); - feedbackType &= ~feedbackTypeBit; - final int serviceCount = services.size(); - for (int i = 0; i < serviceCount; i++) { - Service service = services.get(i); - // Don't report the UIAutomation (fake service) - if (!sFakeAccessibilityServiceComponentName.equals(service.mComponentName) - && (service.mFeedbackType & feedbackTypeBit) != 0) { - result.add(service.mAccessibilityServiceInfo); - } + for (int serviceCount = services.size(), i = 0; i < serviceCount; ++i) { + Service service = services.get(i); + // Don't report the UIAutomation (fake service) + if (!sFakeAccessibilityServiceComponentName.equals(service.mComponentName) + && (service.mFeedbackType & feedbackType) != 0) { + result.add(service.mAccessibilityServiceInfo); } } } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 3d1c2511db27..dbf1e83f8eac 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -290,7 +290,8 @@ final class AutofillManagerServiceImpl { + " f=" + flags; mRequestsHistory.log(historyItem); - // TODO(b/33197203): Handle partitioning + // TODO(b/33197203): Handle scenario when user forced autofill after app was already + // autofilled. final Session session = mSessions.get(activityToken); if (session != null) { // Already started... diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index ac7d19eaba46..801769cc42e1 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -48,6 +48,7 @@ import android.service.autofill.Dataset; import android.service.autofill.FillResponse; import android.service.autofill.SaveInfo; import android.util.ArrayMap; +import android.util.DebugUtils; import android.util.Slog; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; @@ -112,20 +113,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") RemoteFillService mRemoteFillService; - // TODO(b/33197203 , b/35707731): Use List once it supports partitioning @GuardedBy("mLock") - private FillResponse mCurrentResponse; + private ArrayList<FillResponse> mResponses; /** - * Used to remember which {@link Dataset} filled the session. + * Response that requires a service authentitcation request. */ - // TODO(b/33197203 , b/35707731): will be removed once it supports partitioning @GuardedBy("mLock") - private Dataset mAutoFilledDataset; + private FillResponse mResponseWaitingAuth; /** * Dataset that when tapped launched a service authentication request. */ + @GuardedBy("mLock") private Dataset mDatasetWaitingAuth; /** @@ -163,8 +163,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mClient = IAutoFillManagerClient.Stub.asInterface(client); try { client.linkToDeath(() -> { - if (DEBUG) { - Slog.d(TAG, "app binder died"); + if (VERBOSE) { + Slog.v(TAG, "app binder died"); } removeSelf(); @@ -193,6 +193,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState notifyUnavailableToClient(); } synchronized (mLock) { + if (response.getAuthentication() != null) { + // TODO(b/33197203 , b/35707731): make sure it's ignored if there is one already + mResponseWaitingAuth = response; + } processResponseLocked(response); } @@ -318,23 +322,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } public void setAuthenticationResultLocked(Bundle data) { - if (mCurrentResponse == null || data == null) { + if ((mResponseWaitingAuth == null && mDatasetWaitingAuth == null) || data == null) { removeSelf(); } else { final Parcelable result = data.getParcelable( AutofillManager.EXTRA_AUTHENTICATION_RESULT); if (result instanceof FillResponse) { mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName); - - mCurrentResponse = (FillResponse) result; - processResponseLocked(mCurrentResponse); + mResponseWaitingAuth = null; + processResponseLocked((FillResponse) result); } else if (result instanceof Dataset) { final Dataset dataset = (Dataset) result; - final int index = mCurrentResponse.getDatasets().indexOf(mDatasetWaitingAuth); - if (index >= 0) { - mCurrentResponse.getDatasets().set(index, dataset); - autoFill(dataset); - mDatasetWaitingAuth = null; + for (int i = 0; i < mResponses.size(); i++) { + final FillResponse response = mResponses.get(i); + final int index = response.getDatasets().indexOf(mDatasetWaitingAuth); + if (index >= 0) { + response.getDatasets().set(index, dataset); + mDatasetWaitingAuth = null; + autoFill(dataset); + resetViewStatesLocked(dataset, ViewState.STATE_WAITING_DATASET_AUTH); + return; + } } } } @@ -354,15 +362,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.wtf(TAG, "showSaveLocked(): no mStructure"); return true; } - if (mCurrentResponse == null) { + if (mResponses == null) { // Happens when the activity / session was finished before the service replied, or // when the service cannot autofill it (and returned a null response). if (DEBUG) { - Slog.d(TAG, "showSaveLocked(): no mCurrentResponse"); + Slog.d(TAG, "showSaveLocked(): no responses on session"); } return true; } - final SaveInfo saveInfo = mCurrentResponse.getSaveInfo(); + + // TODO(b/33197203 , b/35707731): must iterate over all responses + final FillResponse response = mResponses.get(0); + + final SaveInfo saveInfo = response.getSaveInfo(); if (DEBUG) { Slog.d(TAG, "showSaveLocked(): saveInfo=" + saveInfo); } @@ -385,7 +397,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return true; } - // TODO(b/33197203 , b/35707731): refactor excessive calls to getCurrentValue() boolean allRequiredAreNotEmpty = true; boolean atLeastOneChanged = false; for (int i = 0; i < requiredIds.length; i++) { @@ -393,7 +404,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ViewState viewState = mViewStates.get(id); if (viewState == null) { Slog.w(TAG, "showSaveLocked(): no ViewState for required " + id); - continue; + allRequiredAreNotEmpty = false; + break; } final AutofillValue currentValue = viewState.getCurrentValue(); @@ -462,7 +474,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates); } - final Bundle extras = this.mCurrentResponse.getExtras(); + // TODO(b/33197203 , b/35707731): decide how to handle bundle in multiple partitions + final Bundle extras = mResponses != null ? mResponses.get(0).getExtras() : null; for (Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) { final AutofillValue value = entry.getValue().getCurrentValue(); @@ -497,16 +510,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) { - if (mAutoFilledDataset != null && (flags & FLAG_VALUE_CHANGED) == 0) { - // TODO(b/33197203): ignoring because we don't support partitions yet - Slog.d(TAG, "updateLocked(): ignoring " + id + " after app was autofilled"); - return; - } - ViewState viewState = mViewStates.get(id); + if (viewState == null) { - viewState = new ViewState(this, id, this, ViewState.STATE_INITIAL); - mViewStates.put(id, viewState); + if ((flags & (FLAG_START_SESSION | FLAG_VALUE_CHANGED)) != 0) { + if (DEBUG) { + Slog.d(TAG, "Creating viewState for " + id + " on " + getFlagAsString(flags)); + } + viewState = new ViewState(this, id, this, ViewState.STATE_INITIAL); + mViewStates.put(id, viewState); + } else if ((flags & FLAG_VIEW_ENTERED) != 0) { + viewState = startPartitionLocked(id); + } else { + if (VERBOSE) Slog.v(TAG, "Ignored " + getFlagAsString(flags) + " for " + id); + return; + } } if ((flags & FLAG_START_SESSION) != 0) { @@ -530,7 +548,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Update the internal state... viewState.setState(ViewState.STATE_CHANGED); - // ... and the chooser UI. + + //..and the UI if (value.isText()) { getUiForShowing().filterFillUi(value.getTextValue().toString()); } else { @@ -551,10 +570,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // If the ViewState is ready to be displayed, onReady() will be called. viewState.update(value, virtualBounds); - if (mCurrentResponse != null) { - viewState.setResponse(mCurrentResponse); - } - return; } @@ -566,7 +581,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - Slog.w(TAG, "updateLocked(): unknown flags " + flags); + Slog.w(TAG, "updateLocked(): unknown flags " + flags + ": " + getFlagAsString(flags)); + } + + private ViewState startPartitionLocked(AutofillId id) { + if (DEBUG) { + Slog.d(TAG, "Starting partition for view id " + id); + } + final ViewState viewState = + new ViewState(this, id, this,ViewState.STATE_STARTED_PARTITION); + mViewStates.put(id, viewState); + + /* + * TODO(b/33197203 , b/35707731): when start a new partition, it should + * + * - add autofilled fields as sanitized + * - set focus on ViewStructure that triggered it + * - pass the first onFillRequest() bundle + * - optional: perhaps add a new flag onFilLRequest() to indicate it's a new partition? + */ + mRemoteFillService.onFillRequest(mStructure, null, 0); + + return viewState; } @Override @@ -580,6 +616,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getUiForShowing().showFillUi(filledId, response, filterText, mPackageName); } + String getFlagAsString(int flag) { + return DebugUtils.flagsToString(AutofillManager.class, "FLAG_", flag); + } + private void notifyUnavailableToClient() { if (mCurrentViewId == null) { // TODO(b/33197203): temporary sanity check; should never happen @@ -597,8 +637,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private void processResponseLocked(FillResponse response) { if (DEBUG) { - Slog.d(TAG, "processResponseLocked(auth=" + response.getAuthentication() - + "):" + response); + Slog.d(TAG, "processResponseLocked(mCurrentViewId=" + mCurrentViewId + "):" + response); } if (mCurrentViewId == null) { @@ -607,7 +646,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - mCurrentResponse = response; + if (mResponses == null) { + mResponses = new ArrayList<>(4); + } + mResponses.add(response); setViewStatesLocked(response, ViewState.STATE_FILLABLE); @@ -669,10 +711,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + /** + * Resets the given state from all existing views in the given dataset. + */ + private void resetViewStatesLocked(@NonNull Dataset dataset, int state) { + final ArrayList<AutofillId> ids = dataset.getFieldIds(); + for (int j = 0; j < ids.size(); j++) { + final AutofillId id = ids.get(j); + final ViewState viewState = mViewStates.get(id); + if (viewState != null) { + viewState.resetState(state); + } + } + } + void autoFill(Dataset dataset) { synchronized (mLock) { - mAutoFilledDataset = dataset; - // Autofill it directly... if (dataset.getAuthentication() == null) { autoFillApp(dataset); @@ -680,7 +734,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // ...or handle authentication. + // TODO(b/33197203 , b/35707731): make sure it's ignored if there is one already mDatasetWaitingAuth = dataset; + setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH); final Intent fillInIntent = createAuthFillInIntent(mStructure, null); startAuthentication(dataset.getAuthentication(), fillInIntent); } @@ -690,8 +746,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return mService.getServiceName(); } - FillResponse getCurrentResponse() { - return mCurrentResponse; + FillResponse getResponseWaitingAuth() { + return mResponseWaitingAuth; } private Intent createAuthFillInIntent(AssistStructure structure, Bundle extras) { @@ -714,8 +770,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void dumpLocked(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); pw.print(prefix); pw.print("mFlags: "); pw.println(mFlags); - pw.print(prefix); pw.print("mCurrentResponse: "); pw.println(mCurrentResponse); - pw.print(prefix); pw.print("mAutoFilledDataset: "); pw.println(mAutoFilledDataset); + pw.print(prefix); pw.print("mResponses: "); pw.println(mResponses); + pw.print(prefix); pw.print("mResponseWaitingAuth: "); pw.println(mResponseWaitingAuth); pw.print(prefix); pw.print("mDatasetWaitingAuth: "); pw.println(mDatasetWaitingAuth); pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId); pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size()); @@ -811,4 +867,4 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState destroyLocked(); mService.removeSessionLocked(mActivityToken); } -}
\ No newline at end of file +} diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index 3aba723b7e5f..549f231367c6 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -16,10 +16,13 @@ package com.android.server.autofill; +import static com.android.server.autofill.Helper.DEBUG; + import android.annotation.Nullable; import android.graphics.Rect; import android.service.autofill.FillResponse; import android.util.DebugUtils; +import android.util.Slog; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; @@ -40,6 +43,8 @@ final class ViewState { @Nullable AutofillValue value); } + private static final String TAG = "ViewState"; + // NOTE: state constants must be public because of flagstoString(). public static final int STATE_UNKNOWN = 0x00; /** Initial state. */ @@ -52,6 +57,10 @@ final class ViewState { public static final int STATE_CHANGED = 0x08; /** Set only in the View that started a session. */ public static final int STATE_STARTED_SESSION = 0x10; + /** View that started a new partition when focused on. */ + public static final int STATE_STARTED_PARTITION = 0x20; + /** User select a dataset in this view, but service must authenticate first. */ + public static final int STATE_WAITING_DATASET_AUTH = 0x40; public final AutofillId id; private final Listener mListener; @@ -122,9 +131,15 @@ final class ViewState { } void setState(int state) { - // TODO(b/33197203 , b/35707731): currently it's always setting one state, but once it - // supports partitioning it will need to 'or' some of them.. - mState = state; + if (mState == STATE_INITIAL) { + mState = state; + } else { + mState |= state; + } + } + + void resetState(int state) { + mState &= ~state; } // TODO(b/33197203): need to refactor / rename / document this method to make it clear that @@ -147,6 +162,12 @@ final class ViewState { * fill UI is ready to be displayed (i.e. when response and bounds are set). */ void maybeCallOnFillReady() { + if ((mState & (STATE_AUTOFILLED | STATE_WAITING_DATASET_AUTH)) != 0) { + if (DEBUG) { + Slog.d(TAG, "Ignoring UI for " + id + " on " + getStateAsString()); + } + return; + } // First try the current response associated with this View. if (mResponse != null) { if (mResponse.getDatasets() != null) { @@ -155,9 +176,9 @@ final class ViewState { return; } // Then checks if the session has a response waiting authentication; if so, uses it instead. - final FillResponse currentResponse = mSession.getCurrentResponse(); - if (currentResponse.getAuthentication() != null) { - mListener.onFillReady(currentResponse, this.id, mCurrentValue); + final FillResponse responseWaitingAuth = mSession.getResponseWaitingAuth(); + if (responseWaitingAuth != null) { + mListener.onFillReady(responseWaitingAuth, this.id, mCurrentValue); } } diff --git a/services/core/Android.mk b/services/core/Android.mk index 099f557ad416..8003d21d165e 100644 --- a/services/core/Android.mk +++ b/services/core/Android.mk @@ -39,8 +39,8 @@ endif LOCAL_JACK_FLAGS := \ -D jack.transformations.boost-locked-region-priority=true \ - -D jack.transformations.boost-locked-region-priority.classname=com.android.server.am.ActivityManagerService \ - -D jack.transformations.boost-locked-region-priority.request=com.android.server.am.ActivityManagerService\#boostPriorityForLockedSection \ - -D jack.transformations.boost-locked-region-priority.reset=com.android.server.am.ActivityManagerService\#resetPriorityAfterLockedSection + -D jack.transformations.boost-locked-region-priority.classname=com.android.server.am.ActivityManagerService,com.android.server.wm.WindowHashMap \ + -D jack.transformations.boost-locked-region-priority.request=com.android.server.am.ActivityManagerService\#boostPriorityForLockedSection,com.android.server.wm.WindowManagerService\#boostPriorityForLockedSection \ + -D jack.transformations.boost-locked-region-priority.reset=com.android.server.am.ActivityManagerService\#resetPriorityAfterLockedSection,com.android.server.wm.WindowManagerService\#resetPriorityAfterLockedSection include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 0e752ffdb5a5..25ac008fa586 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -29,13 +29,6 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; -import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; -import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED; -import static android.net.NetworkPolicyManager.RULE_NONE; -import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; -import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; -import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; -import static android.net.NetworkPolicyManager.uidRulesToString; import android.annotation.Nullable; import android.app.BroadcastOptions; @@ -104,7 +97,6 @@ import android.security.Credentials; import android.security.KeyStore; import android.telephony.TelephonyManager; import android.text.TextUtils; -import android.util.ArraySet; import android.util.LocalLog; import android.util.LocalLog.ReadOnlyLocalLog; import android.util.Log; @@ -130,6 +122,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; import com.android.internal.util.WakeupMessage; import com.android.internal.util.XmlUtils; +import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.DataConnectionStats; import com.android.server.connectivity.KeepaliveTracker; @@ -147,6 +140,7 @@ import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; import com.android.server.net.BaseNetworkObserver; import com.android.server.net.LockdownVpnTracker; +import com.android.server.net.NetworkPolicyManagerInternal; import com.google.android.collect.Lists; @@ -221,18 +215,6 @@ public class ConnectivityService extends IConnectivityManager.Stub private boolean mLockdownEnabled; private LockdownVpnTracker mLockdownTracker; - /** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */ - private Object mRulesLock = new Object(); - /** Currently active network rules by UID. */ - @GuardedBy("mRulesLock") - private SparseIntArray mUidRules = new SparseIntArray(); - /** Set of ifaces that are costly. */ - @GuardedBy("mRulesLock") - private ArraySet<String> mMeteredIfaces = new ArraySet<>(); - /** Flag indicating if background data is restricted. */ - @GuardedBy("mRulesLock") - private boolean mRestrictBackground; - final private Context mContext; private int mNetworkPreference; // 0 is full bad, 100 is full good @@ -246,6 +228,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private INetworkManagementService mNetd; private INetworkStatsService mStatsService; private INetworkPolicyManager mPolicyManager; + private NetworkPolicyManagerInternal mPolicyManagerInternal; private String mCurrentTcpBufferSizes; @@ -715,12 +698,15 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetd = checkNotNull(netManager, "missing INetworkManagementService"); mStatsService = checkNotNull(statsService, "missing INetworkStatsService"); mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager"); + mPolicyManagerInternal = checkNotNull( + LocalServices.getService(NetworkPolicyManagerInternal.class), + "missing NetworkPolicyManagerInternal"); + mKeyStore = KeyStore.getInstance(); mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); try { - mPolicyManager.setConnectivityListener(mPolicyListener); - mRestrictBackground = mPolicyManager.getRestrictBackground(); + mPolicyManager.registerListener(mPolicyListener); } catch (RemoteException e) { // ouch, no rules updates means some processes may never get network loge("unable to register INetworkPolicyListener" + e); @@ -991,51 +977,22 @@ public class ConnectivityService extends IConnectivityManager.Stub private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid, boolean ignoreBlocked) { // Networks aren't blocked when ignoring blocked status - if (ignoreBlocked) return false; + if (ignoreBlocked) { + return false; + } // Networks are never blocked for system services - if (isSystem(uid)) return false; - - final boolean networkMetered; - final int uidRules; - + // TODO: consider moving this check to NetworkPolicyManagerInternal.isUidNetworkingBlocked. + if (isSystem(uid)) { + return false; + } synchronized (mVpns) { final Vpn vpn = mVpns.get(UserHandle.getUserId(uid)); if (vpn != null && vpn.isBlockingUid(uid)) { return true; } } - final String iface = (lp == null ? "" : lp.getInterfaceName()); - synchronized (mRulesLock) { - networkMetered = mMeteredIfaces.contains(iface); - uidRules = mUidRules.get(uid, RULE_NONE); - } - - boolean allowed = true; - // Check Data Saver Mode first... - if (networkMetered) { - if ((uidRules & RULE_REJECT_METERED) != 0) { - if (LOGD_RULES) Log.d(TAG, "uid " + uid + " is blacklisted"); - // Explicitly blacklisted. - allowed = false; - } else { - allowed = !mRestrictBackground - || (uidRules & RULE_ALLOW_METERED) != 0 - || (uidRules & RULE_TEMPORARY_ALLOW_METERED) != 0; - if (LOGD_RULES) Log.d(TAG, "allowed status for uid " + uid + " when" - + " mRestrictBackground=" + mRestrictBackground - + ", whitelisted=" + ((uidRules & RULE_ALLOW_METERED) != 0) - + ", tempWhitelist= + ((uidRules & RULE_TEMPORARY_ALLOW_METERED) != 0)" - + ": " + allowed); - } - } - // ...then power restrictions. - if (allowed) { - allowed = (uidRules & RULE_REJECT_ALL) == 0; - if (LOGD_RULES) Log.d(TAG, "allowed status for uid " + uid + " when" - + " rule is " + uidRulesToString(uidRules) + ": " + allowed); - } - return !allowed; + return mPolicyManagerInternal.isUidNetworkingBlocked(uid, iface); } private void maybeLogBlockedNetworkInfo(NetworkInfo ni, int uid) { @@ -1481,67 +1438,24 @@ public class ConnectivityService extends IConnectivityManager.Stub return true; } - private INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() { + private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() { @Override public void onUidRulesChanged(int uid, int uidRules) { - // caller is NPMS, since we only register with them - if (LOGD_RULES) { - log("onUidRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")"); - } - - synchronized (mRulesLock) { - // skip update when we've already applied rules - final int oldRules = mUidRules.get(uid, RULE_NONE); - if (oldRules == uidRules) return; - - if (uidRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, uidRules); - } - } - // TODO: notify UID when it has requested targeted updates } - @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { - // caller is NPMS, since we only register with them - if (LOGD_RULES) { - log("onMeteredIfacesChanged(ifaces=" + Arrays.toString(meteredIfaces) + ")"); - } - - synchronized (mRulesLock) { - mMeteredIfaces.clear(); - for (String iface : meteredIfaces) { - mMeteredIfaces.add(iface); - } - } } - @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { - // caller is NPMS, since we only register with them - if (LOGD_RULES) { - log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")"); - } - - synchronized (mRulesLock) { - mRestrictBackground = restrictBackground; - } - + // TODO: relocate this specific callback in Tethering. if (restrictBackground) { log("onRestrictBackgroundChanged(true): disabling tethering"); mTethering.untetherAll(); } } - @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { - // caller is NPMS, since we only register with them - if (LOGD_RULES) { - log("onUidRulesChanged(uid=" + uid + ", uidPolicies=" + uidPolicies + ")"); - } } }; @@ -1976,33 +1890,6 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.decreaseIndent(); pw.println(); - pw.println("Metered Interfaces:"); - pw.increaseIndent(); - for (String value : mMeteredIfaces) { - pw.println(value); - } - pw.decreaseIndent(); - pw.println(); - - pw.print("Restrict background: "); - pw.println(mRestrictBackground); - pw.println(); - - pw.println("Status for known UIDs:"); - pw.increaseIndent(); - final int size = mUidRules.size(); - for (int i = 0; i < size; i++) { - final int uid = mUidRules.keyAt(i); - pw.print("UID="); - pw.print(uid); - final int uidRules = mUidRules.get(uid, RULE_NONE); - pw.print(" rules="); - pw.print(uidRulesToString(uidRules)); - pw.println(); - } - pw.println(); - pw.decreaseIndent(); - pw.println("Network Requests:"); pw.increaseIndent(); for (NetworkRequestInfo nri : mNetworkRequests.values()) { @@ -3437,6 +3324,10 @@ public class ConnectivityService extends IConnectivityManager.Stub Slog.e(TAG, s); } + private static void loge(String s, Throwable t) { + Slog.e(TAG, s, t); + } + private static <T> T checkNotNull(T value, String message) { if (value == null) { throw new NullPointerException(message); @@ -4197,20 +4088,16 @@ public class ConnectivityService extends IConnectivityManager.Stub private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) { final int uid = Binder.getCallingUid(); if (isSystem(uid)) { + // Exemption for system uid. return; } - // if UID is restricted, don't allow them to bring up metered APNs - if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) == false) { - final int uidRules; - synchronized(mRulesLock) { - uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); - } - if (mRestrictBackground && (uidRules & RULE_ALLOW_METERED) == 0 - && (uidRules & RULE_TEMPORARY_ALLOW_METERED) == 0) { - // we could silently fail or we can filter the available nets to only give - // them those they have access to. Chose the more useful option. - networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); - } + if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) { + // Policy already enforced. + return; + } + if (mPolicyManagerInternal.isUidRestrictedOnMeteredNetworks(uid)) { + // If UID is restricted, don't allow them to bring up metered APNs. + networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); } } diff --git a/services/core/java/com/android/server/FontManagerService.java b/services/core/java/com/android/server/FontManagerService.java deleted file mode 100644 index f1726473c591..000000000000 --- a/services/core/java/com/android/server/FontManagerService.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.content.Context; -import android.graphics.FontListParser; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.text.FontConfig; -import android.util.Slog; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.font.IFontManager; - -import org.xmlpull.v1.XmlPullParserException; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; - -public class FontManagerService extends IFontManager.Stub { - private static final String TAG = "FontManagerService"; - private static final String FONTS_CONFIG = "/system/etc/fonts.xml"; - private static final String SYSTEM_FONT_DIR = "/system/fonts/"; - - @GuardedBy("mLock") - private FontConfig mConfig; - private final Object mLock = new Object(); - - public static final class Lifecycle extends SystemService { - private final FontManagerService mService; - - public Lifecycle(Context context) { - super(context); - mService = new FontManagerService(); - } - - @Override - public void onStart() { - try { - publishBinderService(Context.FONT_SERVICE, mService); - } catch (Throwable t) { - // Starting this service is not critical to the running of this device and should - // therefore not crash the device. If it fails, log the error and continue. - Slog.e(TAG, "Could not start the FontManagerService.", t); - } - } - } - - @Override - public FontConfig getSystemFonts() { - synchronized (mLock) { - if (mConfig != null) { - return mConfig; - } - - mConfig = loadFromSystem(); - if (mConfig == null) { - return null; - } - - for (FontConfig.Family family : mConfig.getFamilies()) { - for (FontConfig.Font font : family.getFonts()) { - File fontFile = new File(SYSTEM_FONT_DIR, font.getFontName()); - font.setUri(Uri.fromFile(fontFile)); - } - } - - return mConfig; - } - } - - private FontConfig loadFromSystem() { - File configFilename = new File(FONTS_CONFIG); - try { - FileInputStream fontsIn = new FileInputStream(configFilename); - return FontListParser.parse(fontsIn); - } catch (IOException | XmlPullParserException e) { - Slog.e(TAG, "Error opening " + configFilename, e); - } - return null; - } - - public FontManagerService() { - } -} diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index a5debda34a01..9218c25a9860 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -280,6 +280,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { if (DBG) Log.d(TAG, "refreshBinding()"); // Make sure the scorer is up-to-date mNetworkScorerAppManager.updateState(); + mNetworkScorerAppManager.migrateNetworkScorerAppSettingIfNeeded(); registerPackageMonitorIfNeeded(); bindToScoringServiceIfNeeded(); } diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java index 1a3bb357eb7b..e127eb9c44af 100644 --- a/services/core/java/com/android/server/NetworkScorerAppManager.java +++ b/services/core/java/com/android/server/NetworkScorerAppManager.java @@ -42,7 +42,6 @@ import java.util.List; * * @hide */ -@VisibleForTesting public class NetworkScorerAppManager { private static final String TAG = "NetworkScorerAppManager"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -64,8 +63,7 @@ public class NetworkScorerAppManager { * Returns the list of available scorer apps. The list will be empty if there are * no valid scorers. */ - @VisibleForTesting - public List<NetworkScorerAppData> getAllValidScorers() { + List<NetworkScorerAppData> getAllValidScorers() { if (VERBOSE) Log.v(TAG, "getAllValidScorers()"); final PackageManager pm = mContext.getPackageManager(); final Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS); @@ -170,8 +168,7 @@ public class NetworkScorerAppManager { * it was disabled or uninstalled). */ @Nullable - @VisibleForTesting - public NetworkScorerAppData getActiveScorer() { + NetworkScorerAppData getActiveScorer() { final int enabledSetting = getNetworkRecommendationsEnabledSetting(); if (enabledSetting == NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF) { return null; @@ -214,8 +211,7 @@ public class NetworkScorerAppManager { * @return true if the scorer was changed, or false if the package is not a valid scorer or * a valid network recommendation provider exists. */ - @VisibleForTesting - public boolean setActiveScorer(String packageName) { + boolean setActiveScorer(String packageName) { final String oldPackageName = getNetworkRecommendationsPackage(); if (TextUtils.equals(oldPackageName, packageName)) { @@ -250,8 +246,7 @@ public class NetworkScorerAppManager { * is no longer valid then {@link Settings.Global#NETWORK_RECOMMENDATIONS_ENABLED} will be set * to <code>0</code> (disabled). */ - @VisibleForTesting - public void updateState() { + void updateState() { final int enabledSetting = getNetworkRecommendationsEnabledSetting(); if (enabledSetting == NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF) { // Don't change anything if it's forced off. @@ -286,6 +281,56 @@ public class NetworkScorerAppManager { } } + /** + * Migrates the NETWORK_SCORER_APP Setting to the USE_OPEN_WIFI_PACKAGE Setting. + */ + void migrateNetworkScorerAppSettingIfNeeded() { + final String scorerAppPkgNameSetting = + mSettingsFacade.getString(mContext, Settings.Global.NETWORK_SCORER_APP); + if (TextUtils.isEmpty(scorerAppPkgNameSetting)) { + // Early exit, nothing to do. + return; + } + + final NetworkScorerAppData currentAppData = getActiveScorer(); + if (currentAppData == null) { + // Don't touch anything until we have an active scorer to work with. + return; + } + + if (DEBUG) { + Log.d(TAG, "Migrating Settings.Global.NETWORK_SCORER_APP " + + "(" + scorerAppPkgNameSetting + ")..."); + } + + // If the new (useOpenWifi) Setting isn't set and the old Setting's value matches the + // new metadata value then update the new Setting with the old value. Otherwise it's a + // mismatch so we shouldn't enable the Setting automatically. + final ComponentName enableUseOpenWifiActivity = + currentAppData.getEnableUseOpenWifiActivity(); + final String useOpenWifiSetting = + mSettingsFacade.getString(mContext, Settings.Global.USE_OPEN_WIFI_PACKAGE); + if (TextUtils.isEmpty(useOpenWifiSetting) + && enableUseOpenWifiActivity != null + && scorerAppPkgNameSetting.equals(enableUseOpenWifiActivity.getPackageName())) { + mSettingsFacade.putString(mContext, Settings.Global.USE_OPEN_WIFI_PACKAGE, + scorerAppPkgNameSetting); + if (DEBUG) { + Log.d(TAG, "Settings.Global.USE_OPEN_WIFI_PACKAGE set to " + + "'" + scorerAppPkgNameSetting + "'."); + } + } + + // Clear out the old setting so we don't run through the migration code again. + mSettingsFacade.putString(mContext, Settings.Global.NETWORK_SCORER_APP, null); + if (DEBUG) { + Log.d(TAG, "Settings.Global.NETWORK_SCORER_APP migration complete."); + final String setting = + mSettingsFacade.getString(mContext, Settings.Global.USE_OPEN_WIFI_PACKAGE); + Log.d(TAG, "Settings.Global.USE_OPEN_WIFI_PACKAGE is: '" + setting + "'."); + } + } + private String getDefaultPackageSetting() { return mContext.getResources().getString( R.string.config_defaultNetworkRecommendationProviderPackage); diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index 421d5a6ab964..c105b1244e74 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -16,6 +16,7 @@ package com.android.server; +import android.app.ActivityThread; import android.content.Context; import android.os.IBinder; import android.os.ServiceManager; @@ -104,6 +105,16 @@ public abstract class SystemService { } /** + * Get the system UI context. This context is to be used for displaying UI. It is themable, + * which means resources can be overridden at runtime. Do not use to retrieve properties that + * configure the behavior of the device that is not UX related. + */ + public final Context getUiContext() { + // This has already been set up by the time any SystemServices are created. + return ActivityThread.currentActivityThread().getSystemUiContext(); + } + + /** * Returns true if the system is running in safe mode. * TODO: we should define in which phase this becomes valid */ diff --git a/services/core/java/com/android/server/ThreadPriorityBooster.java b/services/core/java/com/android/server/ThreadPriorityBooster.java new file mode 100644 index 000000000000..17965d099af0 --- /dev/null +++ b/services/core/java/com/android/server/ThreadPriorityBooster.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server; + +import android.os.Process; + +/** + * Utility class to boost threads in sections where important locks are held. + */ +public class ThreadPriorityBooster { + + private final int mBoostToPriority; + private final int mLockGuardIndex; + + private final ThreadLocal<PriorityState> mThreadState = new ThreadLocal<PriorityState>() { + @Override protected PriorityState initialValue() { + return new PriorityState(); + } + }; + + public ThreadPriorityBooster(int boostToPriority, int lockGuardIndex) { + mBoostToPriority = boostToPriority; + mLockGuardIndex = lockGuardIndex; + } + + public void boost() { + final int tid = Process.myTid(); + final int prevPriority = Process.getThreadPriority(tid); + PriorityState state = mThreadState.get(); + if (state.regionCounter == 0 && prevPriority > mBoostToPriority) { + state.prevPriority = prevPriority; + Process.setThreadPriority(tid, mBoostToPriority); + } + state.regionCounter++; + if (LockGuard.ENABLED) { + LockGuard.guard(mLockGuardIndex); + } + } + + public void reset() { + PriorityState state = mThreadState.get(); + state.regionCounter--; + if (state.regionCounter == 0 && state.prevPriority > mBoostToPriority) { + Process.setThreadPriority(Process.myTid(), state.prevPriority); + } + } + + private static class PriorityState { + + /** + * Acts as counter for number of synchronized region that needs to acquire 'this' as a lock + * the current thread is currently in. When it drops down to zero, we will no longer boost + * the thread's priority. + */ + int regionCounter; + + /** + * The thread's previous priority before boosting. + */ + int prevPriority; + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index d996ee282ef9..e560d325e6dd 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -123,7 +123,6 @@ import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; /** @@ -191,17 +190,14 @@ public class AccountManagerService } private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>(); - private final AtomicInteger mNotificationIds = - new AtomicInteger(SystemMessage.ACCOUNT_MANAGER_BASE); static class UserAccounts { private final int userId; final AccountsDb accountsDb; - private final HashMap<Pair<Pair<Account, String>, Integer>, Integer> - credentialsPermissionNotificationIds = - new HashMap<Pair<Pair<Account, String>, Integer>, Integer>(); - private final HashMap<Account, Integer> signinRequiredNotificationIds = - new HashMap<Account, Integer>(); + private final HashMap<Pair<Pair<Account, String>, Integer>, NotificationId> + credentialsPermissionNotificationIds = new HashMap<>(); + private final HashMap<Account, NotificationId> signinRequiredNotificationIds + = new HashMap<>(); final Object cacheLock = new Object(); final Object dbLock = new Object(); // if needed, dbLock must be obtained before cacheLock /** protected by the {@link #cacheLock} */ @@ -1863,12 +1859,12 @@ public class AccountManagerService */ cancelNotification( getSigninRequiredNotificationId(accounts, accountToRename), - new UserHandle(accounts.userId)); + new UserHandle(accounts.userId)); synchronized(accounts.credentialsPermissionNotificationIds) { for (Pair<Pair<Account, String>, Integer> pair: accounts.credentialsPermissionNotificationIds.keySet()) { if (accountToRename.equals(pair.first.first)) { - int id = accounts.credentialsPermissionNotificationIds.get(pair); + NotificationId id = accounts.credentialsPermissionNotificationIds.get(pair); cancelNotification(id, new UserHandle(accounts.userId)); } } @@ -2021,7 +2017,7 @@ public class AccountManagerService for (Pair<Pair<Account, String>, Integer> pair: accounts.credentialsPermissionNotificationIds.keySet()) { if (account.equals(pair.first.first)) { - int id = accounts.credentialsPermissionNotificationIds.get(pair); + NotificationId id = accounts.credentialsPermissionNotificationIds.get(pair); cancelNotification(id, user); } } @@ -2912,8 +2908,8 @@ public class AccountManagerService // the intent from a non-Activity context. This is the default behavior. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } - intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account, - authTokenType, uid) + (packageName != null ? packageName : ""))); + intent.addCategory(getCredentialPermissionNotificationId(account, + authTokenType, uid).mTag + (packageName != null ? packageName : "")); intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account); intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType); intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response); @@ -2922,33 +2918,39 @@ public class AccountManagerService return intent; } - private Integer getCredentialPermissionNotificationId(Account account, String authTokenType, - int uid) { - Integer id; + private NotificationId getCredentialPermissionNotificationId(Account account, + String authTokenType, int uid) { + NotificationId nId; UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); synchronized (accounts.credentialsPermissionNotificationIds) { final Pair<Pair<Account, String>, Integer> key = new Pair<Pair<Account, String>, Integer>( new Pair<Account, String>(account, authTokenType), uid); - id = accounts.credentialsPermissionNotificationIds.get(key); - if (id == null) { - id = mNotificationIds.incrementAndGet(); - accounts.credentialsPermissionNotificationIds.put(key, id); + nId = accounts.credentialsPermissionNotificationIds.get(key); + if (nId == null) { + String tag = TAG + ":" + SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION + + ":" + account.hashCode() + ":" + authTokenType.hashCode(); + int id = SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION; + nId = new NotificationId(tag, id); + accounts.credentialsPermissionNotificationIds.put(key, nId); } } - return id; + return nId; } - private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) { - Integer id; + private NotificationId getSigninRequiredNotificationId(UserAccounts accounts, Account account) { + NotificationId nId; synchronized (accounts.signinRequiredNotificationIds) { - id = accounts.signinRequiredNotificationIds.get(account); - if (id == null) { - id = mNotificationIds.incrementAndGet(); - accounts.signinRequiredNotificationIds.put(account, id); + nId = accounts.signinRequiredNotificationIds.get(account); + if (nId == null) { + String tag = TAG + ":" + SystemMessage.NOTE_ACCOUNT_REQUIRE_SIGNIN + + ":" + account.hashCode(); + int id = SystemMessage.NOTE_ACCOUNT_REQUIRE_SIGNIN; + nId = new NotificationId(tag, id); + accounts.signinRequiredNotificationIds.put(account, nId); } } - return id; + return nId; } @Override @@ -4931,8 +4933,8 @@ public class AccountManagerService createNoCredentialsPermissionNotification(account, intent, packageName, userId); } else { Context contextForUser = getContextForUser(new UserHandle(userId)); - final Integer notificationId = getSigninRequiredNotificationId(accounts, account); - intent.addCategory(String.valueOf(notificationId)); + final NotificationId id = getSigninRequiredNotificationId(accounts, account); + intent.addCategory(id.mTag); final String notificationTitleFormat = contextForUser.getText(R.string.notification_title).toString(); @@ -4948,21 +4950,21 @@ public class AccountManagerService mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(userId))) .build(); - installNotification(notificationId, n, packageName, userId); + installNotification(id, n, packageName, userId); } } finally { restoreCallingIdentity(identityToken); } } - private void installNotification(int notificationId, final Notification notification, + private void installNotification(NotificationId id, final Notification notification, String packageName, int userId) { final long token = clearCallingIdentity(); try { INotificationManager notificationManager = mInjector.getNotificationManager(); try { - notificationManager.enqueueNotificationWithTag(packageName, packageName, null, - notificationId, notification, new int[1], userId); + notificationManager.enqueueNotificationWithTag(packageName, packageName, + id.mTag, id.mId, notification, new int[1], userId); } catch (RemoteException e) { /* ignore - local call */ } @@ -4971,15 +4973,15 @@ public class AccountManagerService } } - private void cancelNotification(int id, UserHandle user) { + private void cancelNotification(NotificationId id, UserHandle user) { cancelNotification(id, mContext.getPackageName(), user); } - private void cancelNotification(int id, String packageName, UserHandle user) { + private void cancelNotification(NotificationId id, String packageName, UserHandle user) { long identityToken = clearCallingIdentity(); try { INotificationManager service = mInjector.getNotificationManager(); - service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier()); + service.cancelNotificationWithTag(packageName, id.mTag, id.mId, user.getIdentifier()); } catch (RemoteException e) { /* ignore - local call */ } finally { @@ -5893,4 +5895,14 @@ public class AccountManagerService return NotificationManager.getService(); } } + + private class NotificationId { + final String mTag; + private final int mId; + + NotificationId(String tag, int type) { + mTag = tag; + mId = type; + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b4ea49b9430d..19fc2b876c3e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17,12 +17,12 @@ package com.android.server.am; import static android.Manifest.permission.CHANGE_CONFIGURATION; +import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; -import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; @@ -42,13 +42,49 @@ import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION; +import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode; +import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground; import static android.os.Build.VERSION_CODES.N; +import static android.os.Process.BLUETOOTH_UID; +import static android.os.Process.FIRST_APPLICATION_UID; +import static android.os.Process.FIRST_ISOLATED_UID; +import static android.os.Process.LAST_ISOLATED_UID; +import static android.os.Process.NFC_UID; +import static android.os.Process.PHONE_UID; import static android.os.Process.PROC_CHAR; import static android.os.Process.PROC_OUT_LONG; import static android.os.Process.PROC_PARENS; import static android.os.Process.PROC_SPACE_TERM; -import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode; -import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground; +import static android.os.Process.ProcessStartResult; +import static android.os.Process.ROOT_UID; +import static android.os.Process.SCHED_FIFO; +import static android.os.Process.SCHED_OTHER; +import static android.os.Process.SCHED_RESET_ON_FORK; +import static android.os.Process.SHELL_UID; +import static android.os.Process.SIGNAL_QUIT; +import static android.os.Process.SIGNAL_USR1; +import static android.os.Process.SYSTEM_UID; +import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE; +import static android.os.Process.THREAD_GROUP_DEFAULT; +import static android.os.Process.THREAD_GROUP_TOP_APP; +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; +import static android.os.Process.THREAD_PRIORITY_FOREGROUND; +import static android.os.Process.getFreeMemory; +import static android.os.Process.getThreadPriority; +import static android.os.Process.getTotalMemory; +import static android.os.Process.isThreadInProcess; +import static android.os.Process.killProcess; +import static android.os.Process.killProcessQuiet; +import static android.os.Process.myPid; +import static android.os.Process.myUid; +import static android.os.Process.readProcFile; +import static android.os.Process.removeAllProcessGroups; +import static android.os.Process.sendSignal; +import static android.os.Process.setProcessGroup; +import static android.os.Process.setThreadPriority; +import static android.os.Process.setThreadScheduler; +import static android.os.Process.startWebView; +import static android.os.Process.zygoteProcess; import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES; import static android.provider.Settings.Global.DEBUG_APP; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; @@ -323,7 +359,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; -import com.android.internal.notification.SystemNotificationChannels; import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -341,6 +376,7 @@ import com.android.internal.app.procstats.ProcessStats; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.IResultReceiver; @@ -367,6 +403,7 @@ import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.SystemServiceManager; +import com.android.server.ThreadPriorityBooster; import com.android.server.Watchdog; import com.android.server.am.ActivityStack.ActivityState; import com.android.server.firewall.IntentFirewall; @@ -409,7 +446,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import dalvik.system.VMRuntime; - import libcore.io.IoUtils; import libcore.util.EmptyArray; @@ -547,7 +583,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Maximum number of persisted Uri grants a package is allowed static final int MAX_PERSISTED_URI_GRANTS = 128; - static final int MY_PID = Process.myPid(); + static final int MY_PID = myPid(); static final String[] EMPTY_STRING_ARRAY = new String[0]; @@ -680,13 +716,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (mTopAppVrThreadTid > 0) { // Ensure that when entering persistent VR mode the last top-app loses // SCHED_FIFO. - Process.setThreadScheduler(mTopAppVrThreadTid, Process.SCHED_OTHER, 0); + setThreadScheduler(mTopAppVrThreadTid, SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } } else if (mPersistentVrThreadTid > 0) { // Ensure that when leaving persistent VR mode we reschedule the high priority // persistent thread. - Process.setThreadScheduler(mPersistentVrThreadTid, Process.SCHED_OTHER, 0); + setThreadScheduler(mPersistentVrThreadTid, SCHED_OTHER, 0); mPersistentVrThreadTid = 0; } } @@ -773,42 +809,15 @@ public class ActivityManagerService extends IActivityManager.Stub && !mKeyguardController.isKeyguardShowing(); } - private static final class PriorityState { - // Acts as counter for number of synchronized region that needs to acquire 'this' as a lock - // the current thread is currently in. When it drops down to zero, we will no longer boost - // the thread's priority. - private int regionCounter = 0; - - // The thread's previous priority before boosting. - private int prevPriority = Integer.MIN_VALUE; - } - - static ThreadLocal<PriorityState> sThreadPriorityState = new ThreadLocal<PriorityState>() { - @Override protected PriorityState initialValue() { - return new PriorityState(); - } - }; + private static ThreadPriorityBooster sThreadPriorityBooster = new ThreadPriorityBooster( + THREAD_PRIORITY_FOREGROUND, LockGuard.INDEX_ACTIVITY); static void boostPriorityForLockedSection() { - int tid = Process.myTid(); - int prevPriority = Process.getThreadPriority(tid); - PriorityState state = sThreadPriorityState.get(); - if (state.regionCounter == 0 && prevPriority > -2) { - state.prevPriority = prevPriority; - Process.setThreadPriority(tid, -2); - } - state.regionCounter++; - if (LockGuard.ENABLED) { - LockGuard.guard(LockGuard.INDEX_ACTIVITY); - } + sThreadPriorityBooster.boost(); } static void resetPriorityAfterLockedSection() { - PriorityState state = sThreadPriorityState.get(); - state.regionCounter--; - if (state.regionCounter == 0 && state.prevPriority > -2) { - Process.setThreadPriority(Process.myTid(), state.prevPriority); - } + sThreadPriorityBooster.reset(); } public class PendingAssistExtras extends Binder implements Runnable { @@ -889,7 +898,7 @@ public class ActivityManagerService extends IActivityManager.Stub * Non-persistent app uid whitelist for background restrictions */ int[] mBackgroundUidWhitelist = new int[] { - Process.BLUETOOTH_UID + BLUETOOTH_UID }; /** @@ -1350,7 +1359,13 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") boolean mLaunchWarningShown = false; @GuardedBy("this") boolean mCheckedForSetup = false; - Context mContext; + final Context mContext; + + /** + * This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can + * change at runtime. Use mContext for non-UI purposes. + */ + final Context mUiContext; /** * The time at which we will allow normal application switches again, @@ -1852,7 +1867,7 @@ public class ActivityManagerService extends IActivityManager.Stub } break; case SHOW_FACTORY_ERROR_UI_MSG: { Dialog d = new FactoryErrorDialog( - mContext, msg.getData().getCharSequence("msg")); + mUiContext, msg.getData().getCharSequence("msg")); d.show(); ensureBootCompleted(); } break; @@ -1863,7 +1878,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (!app.waitedForDebugger) { Dialog d = new AppWaitingForDebuggerDialog( ActivityManagerService.this, - mContext, app); + mUiContext, app); app.waitDialog = d; app.waitedForDebugger = true; d.show(); @@ -1878,24 +1893,24 @@ public class ActivityManagerService extends IActivityManager.Stub } break; case SHOW_UID_ERROR_UI_MSG: { if (mShowDialogs) { - AlertDialog d = new BaseErrorDialog(mContext); + AlertDialog d = new BaseErrorDialog(mUiContext); d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); d.setCancelable(false); - d.setTitle(mContext.getText(R.string.android_system_label)); - d.setMessage(mContext.getText(R.string.system_error_wipe_data)); - d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok), + d.setTitle(mUiContext.getText(R.string.android_system_label)); + d.setMessage(mUiContext.getText(R.string.system_error_wipe_data)); + d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.ok), obtainMessage(DISMISS_DIALOG_UI_MSG, d)); d.show(); } } break; case SHOW_FINGERPRINT_ERROR_UI_MSG: { if (mShowDialogs) { - AlertDialog d = new BaseErrorDialog(mContext); + AlertDialog d = new BaseErrorDialog(mUiContext); d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); d.setCancelable(false); - d.setTitle(mContext.getText(R.string.android_system_label)); - d.setMessage(mContext.getText(R.string.system_error_manufacturer)); - d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok), + d.setTitle(mUiContext.getText(R.string.android_system_label)); + d.setMessage(mUiContext.getText(R.string.system_error_manufacturer)); + d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.ok), obtainMessage(DISMISS_DIALOG_UI_MSG, d)); d.show(); } @@ -1919,7 +1934,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (mode == ActivityManager.COMPAT_MODE_DISABLED || mode == ActivityManager.COMPAT_MODE_ENABLED) { mCompatModeDialog = new CompatModeDialog( - ActivityManagerService.this, mContext, + ActivityManagerService.this, mUiContext, ar.info.applicationInfo); mCompatModeDialog.show(); } @@ -1939,7 +1954,7 @@ public class ActivityManagerService extends IActivityManager.Stub ar.packageName)) { // TODO(multi-display): Show dialog on appropriate display. mUnsupportedDisplaySizeDialog = new UnsupportedDisplaySizeDialog( - ActivityManagerService.this, mContext, ar.info.applicationInfo); + ActivityManagerService.this, mUiContext, ar.info.applicationInfo); mUnsupportedDisplaySizeDialog.show(); } } @@ -2461,12 +2476,12 @@ public class ActivityManagerService extends IActivityManager.Stub if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { try { if (mVrState == VR_MODE) { - Process.setThreadScheduler(proc.vrThreadTid, - Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + setThreadScheduler(proc.vrThreadTid, + SCHED_FIFO | SCHED_RESET_ON_FORK, 1); mTopAppVrThreadTid = proc.vrThreadTid; } else { - Process.setThreadScheduler(proc.vrThreadTid, - Process.SCHED_OTHER, 0); + setThreadScheduler(proc.vrThreadTid, + SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } } catch (IllegalArgumentException e) { @@ -2523,7 +2538,7 @@ public class ActivityManagerService extends IActivityManager.Stub final List<ProcessCpuTracker.Stats> stats; synchronized (mProcessCpuTracker) { stats = mProcessCpuTracker.getStats( (st)-> { - return st.vsize > 0 && st.uid < Process.FIRST_APPLICATION_UID; + return st.vsize > 0 && st.uid < FIRST_APPLICATION_UID; }); } final int N = stats.size(); @@ -2731,6 +2746,7 @@ public class ActivityManagerService extends IActivityManager.Stub public ActivityManagerService(Injector injector) { mInjector = injector; mContext = mInjector.getContext(); + mUiContext = null; GL_ES_VERSION = 0; mActivityStarter = null; mAppErrors = null; @@ -2762,8 +2778,10 @@ public class ActivityManagerService extends IActivityManager.Stub LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY); mInjector = new Injector(); mContext = systemContext; + mFactoryTest = FactoryTest.getMode(); mSystemThread = ActivityThread.currentActivityThread(); + mUiContext = mSystemThread.getSystemUiContext(); Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass()); @@ -2771,7 +2789,7 @@ public class ActivityManagerService extends IActivityManager.Stub com.android.internal.R.bool.config_permissionReviewRequired); mHandlerThread = new ServiceThread(TAG, - android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/); + THREAD_PRIORITY_FOREGROUND, false /*allowIo*/); mHandlerThread.start(); mHandler = new MainHandler(mHandlerThread.getLooper()); mUiHandler = mInjector.getUiHandler(this); @@ -2792,7 +2810,7 @@ public class ActivityManagerService extends IActivityManager.Stub /* static; one-time init here */ if (sKillHandler == null) { sKillThread = new ServiceThread(TAG + ":kill", - android.os.Process.THREAD_PRIORITY_BACKGROUND, true /* allowIo */); + THREAD_PRIORITY_BACKGROUND, true /* allowIo */); sKillThread.start(); sKillHandler = new KillHandler(sKillThread.getLooper()); } @@ -2806,7 +2824,7 @@ public class ActivityManagerService extends IActivityManager.Stub mServices = new ActiveServices(this); mProviderMap = new ProviderMap(this); - mAppErrors = new AppErrors(mContext, this); + mAppErrors = new AppErrors(mUiContext, this); // TODO: Move creation of battery stats service outside of activity manager service. File dataDir = Environment.getDataDirectory(); @@ -2849,7 +2867,7 @@ public class ActivityManagerService extends IActivityManager.Stub mTempConfig.setToDefaults(); mTempConfig.setLocales(LocaleList.getDefault()); mConfigurationSeq = mTempConfig.seq = 1; - mStackSupervisor = new ActivityStackSupervisor(this); + mStackSupervisor = createStackSupervisor(); mStackSupervisor.onConfigurationChanged(mTempConfig); mKeyguardController = mStackSupervisor.mKeyguardController; mCompatModePackages = new CompatModePackages(this, systemDir, mHandler); @@ -2897,6 +2915,10 @@ public class ActivityManagerService extends IActivityManager.Stub Watchdog.getInstance().addThread(mHandler); } + protected ActivityStackSupervisor createStackSupervisor() { + return new ActivityStackSupervisor(this, mHandler.getLooper()); + } + public void setSystemServiceManager(SystemServiceManager mgr) { mSystemServiceManager = mgr; } @@ -2906,7 +2928,7 @@ public class ActivityManagerService extends IActivityManager.Stub } private void start() { - Process.removeAllProcessGroups(); + removeAllProcessGroups(); mProcessCpuThread.start(); mBatteryStatsService.publish(mContext); @@ -3114,7 +3136,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, - -1, Process.SYSTEM_UID, UserHandle.USER_ALL); + -1, SYSTEM_UID, UserHandle.USER_ALL); } } @@ -3151,7 +3173,8 @@ public class ActivityManagerService extends IActivityManager.Stub * {@link ActivityStack#setResumedActivityLocked} when an activity is resumed. */ void setResumedActivityUncheckLocked(ActivityRecord r, String reason) { - if (r.task.isApplicationTask()) { + final TaskRecord task = r.getTask(); + if (task.isApplicationTask()) { if (mCurAppTimeTracker != r.appTimeTracker) { // We are switching app tracking. Complete the current one. if (mCurAppTimeTracker != null) { @@ -3174,17 +3197,18 @@ public class ActivityManagerService extends IActivityManager.Stub // TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null // TODO: Probably not, because we don't want to resume voice on switching // back to this activity - if (r.task.voiceInteractor != null) { - startRunningVoiceLocked(r.task.voiceSession, r.info.applicationInfo.uid); + if (task.voiceInteractor != null) { + startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid); } else { finishRunningVoiceLocked(); if (mLastResumedActivity != null) { final IVoiceInteractionSession session; - if (mLastResumedActivity.task != null - && mLastResumedActivity.task.voiceSession != null) { - session = mLastResumedActivity.task.voiceSession; + final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask(); + if (lastResumedActivityTask != null + && lastResumedActivityTask.voiceSession != null) { + session = lastResumedActivityTask.voiceSession; } else { session = mLastResumedActivity.voiceSession; } @@ -3317,7 +3341,7 @@ public class ActivityManagerService extends IActivityManager.Stub final void showAskCompatModeDialogLocked(ActivityRecord r) { Message msg = Message.obtain(); msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG; - msg.obj = r.task.askedCompatMode ? null : r; + msg.obj = r.getTask().askedCompatMode ? null : r; mUiHandler.sendMessage(msg); } @@ -3384,7 +3408,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (lrui >= 0) { if (!app.killed) { Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app); - Process.killProcessQuiet(app.pid); + killProcessQuiet(app.pid); killProcessGroup(app.uid, app.pid); } if (lrui <= mLruProcessActivityStart) { @@ -3593,7 +3617,7 @@ public class ActivityManagerService extends IActivityManager.Stub } final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) { - if (uid == Process.SYSTEM_UID) { + if (uid == SYSTEM_UID) { // The system gets to run in any process. If there are multiple // processes with the same uid, just pick the first (this // should never happen). @@ -3659,7 +3683,7 @@ public class ActivityManagerService extends IActivityManager.Stub // closest thing to a parent's uid is SYSTEM_UID. // The only important thing here is to keep AI.uid != PR.uid, in order to trigger // the |isolated| logic in the ProcessRecord constructor. - info.uid = Process.SYSTEM_UID; + info.uid = SYSTEM_UID; info.processName = processName; info.className = entryPoint; info.packageName = "android"; @@ -3954,9 +3978,9 @@ public class ActivityManagerService extends IActivityManager.Stub Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName); checkTime(startTime, "startProcess: asking zygote to start proc"); - Process.ProcessStartResult startResult; + ProcessStartResult startResult; if (hostingType.equals("webview_service")) { - startResult = Process.startWebView(entryPoint, + startResult = startWebView(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, null, entryPointArgs); @@ -4193,7 +4217,7 @@ public class ActivityManagerService extends IActivityManager.Stub } void enforceShellRestriction(String restriction, int userHandle) { - if (Binder.getCallingUid() == Process.SHELL_UID) { + if (Binder.getCallingUid() == SHELL_UID) { if (userHandle < 0 || mUserController.hasUserRestriction(restriction, userHandle)) { throw new SecurityException("Shell does not have permission to access user " + userHandle); @@ -4559,7 +4583,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (sourceRecord.app == null) { throw new SecurityException("Called without a process attached to activity"); } - if (UserHandle.getAppId(sourceRecord.app.uid) != Process.SYSTEM_UID) { + if (UserHandle.getAppId(sourceRecord.app.uid) != SYSTEM_UID) { // This is still okay, as long as this activity is running under the // uid of the original calling activity. if (sourceRecord.app.uid != sourceRecord.launchedFromUid) { @@ -4723,7 +4747,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (ActivityRecord.forTokenLocked(callingActivity) != activity) { throw new SecurityException("Only focused activity can call startVoiceInteraction"); } - if (mRunningVoice != null || activity.task.voiceSession != null + if (mRunningVoice != null || activity.getTask().voiceSession != null || activity.voiceSession != null) { Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction"); return; @@ -5033,7 +5057,7 @@ public class ActivityManagerService extends IActivityManager.Stub return true; } // Keep track of the root activity of the task before we finish it - TaskRecord tr = r.task; + TaskRecord tr = r.getTask(); ActivityRecord rootR = tr.getRootActivity(); if (rootR == null) { Slog.w(TAG, "Finishing task with all activities already finished"); @@ -5170,7 +5194,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps // can finish. - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV && mStackSupervisor.isLastLockedTask(task) && task.getRootActivity() == r) { mStackSupervisor.showLockTaskToast(); @@ -5411,7 +5435,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (!app.killed) { if (!fromBinderDied) { - Process.killProcessQuiet(pid); + killProcessQuiet(pid); } killProcessGroup(app.uid, pid); app.killed = true; @@ -5506,7 +5530,7 @@ public class ActivityManagerService extends IActivityManager.Stub } public void dumpWithTimeout(int pid) { - Process.sendSignal(pid, Process.SIGNAL_QUIT); + sendSignal(pid, SIGNAL_QUIT); synchronized (this) { try { wait(TRACE_DUMP_TIMEOUT_MS); // Wait for traces file to be closed. @@ -5761,7 +5785,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); intent.putExtra(Intent.EXTRA_UID, pkgUidF); intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(pkgUidF)); - broadcastIntentInPackage("android", Process.SYSTEM_UID, intent, + broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0, null, null, null, null, false, false, userIdF); if (observer != null) { @@ -5979,7 +6003,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void addPackageDependency(String packageName) { synchronized (this) { int callingPid = Binder.getCallingPid(); - if (callingPid == Process.myPid()) { + if (callingPid == myPid()) { // Yeah, um, no. return; } @@ -6011,7 +6035,7 @@ public class ActivityManagerService extends IActivityManager.Stub } int callerUid = Binder.getCallingUid(); // Only the system server can kill an application - if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) { + if (UserHandle.getAppId(callerUid) == SYSTEM_UID) { // Post an aysnc message to kill the application Message msg = mHandler.obtainMessage(KILL_APPLICATION_MSG); msg.arg1 = appId; @@ -6038,7 +6062,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { // Only allow this from foreground processes, so that background // applications can't abuse it to prevent system UI from being shown. - if (uid >= Process.FIRST_APPLICATION_UID) { + if (uid >= FIRST_APPLICATION_UID) { ProcessRecord proc; synchronized (mPidsSelfLocked) { proc = mPidsSelfLocked.get(pid); @@ -6069,7 +6093,7 @@ public class ActivityManagerService extends IActivityManager.Stub broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, - -1, Process.SYSTEM_UID, UserHandle.USER_ALL); + -1, SYSTEM_UID, UserHandle.USER_ALL); } @Override @@ -6135,7 +6159,7 @@ public class ActivityManagerService extends IActivityManager.Stub int callerUid = Binder.getCallingUid(); // Only the system server can kill an application - if (callerUid == Process.SYSTEM_UID) { + if (callerUid == SYSTEM_UID) { synchronized (this) { ProcessRecord app = getProcessRecordLocked(processName, uid, true); if (app != null && app.thread != null) { @@ -6171,7 +6195,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid)); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid)); + null, false, false, MY_PID, SYSTEM_UID, UserHandle.getUserId(uid)); } @@ -6688,7 +6712,7 @@ public class ActivityManagerService extends IActivityManager.Stub + " (IApplicationThread " + thread + "); dropping process"); EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid); if (pid > 0 && pid != MY_PID) { - Process.killProcessQuiet(pid); + killProcessQuiet(pid); //TODO: killProcessGroup(app.info.uid, pid); } else { try { @@ -6796,7 +6820,7 @@ public class ActivityManagerService extends IActivityManager.Stub // If the app is being launched for restore or full backup, set it up specially boolean isRestrictedBackupMode = false; if (mBackupTarget != null && mBackupAppName.equals(processName)) { - isRestrictedBackupMode = mBackupTarget.appInfo.uid >= Process.FIRST_APPLICATION_UID + isRestrictedBackupMode = mBackupTarget.appInfo.uid >= FIRST_APPLICATION_UID && ((mBackupTarget.backupMode == BackupRecord.RESTORE) || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL) || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL)); @@ -7019,7 +7043,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void showBootMessage(final CharSequence msg, final boolean always) { - if (Binder.getCallingUid() != Process.myUid()) { + if (Binder.getCallingUid() != myUid()) { throw new SecurityException(); } mWindowManager.showBootMessage(msg, always); @@ -7056,7 +7080,7 @@ public class ActivityManagerService extends IActivityManager.Stub ArraySet<String> completedIsas = new ArraySet<String>(); for (String abi : Build.SUPPORTED_ABIS) { - Process.zygoteProcess.establishZygoteConnectionForAbi(abi); + zygoteProcess.establishZygoteConnectionForAbi(abi); final String instructionSet = VMRuntime.getInstructionSet(abi); if (!completedIsas.contains(instructionSet)) { try { @@ -7397,7 +7421,7 @@ public class ActivityManagerService extends IActivityManager.Stub userId = UserHandle.USER_CURRENT; } try { - if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { + if (callingUid != 0 && callingUid != SYSTEM_UID) { final int uid = AppGlobals.getPackageManager().getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid)); if (!UserHandle.isSameApp(callingUid, uid)) { @@ -7834,7 +7858,7 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } // An activity is consider to be in multi-window mode if its task isn't fullscreen. - return !r.task.mFullscreen; + return !r.getTask().mFullscreen; } } finally { Binder.restoreCallingIdentity(origId); @@ -8664,7 +8688,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Third... does the caller itself have permission to access // this uri? final int callingAppId = UserHandle.getAppId(callingUid); - if ((callingAppId == Process.SYSTEM_UID) || (callingAppId == Process.ROOT_UID)) { + if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) { if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) { // Exempted authority for cropping user photos in Settings app } else { @@ -9150,7 +9174,7 @@ public class ActivityManagerService extends IActivityManager.Stub throw new IllegalArgumentException("Unknown owner: " + token); } if (fromUid != Binder.getCallingUid()) { - if (Binder.getCallingUid() != Process.myUid()) { + if (Binder.getCallingUid() != myUid()) { // Only system code can grant URI permissions on behalf // of other users. throw new SecurityException("nice try"); @@ -9534,8 +9558,8 @@ public class ActivityManagerService extends IActivityManager.Stub public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) { final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ); final long cachedAppMem = mProcessList.getMemLevel(ProcessList.CACHED_APP_MIN_ADJ); - outInfo.availMem = Process.getFreeMemory(); - outInfo.totalMem = Process.getTotalMemory(); + outInfo.availMem = getFreeMemory(); + outInfo.totalMem = getTotalMemory(); outInfo.threshold = homeAppMem; outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2)); outInfo.hiddenAppThreshold = cachedAppMem; @@ -9927,8 +9951,9 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { r.setTaskDescription(td); - r.task.updateTaskDescription(); - mTaskChangeNotificationController.notifyTaskDescriptionChanged(r.task.taskId, td); + final TaskRecord task = r.getTask(); + task.updateTaskDescription(); + mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td); } } } @@ -10378,8 +10403,8 @@ public class ActivityManagerService extends IActivityManager.Stub } if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r); - r.task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, - ANIMATE, !DEFER_RESUME, "exitFreeformMode"); + r.getTask().reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, + REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "exitFreeformMode"); } finally { Binder.restoreCallingIdentity(ident); } @@ -10677,7 +10702,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void updateDeviceOwner(String packageName) { final int callingUid = Binder.getCallingUid(); - if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { + if (callingUid != 0 && callingUid != SYSTEM_UID) { throw new SecurityException("updateDeviceOwner called from non-system process"); } synchronized (this) { @@ -10688,7 +10713,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void updateLockTaskPackages(int userId, String[] packages) { final int callingUid = Binder.getCallingUid(); - if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { + if (callingUid != 0 && callingUid != SYSTEM_UID) { enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES, "updateLockTaskPackages()"); } @@ -10711,7 +10736,7 @@ public class ActivityManagerService extends IActivityManager.Stub // is initiated by system after the pinning request was shown and locked mode is initiated // by an authorized app directly final int callingUid = Binder.getCallingUid(); - boolean isSystemInitiated = callingUid == Process.SYSTEM_UID; + boolean isSystemInitiated = callingUid == SYSTEM_UID; long ident = Binder.clearCallingIdentity(); try { if (!isSystemInitiated) { @@ -10760,7 +10785,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { return; } - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (task != null) { startLockTaskModeLocked(task); } @@ -10859,7 +10884,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { return; } - mStackSupervisor.showLockTaskEscapeMessageLocked(r.task); + mStackSupervisor.showLockTaskEscapeMessageLocked(r.getTask()); } } @@ -11190,7 +11215,7 @@ public class ActivityManagerService extends IActivityManager.Stub proc.procStatFile = "/proc/" + proc.pid + "/stat"; } mProcessStateStatsLongs[0] = 0; - if (!Process.readProcFile(proc.procStatFile, PROCESS_STATE_STATS_FORMAT, null, + if (!readProcFile(proc.procStatFile, PROCESS_STATE_STATS_FORMAT, null, mProcessStateStatsLongs, null)) { if (DEBUG_OOM_ADJ) Slog.d(TAG, "UNABLE TO RETRIEVE STATE FOR " + proc.procStatFile); return false; @@ -11892,7 +11917,7 @@ public class ActivityManagerService extends IActivityManager.Stub public final void installSystemProviders() { List<ProviderInfo> providers; synchronized (this) { - ProcessRecord app = mProcessNames.get("system", Process.SYSTEM_UID); + ProcessRecord app = mProcessNames.get("system", SYSTEM_UID); providers = generateApplicationProvidersLocked(app); if (providers != null) { for (int i=providers.size()-1; i>=0; i--) { @@ -12065,11 +12090,11 @@ public class ActivityManagerService extends IActivityManager.Stub int uid = info.uid; if (isolated) { if (isolatedUid == 0) { - int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1; + int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1; while (true) { - if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID - || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) { - mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID; + if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID + || mNextIsolatedProcessUid > LAST_ISOLATED_UID) { + mNextIsolatedProcessUid = FIRST_ISOLATED_UID; } uid = UserHandle.getUid(userId, mNextIsolatedProcessUid); mNextIsolatedProcessUid++; @@ -13121,9 +13146,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { return false; } - int index = r.task.mActivities.lastIndexOf(r); + final TaskRecord task = r.getTask(); + int index = task.mActivities.lastIndexOf(r); if (index > 0) { - ActivityRecord under = r.task.mActivities.get(index - 1); + ActivityRecord under = task.mActivities.get(index - 1); under.returningOptions = ActivityOptions.fromBundle(options); } final boolean translucentChanged = r.changeWindowTranslucency(false); @@ -13257,7 +13283,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { // Disable any existing VR thread. if (mTopAppVrThreadTid > 0) { - Process.setThreadScheduler(mTopAppVrThreadTid, Process.SCHED_OTHER, 0); + setThreadScheduler(mTopAppVrThreadTid, SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } @@ -13281,15 +13307,15 @@ public class ActivityManagerService extends IActivityManager.Stub */ private int updateVrThreadLocked(ProcessRecord proc, int lastTid, int pid, int tid) { // ensure the tid belongs to the process - if (!Process.isThreadInProcess(pid, tid)) { + if (!isThreadInProcess(pid, tid)) { throw new IllegalArgumentException("VR thread does not belong to process"); } // reset existing VR thread to CFS if this thread still exists and belongs to // the calling process - if (lastTid != 0 && Process.isThreadInProcess(pid, lastTid)) { + if (lastTid != 0 && isThreadInProcess(pid, lastTid)) { try { - Process.setThreadScheduler(lastTid, Process.SCHED_OTHER, 0); + setThreadScheduler(lastTid, SCHED_OTHER, 0); } catch (IllegalArgumentException e) { // Ignore this. Only occurs in race condition where previous VR thread // was destroyed during this method call. @@ -13300,8 +13326,8 @@ public class ActivityManagerService extends IActivityManager.Stub try { if ((proc == null || proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) && tid > 0) { - Process.setThreadScheduler(tid, - Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + setThreadScheduler(tid, + SCHED_FIFO | SCHED_RESET_ON_FORK, 1); } return tid; } catch (IllegalArgumentException e) { @@ -13320,7 +13346,7 @@ public class ActivityManagerService extends IActivityManager.Stub proc = mPidsSelfLocked.get(pid); if (proc != null && proc.renderThreadTid == 0 && tid > 0) { // ensure the tid belongs to the process - if (!Process.isThreadInProcess(pid, tid)) { + if (!isThreadInProcess(pid, tid)) { throw new IllegalArgumentException( "Render thread does not belong to process"); } @@ -13332,10 +13358,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band"); if (mUseFifoUiScheduling) { - Process.setThreadScheduler(proc.renderThreadTid, - Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + setThreadScheduler(proc.renderThreadTid, + SCHED_FIFO | SCHED_RESET_ON_FORK, 1); } else { - Process.setThreadPriority(proc.renderThreadTid, -10); + setThreadPriority(proc.renderThreadTid, -10); } } } else { @@ -13410,7 +13436,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { throw new IllegalArgumentException(); } - return r.task.getTopActivity() == r; + return r.getTask().getTopActivity() == r; } } @@ -13496,7 +13522,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (sender == null) { uid = sourceUid; } else { - uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid; + uid = rec.uid == MY_UID ? SYSTEM_UID : rec.uid; } BatteryStatsImpl.Uid.Pkg pkg = stats.getPackageStatsLocked(sourceUid >= 0 ? sourceUid : uid, @@ -13519,7 +13545,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (sender == null) { uid = sourceUid; } else { - uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid; + uid = rec.uid == MY_UID ? SYSTEM_UID : rec.uid; } mBatteryStatsService.noteAlarmStart(tag, sourceUid >= 0 ? sourceUid : uid); } @@ -13538,14 +13564,14 @@ public class ActivityManagerService extends IActivityManager.Stub if (sender == null) { uid = sourceUid; } else { - uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid; + uid = rec.uid == MY_UID ? SYSTEM_UID : rec.uid; } mBatteryStatsService.noteAlarmFinish(tag, sourceUid >= 0 ? sourceUid : uid); } } public boolean killPids(int[] pids, String pReason, boolean secure) { - if (Binder.getCallingUid() != Process.SYSTEM_UID) { + if (Binder.getCallingUid() != SYSTEM_UID) { throw new SecurityException("killPids only available to the system"); } String reason = (pReason == null) ? "Unknown" : pReason; @@ -13611,7 +13637,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public boolean killProcessesBelowForeground(String reason) { - if (Binder.getCallingUid() != Process.SYSTEM_UID) { + if (Binder.getCallingUid() != SYSTEM_UID) { throw new SecurityException("killProcessesBelowForeground() only available to system"); } @@ -13619,7 +13645,7 @@ public class ActivityManagerService extends IActivityManager.Stub } private boolean killProcessesBelowAdj(int belowAdj, String reason) { - if (Binder.getCallingUid() != Process.SYSTEM_UID) { + if (Binder.getCallingUid() != SYSTEM_UID) { throw new SecurityException("killProcessesBelowAdj() only available to system"); } @@ -13696,7 +13722,7 @@ public class ActivityManagerService extends IActivityManager.Stub Log.i(TAG, "Shutting down activity manager..."); shutdown(10000); Log.i(TAG, "Shutdown complete, restarting!"); - Process.killProcess(Process.myPid()); + killProcess(myPid()); System.exit(10); } }; @@ -14040,7 +14066,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, Process.SYSTEM_UID, + null, false, false, MY_PID, SYSTEM_UID, currentUserId); intent = new Intent(Intent.ACTION_USER_STARTING); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -14054,7 +14080,7 @@ public class ActivityManagerService extends IActivityManager.Stub } }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, - null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); + null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); } catch (Throwable t) { Slog.wtf(TAG, "Failed sending first user broadcasts", t); } finally { @@ -15928,8 +15954,9 @@ public class ActivityManagerService extends IActivityManager.Stub } needSep = true; synchronized (this) { - if (lastTask != r.task) { - lastTask = r.task; + final TaskRecord task = r.getTask(); + if (lastTask != task) { + lastTask = task; pw.print("TASK "); pw.print(lastTask.affinity); pw.print(" id="); pw.print(lastTask.taskId); pw.print(" userId="); pw.println(lastTask.userId); @@ -16698,22 +16725,22 @@ public class ActivityManagerService extends IActivityManager.Stub private final long[] getKsmInfo() { long[] longOut = new long[4]; final int[] SINGLE_LONG_FORMAT = new int[] { - Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG + PROC_SPACE_TERM| PROC_OUT_LONG }; long[] longTmp = new long[1]; - Process.readProcFile("/sys/kernel/mm/ksm/pages_shared", + readProcFile("/sys/kernel/mm/ksm/pages_shared", SINGLE_LONG_FORMAT, null, longTmp, null); longOut[KSM_SHARED] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; longTmp[0] = 0; - Process.readProcFile("/sys/kernel/mm/ksm/pages_sharing", + readProcFile("/sys/kernel/mm/ksm/pages_sharing", SINGLE_LONG_FORMAT, null, longTmp, null); longOut[KSM_SHARING] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; longTmp[0] = 0; - Process.readProcFile("/sys/kernel/mm/ksm/pages_unshared", + readProcFile("/sys/kernel/mm/ksm/pages_unshared", SINGLE_LONG_FORMAT, null, longTmp, null); longOut[KSM_UNSHARED] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; longTmp[0] = 0; - Process.readProcFile("/sys/kernel/mm/ksm/pages_volatile", + readProcFile("/sys/kernel/mm/ksm/pages_volatile", SINGLE_LONG_FORMAT, null, longTmp, null); longOut[KSM_VOLATILE] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; return longOut; @@ -17989,7 +18016,7 @@ public class ActivityManagerService extends IActivityManager.Stub String className, int flags) { boolean result = false; // For apps that don't have pre-defined UIDs, check for permission - if (UserHandle.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) { + if (UserHandle.getAppId(aInfo.uid) >= FIRST_APPLICATION_UID) { if ((flags & ServiceInfo.FLAG_SINGLE_USER) != 0) { if (ActivityManager.checkUidPermission( INTERACT_ACROSS_USERS, @@ -18008,7 +18035,7 @@ public class ActivityManagerService extends IActivityManager.Stub result = true; } else if ((flags & ServiceInfo.FLAG_SINGLE_USER) != 0) { // Phone app and persistent apps are allowed to export singleuser providers. - result = UserHandle.isSameApp(aInfo.uid, Process.PHONE_UID) + result = UserHandle.isSameApp(aInfo.uid, PHONE_UID) || (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0; } if (DEBUG_MU) Slog.v(TAG_MU, @@ -18026,8 +18053,8 @@ public class ActivityManagerService extends IActivityManager.Stub boolean isValidSingletonCall(int callingUid, int componentUid) { int componentAppId = UserHandle.getAppId(componentUid); return UserHandle.isSameApp(callingUid, componentUid) - || componentAppId == Process.SYSTEM_UID - || componentAppId == Process.PHONE_UID + || componentAppId == SYSTEM_UID + || componentAppId == PHONE_UID || ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, componentUid) == PackageManager.PERMISSION_GRANTED; } @@ -18268,7 +18295,7 @@ public class ActivityManagerService extends IActivityManager.Stub // ========================================================= private boolean isInstantApp(ProcessRecord record, String callerPackage, int uid) { - if (UserHandle.getAppId(uid) < Process.FIRST_APPLICATION_UID) { + if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) { return false; } // Easy case -- we have the app's ProcessRecord. @@ -18329,7 +18356,7 @@ public class ActivityManagerService extends IActivityManager.Stub + " (pid=" + Binder.getCallingPid() + ") when registering receiver " + receiver); } - if (callerApp.info.uid != Process.SYSTEM_UID && + if (callerApp.info.uid != SYSTEM_UID && !callerApp.pkgList.containsKey(callerPackage) && !"android".equals(callerPackage)) { throw new SecurityException("Given caller package " + callerPackage @@ -18546,7 +18573,7 @@ public class ActivityManagerService extends IActivityManager.Stub for (int user : users) { // Skip users that have Shell restrictions, with exception of always permitted // Shell broadcasts - if (callingUid == Process.SHELL_UID + if (callingUid == SHELL_UID && mUserController.hasUserRestriction( UserManager.DISALLOW_DEBUGGING_FEATURES, user) && !isPermittedShellBroadcast(intent)) { @@ -18730,7 +18757,7 @@ public class ActivityManagerService extends IActivityManager.Stub // and upgrade steps. if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) { - if ((callingUid != Process.SYSTEM_UID + if ((callingUid != SYSTEM_UID || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { Slog.w(TAG, "Skipping broadcast of " + intent @@ -18774,11 +18801,11 @@ public class ActivityManagerService extends IActivityManager.Stub final boolean isCallerSystem; switch (UserHandle.getAppId(callingUid)) { - case Process.ROOT_UID: - case Process.SYSTEM_UID: - case Process.PHONE_UID: - case Process.BLUETOOTH_UID: - case Process.NFC_UID: + case ROOT_UID: + case SYSTEM_UID: + case PHONE_UID: + case BLUETOOTH_UID: + case NFC_UID: isCallerSystem = true; break; default: @@ -19154,7 +19181,7 @@ public class ActivityManagerService extends IActivityManager.Stub receivers = collectReceiverComponents(intent, resolvedType, callingUid, users); } if (intent.getComponent() == null) { - if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) { + if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) { // Query one target user at a time, excluding shell-restricted users for (int i = 0; i < users.length; i++) { if (mUserController.hasUserRestriction( @@ -19396,8 +19423,8 @@ public class ActivityManagerService extends IActivityManager.Stub if ((flags & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) { switch (Binder.getCallingUid()) { - case Process.ROOT_UID: - case Process.SHELL_UID: + case ROOT_UID: + case SHELL_UID: break; default: Slog.w(TAG, "Removing FLAG_RECEIVER_FROM_SHELL because caller is UID " @@ -19852,7 +19879,7 @@ public class ActivityManagerService extends IActivityManager.Stub private void enforceWriteSettingsPermission(String func) { int uid = Binder.getCallingUid(); - if (uid == Process.ROOT_UID) { + if (uid == ROOT_UID) { return; } @@ -20050,7 +20077,7 @@ public class ActivityManagerService extends IActivityManager.Stub | Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID, + AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) { intent = new Intent(Intent.ACTION_LOCALE_CHANGED); @@ -20061,7 +20088,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); } broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID, + AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); } @@ -20561,8 +20588,9 @@ public class ActivityManagerService extends IActivityManager.Stub app.cached = false; app.empty = false; foregroundActivities = true; - if (r.task != null && minLayer > 0) { - final int layer = r.task.mLayerRank; + final TaskRecord task = r.getTask(); + if (task != null && minLayer > 0) { + final int layer = task.mLayerRank; if (layer >= 0 && minLayer > layer) { minLayer = layer; } @@ -21534,27 +21562,27 @@ public class ActivityManagerService extends IActivityManager.Stub int processGroup; switch (app.curSchedGroup) { case ProcessList.SCHED_GROUP_BACKGROUND: - processGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + processGroup = THREAD_GROUP_BG_NONINTERACTIVE; break; case ProcessList.SCHED_GROUP_TOP_APP: case ProcessList.SCHED_GROUP_TOP_APP_BOUND: - processGroup = Process.THREAD_GROUP_TOP_APP; + processGroup = THREAD_GROUP_TOP_APP; break; default: - processGroup = Process.THREAD_GROUP_DEFAULT; + processGroup = THREAD_GROUP_DEFAULT; break; } long oldId = Binder.clearCallingIdentity(); try { - Process.setProcessGroup(app.pid, processGroup); + setProcessGroup(app.pid, processGroup); if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { // do nothing if we already switched to RT if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { // Switch VR thread for app to SCHED_FIFO if (mVrState == VR_MODE && app.vrThreadTid != 0) { try { - Process.setThreadScheduler(app.vrThreadTid, - Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + setThreadScheduler(app.vrThreadTid, + SCHED_FIFO | SCHED_RESET_ON_FORK, 1); mTopAppVrThreadTid = app.vrThreadTid; } catch (IllegalArgumentException e) { // thread died, ignore @@ -21562,17 +21590,17 @@ public class ActivityManagerService extends IActivityManager.Stub } if (mUseFifoUiScheduling) { // Switch UI pipeline for app to SCHED_FIFO - app.savedPriority = Process.getThreadPriority(app.pid); + app.savedPriority = getThreadPriority(app.pid); try { - Process.setThreadScheduler(app.pid, - Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + setThreadScheduler(app.pid, + SCHED_FIFO | SCHED_RESET_ON_FORK, 1); } catch (IllegalArgumentException e) { // thread died, ignore } if (app.renderThreadTid != 0) { try { - Process.setThreadScheduler(app.renderThreadTid, - Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + setThreadScheduler(app.renderThreadTid, + SCHED_FIFO | SCHED_RESET_ON_FORK, 1); } catch (IllegalArgumentException e) { // thread died, ignore } @@ -21587,10 +21615,10 @@ public class ActivityManagerService extends IActivityManager.Stub } } else { // Boost priority for top app UI and render threads - Process.setThreadPriority(app.pid, -10); + setThreadPriority(app.pid, -10); if (app.renderThreadTid != 0) { try { - Process.setThreadPriority(app.renderThreadTid, -10); + setThreadPriority(app.renderThreadTid, -10); } catch (IllegalArgumentException e) { // thread died, ignore } @@ -21602,23 +21630,23 @@ public class ActivityManagerService extends IActivityManager.Stub // Reset VR thread to SCHED_OTHER // Safe to do even if we're not in VR mode if (app.vrThreadTid != 0) { - Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0); + setThreadScheduler(app.vrThreadTid, SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } if (mUseFifoUiScheduling) { // Reset UI pipeline to SCHED_OTHER - Process.setThreadScheduler(app.pid, Process.SCHED_OTHER, 0); - Process.setThreadPriority(app.pid, app.savedPriority); + setThreadScheduler(app.pid, SCHED_OTHER, 0); + setThreadPriority(app.pid, app.savedPriority); if (app.renderThreadTid != 0) { - Process.setThreadScheduler(app.renderThreadTid, - Process.SCHED_OTHER, 0); - Process.setThreadPriority(app.renderThreadTid, -4); + setThreadScheduler(app.renderThreadTid, + SCHED_OTHER, 0); + setThreadPriority(app.renderThreadTid, -4); } } else { // Reset priority for top app UI and render threads - Process.setThreadPriority(app.pid, 0); + setThreadPriority(app.pid, 0); if (app.renderThreadTid != 0) { - Process.setThreadPriority(app.renderThreadTid, 0); + setThreadPriority(app.renderThreadTid, 0); } } } @@ -22728,7 +22756,7 @@ public class ActivityManagerService extends IActivityManager.Stub /** This method sends the specified signal to each of the persistent apps */ public void signalPersistentProcesses(int sig) throws RemoteException { - if (sig != Process.SIGNAL_USR1) { + if (sig != SIGNAL_USR1) { throw new SecurityException("Only SIGNAL_USR1 is allowed"); } @@ -22742,7 +22770,7 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { ProcessRecord r = mLruProcesses.get(i); if (r.thread != null && r.persistent) { - Process.sendSignal(r.pid, sig); + sendSignal(r.pid, sig); } } } @@ -23883,7 +23911,7 @@ public class ActivityManagerService extends IActivityManager.Stub final boolean updateFrameworkRes = packagesToUpdate.contains("android"); for (int i = mLruProcesses.size() - 1; i >= 0; i--) { final ProcessRecord app = mLruProcesses.get(i); - if (app.thread == null || app.pid == Process.myPid()) { + if (app.thread == null) { continue; } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index cbb51e1566c4..3a2941467cb6 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -229,7 +229,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private int theme; // resource identifier of activity's theme. private int realTheme; // actual theme resource we will use, never 0. private int windowFlags; // custom window flags for preview window. - TaskRecord task; // the task this is in. + private TaskRecord task; // the task this is in. private long createTime = System.currentTimeMillis(); long displayStartTime; // when we started launching this activity long fullyDrawnStartTime; // when we started launching this activity @@ -686,9 +686,48 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo @Override protected ConfigurationContainer getParent() { + return getTask(); + } + + TaskRecord getTask() { return task; } + /** + * Sets reference to the {@link TaskRecord} the {@link ActivityRecord} will treat as its parent. + * Note that this does not actually add the {@link ActivityRecord} as a {@link TaskRecord} + * children. However, this method will clean up references to this {@link ActivityRecord} in + * {@link ActivityStack}. + * @param task The new parent {@link TaskRecord}. + */ + void setTask(TaskRecord task) { + setTask(task, false /*reparenting*/); + } + + /** + * This method should only be called by {@link TaskRecord#removeActivity(ActivityRecord)}. + */ + void setTask(TaskRecord task, boolean reparenting) { + // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}. + if (task != null && task == getTask()) { + return; + } + + final ActivityStack stack = getStack(); + + // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this + // {@link ActivityRecord} from its current {@link ActivityStack}. + if (!reparenting && stack != null && (task == null || stack != task.getStack())) { + stack.onActivityRemovedFromStack(this); + } + + this.task = task; + + if (!reparenting) { + onParentChanged(); + } + } + static class Token extends IApplicationToken.Stub { private final WeakReference<ActivityRecord> weakActivity; @@ -925,8 +964,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // Must reparent first in window manager mWindowContainerController.reparent(newTask.getWindowContainerController(), position); - // Remove the activity from the old task and add it to the new task - prevTask.removeActivity(this); + // Remove the activity from the old task and add it to the new task. + prevTask.removeActivity(this, true /*reparenting*/); newTask.addActivityAtIndex(position, this); } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index ee37463d0980..f13b11e65a88 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -116,6 +116,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; @@ -725,7 +726,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (r == null) { return null; } - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final ActivityStack stack = r.getStack(); if (stack != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) { if (stack != this) Slog.w(TAG, @@ -934,7 +935,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls=" + taskIntent.getComponent().flattenToShortString() - + "/aff=" + r.task.rootAffinity + " to new cls=" + + "/aff=" + r.getTask().rootAffinity + " to new cls=" + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity); // TODO Refactor to remove duplications. Check if logic can be simplified. if (taskIntent != null && taskIntent.getComponent() != null && @@ -1049,8 +1050,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai void addRecentActivityLocked(ActivityRecord r) { if (r != null) { - mRecentTasks.addLocked(r.task); - r.task.touchActiveTime(); + final TaskRecord task = r.getTask(); + mRecentTasks.addLocked(task); + task.touchActiveTime(); } } @@ -1226,11 +1228,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null; prev.state = ActivityState.PAUSING; - prev.task.touchActiveTime(); + prev.getTask().touchActiveTime(); clearLaunchTime(prev); final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(); if (mService.mHasRecents - && (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) { + && (next == null || next.noDisplay || next.getTask() != prev.getTask() + || uiSleeping)) { prev.mUpdateTaskThumbnailWhenHidden = true; } stopFullyDrawnTraceIfNeeded(); @@ -1457,7 +1460,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Find the first visible activity above the passed activity and if it is translucent return it // otherwise return null; ActivityRecord findNextTranslucentActivity(ActivityRecord r) { - TaskRecord task = r.task; + TaskRecord task = r.getTask(); if (task == null) { return null; } @@ -1604,7 +1607,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Otherwise, the docked stack is always visible, except in the case where the top // running activity task in the focus stack doesn't support any form of resizing but we // show it for the home task even though it's not resizable. - final TaskRecord task = r != null ? r.task : null; + final TaskRecord task = r != null ? r.getTask() : null; return task == null || task.supportsSplitScreen() || task.isHomeTask() ? STACK_VISIBLE : STACK_INVISIBLE; } @@ -2157,8 +2160,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mResumedActivity = r; r.state = ActivityState.RESUMED; mService.setResumedActivityUncheckLocked(r, reason); - r.task.touchActiveTime(); - mRecentTasks.addLocked(r.task); + final TaskRecord task = r.getTask(); + task.touchActiveTime(); + mRecentTasks.addLocked(task); } private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { @@ -2207,8 +2211,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return false; } - final TaskRecord nextTask = next.task; - final TaskRecord prevTask = prev != null ? prev.task : null; + final TaskRecord nextTask = next.getTask(); + final TaskRecord prevTask = prev != null ? prev.getTask() : null; if (prevTask != null && prevTask.getStack() == this && prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) { if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); @@ -2373,7 +2377,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai anim = false; mWindowManager.prepareAppTransition(TRANSIT_NONE, false); } else { - mWindowManager.prepareAppTransition(prev.task == next.task + mWindowManager.prepareAppTransition(prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE : TRANSIT_TASK_CLOSE, false); } @@ -2385,7 +2389,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai anim = false; mWindowManager.prepareAppTransition(TRANSIT_NONE, false); } else { - mWindowManager.prepareAppTransition(prev.task == next.task + mWindowManager.prepareAppTransition(prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND @@ -2522,7 +2526,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.notifyAppResumed(next.stopped, allowSavedSurface); EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId, - System.identityHashCode(next), next.task.taskId, next.shortComponentName); + System.identityHashCode(next), next.getTask().taskId, + next.shortComponentName); next.sleeping = false; mService.showUnsupportedZoomDialogIfNeededLocked(next); @@ -2696,9 +2701,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } - // If the task was launched from the assistant stack, set the return type to assistant final ActivityStack lastStack = mStackSupervisor.getLastStack(); - if (lastStack != null && lastStack.isAssistantStack()) { + + // If there is no last task, do not set task to return to + if (lastStack == null) { + return; + } + + // If the task was launched from the assistant stack, set the return type to assistant + if (lastStack.isAssistantStack()) { task.setTaskToReturnTo(ASSISTANT_ACTIVITY_TYPE); return; } @@ -2721,7 +2732,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity, boolean newTask, boolean keepCurTransition, ActivityOptions options) { - TaskRecord rTask = r.task; + TaskRecord rTask = r.getTask(); final int taskId = rTask.taskId; // mLaunchTaskBehind tasks get placed at the back of the task stack. if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) { @@ -2740,7 +2751,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // All activities in task are finishing. continue; } - if (task == r.task) { + if (task == rTask) { // Here it is! Now, if this is not yet visible to the // user, then just add it without starting; it will // get started when the user navigates back to it. @@ -2762,13 +2773,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // If we are not placing the new activity frontmost, we do not want to deliver the // onUserLeaving callback to the actual frontmost activity - if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) { + final TaskRecord activityTask = r.getTask(); + if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) { mStackSupervisor.mUserLeaving = false; if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING, "startActivity() behind front, mUserLeaving=false"); } - task = r.task; + task = activityTask; // Slot the activity into the history stack and proceed if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task, @@ -2795,7 +2807,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } else { // If a new task is being launched, then mark the existing top activity as // supporting picture-in-picture while pausing - if (focusedTopActivity != null) { + if (focusedTopActivity != null && + focusedTopActivity.getStack().getStackId() != PINNED_STACK_ID) { focusedTopActivity.supportsPictureInPictureWhilePausing = true; } transit = TRANSIT_TASK_OPEN; @@ -2829,11 +2842,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // "has the same starting icon" as the next one. This allows the // window manager to keep the previous window it had previously // created, if it still had one. - ActivityRecord prev = r.task.topRunningActivityWithStartingWindowLocked(); + TaskRecord prevTask = r.getTask(); + ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked(); if (prev != null) { // We don't want to reuse the previous starting preview if: // (1) The current activity is in a different task. - if (prev.task != r.task) { + if (prev.getTask() != prevTask) { prev = null; } // (2) The current activity is already displayed. @@ -2852,7 +2866,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai private boolean isTaskSwitch(ActivityRecord r, ActivityRecord topFocusedActivity) { - return topFocusedActivity != null && r.task != topFocusedActivity.task; + return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask(); } /** @@ -2919,20 +2933,20 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ? mTaskHistory.get(0).mActivities.get(0) : null; if (bottom != null && target.taskAffinity != null - && target.taskAffinity.equals(bottom.task.affinity)) { + && target.taskAffinity.equals(bottom.getTask().affinity)) { // If the activity currently at the bottom has the // same task affinity as the one we are moving, // then merge it into the same task. - targetTask = bottom.task; + targetTask = bottom.getTask(); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target - + " out to bottom task " + bottom.task); + + " out to bottom task " + targetTask); } else { targetTask = createTaskRecord( mStackSupervisor.getNextTaskIdForUserLocked(target.userId), target.info, null, null, null, false, target.mActivityType); targetTask.affinityIntent = target.intent; if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target - + " out to new task " + target.task); + + " out to new task " + targetTask); } boolean noOptions = canMoveOptions; @@ -2954,7 +2968,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai "Removing activity " + p + " from task=" + task + " adding to task=" + targetTask + " Callers=" + Debug.getCallers(4)); if (DEBUG_TASKS) Slog.v(TAG_TASKS, - "Pushing next activity " + p + " out to target's task " + target.task); + "Pushing next activity " + p + " out to target's task " + target); p.reparent(targetTask, 0 /* position - bottom */, "resetTargetTaskIfNeeded"); } @@ -3125,13 +3139,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean forceReset = (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; if (ACTIVITY_INACTIVE_RESET_TIME > 0 - && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { + && taskTop.getTask().getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { if ((newActivity.info.flags & ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) { forceReset = true; } } - final TaskRecord task = taskTop.task; + final TaskRecord task = taskTop.getTask(); /** False until we evaluate the TaskRecord associated with taskTop. Switches to true * for remaining tasks. Used for later tasks to reparent to task. */ @@ -3214,7 +3228,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // stack as long as there is a running activity. return; } else { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final boolean isAssistantOrOverAssistant = task.getStack().isAssistantStack() || task.isOverAssistantStack(); if (r.frontOfTask && task == topTask() && @@ -3373,10 +3387,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); - int taskNdx = mTaskHistory.indexOf(r.task); - int activityNdx = r.task.mActivities.indexOf(r); + finishedTask = r.getTask(); + int taskNdx = mTaskHistory.indexOf(finishedTask); + final TaskRecord task = finishedTask; + int activityNdx = task.mActivities.indexOf(r); finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); - finishedTask = r.task; + finishedTask = task; // Also terminate any activities below it that aren't yet // stopped, to avoid a situation where one will get // re-start our crashing activity once it gets resumed again. @@ -3446,7 +3462,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } final boolean finishActivityAffinityLocked(ActivityRecord r) { - ArrayList<ActivityRecord> activities = r.task.mActivities; + ArrayList<ActivityRecord> activities = r.getTask().mActivities; for (int index = activities.indexOf(r); index >= 0; --index) { ActivityRecord cur = activities.get(index); if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) { @@ -3511,7 +3527,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mWindowManager.deferSurfaceLayout(); try { r.makeFinishingLocked(); - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, r.userId, System.identityHashCode(r), task.taskId, r.shortComponentName, reason); @@ -3700,23 +3716,24 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) { // Basic case: for simple app-centric recents, we need to recreate // the task if the affinity has changed. - if (srec == null || srec.task.affinity == null || - !srec.task.affinity.equals(destAffinity)) { + if (srec == null || srec.getTask().affinity == null || + !srec.getTask().affinity.equals(destAffinity)) { return true; } // Document-centric case: an app may be split in to multiple documents; // they need to re-create their task if this current activity is the root // of a document, unless simply finishing it will return them to the the // correct app behind. - if (srec.frontOfTask && srec.task != null && srec.task.getBaseIntent() != null - && srec.task.getBaseIntent().isDocument()) { + final TaskRecord task = srec.getTask(); + if (srec.frontOfTask && task != null && task.getBaseIntent() != null + && task.getBaseIntent().isDocument()) { // Okay, this activity is at the root of its task. What to do, what to do... - if (srec.task.getTaskToReturnTo() != ActivityRecord.APPLICATION_ACTIVITY_TYPE) { + if (task.getTaskToReturnTo() != ActivityRecord.APPLICATION_ACTIVITY_TYPE) { // Finishing won't return to an application, so we need to recreate. return true; } // We now need to get the task below it to determine what to do. - int taskIdx = mTaskHistory.indexOf(srec.task); + int taskIdx = mTaskHistory.indexOf(task); if (taskIdx <= 0) { Slog.w(TAG, "shouldUpRecreateTask: task not in history for " + srec); return false; @@ -3726,7 +3743,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return true; } TaskRecord prevTask = mTaskHistory.get(taskIdx); - if (!srec.task.affinity.equals(prevTask.affinity)) { + if (!task.affinity.equals(prevTask.affinity)) { // These are different apps, so need to recreate. return true; } @@ -3736,7 +3753,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode, Intent resultData) { - final TaskRecord task = srec.task; + final TaskRecord task = srec.getTask(); final ArrayList<ActivityRecord> activities = task.mActivities; final int start = activities.indexOf(srec); if (!mTaskHistory.contains(task) || (start < 0)) { @@ -3815,6 +3832,22 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai Binder.restoreCallingIdentity(origId); return foundParentInTask; } + + /** + * Remove any state associated with the {@link ActivityRecord}. This should be called whenever + * an activity moves away from the stack. + */ + void onActivityRemovedFromStack(ActivityRecord r) { + if (mResumedActivity == r) { + mResumedActivity = null; + } + if (mPausingActivity == r) { + mPausingActivity = null; + } + + removeTimeoutsForActivityLocked(r); + } + /** * Perform the common clean-up of an activity record. This is called both * as part of destroyActivityLocked() (when destroying the client-side @@ -3825,12 +3858,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * Note: Call before #removeActivityFromHistoryLocked. */ private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) { - if (mResumedActivity == r) { - mResumedActivity = null; - } - if (mPausingActivity == r) { - mPausingActivity = null; - } + onActivityRemovedFromStack(r); r.deferRelaunchUntilPaused = false; r.frozenBeforeDestroy = false; @@ -3897,7 +3925,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r); r.app = null; r.removeWindowContainer(); - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final boolean lastActivity = task != null ? task.removeActivity(r) : false; // If we are removing the last activity in the task, not including task overlay activities, // then fall through into the block below to remove the entire task itself @@ -4043,7 +4071,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai + ", app=" + (r.app != null ? r.app.processName : "(null)")); EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, r.userId, System.identityHashCode(r), - r.task.taskId, r.shortComponentName, reason); + r.getTask().taskId, r.shortComponentName, reason); boolean removedFromHistory = false; @@ -4275,7 +4303,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai Slog.w(TAG, "Force removing " + r + ": app died, no saved state"); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, r.userId, System.identityHashCode(r), - r.task.taskId, r.shortComponentName, + r.getTask().taskId, r.shortComponentName, "proc died without state saved"); if (r.state == ActivityState.RESUMED) { mService.updateUsageStats(r, false); @@ -4403,7 +4431,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } // If a new task is moved to the front, then mark the existing top activity as supporting // picture-in-picture while paused - if (topActivity != null) { + if (topActivity != null && topActivity.getStack().getStackId() != PINNED_STACK_ID) { topActivity.supportsPictureInPictureWhilePausing = true; } @@ -4529,7 +4557,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } - final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null; + final TaskRecord task = mResumedActivity != null ? mResumedActivity.getTask() : null; if (prevIsHome || (task == tr && canGoHome) || (numTasks <= 1 && isOnHomeDisplay())) { if (!mService.mBooting && !mService.mBooted) { // Not ready yet! @@ -4570,7 +4598,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } - final TaskRecord startTask = start.task; + final TaskRecord startTask = start.getTask(); boolean behindFullscreen = false; boolean updatedConfig = false; @@ -4578,7 +4606,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final TaskRecord task = mTaskHistory.get(taskIndex); final ArrayList<ActivityRecord> activities = task.mActivities; int activityIndex = - (start.task == task) ? activities.indexOf(start) : activities.size() - 1; + (start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1; for (; activityIndex >= 0; --activityIndex) { final ActivityRecord r = activities.get(activityIndex); updatedConfig |= r.ensureActivityConfigurationLocked(0 /* globalChanges */, @@ -4739,7 +4767,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai || filterByClasses.contains(r.realActivity.getClassName()))) || (packageName == null && r.userId == userId); if ((userId == UserHandle.USER_ALL || r.userId == userId) - && (sameComponent || r.task == lastTask) + && (sameComponent || r.getTask() == lastTask) && (r.app == null || evenPersistent || !r.app.persistent)) { if (!doit) { if (r.finishing) { @@ -4765,7 +4793,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } r.app = null; } - lastTask = r.task; + lastTask = r.getTask(); if (finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop", true)) { // r has been deleted from mActivities, accommodate. @@ -4816,7 +4844,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_ALL) Slog.v( TAG, r.intent.getComponent().flattenToShortString() - + ": task=" + r.task); + + ": task=" + r.getTask()); } RunningTaskInfo ci = new RunningTaskInfo(); @@ -4832,8 +4860,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai topTask = false; } - if (top.task != null) { - ci.description = top.task.lastDescription; + if (top.getTask() != null) { + ci.description = top.getTask().lastDescription; } ci.numActivities = numActivities; ci.numRunning = numRunning; @@ -4982,9 +5010,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai task.removeWindowContainer(); } - final ActivityRecord r = mResumedActivity; - if (r != null && r.task == task) { - mResumedActivity = null; + for (ActivityRecord record : task.mActivities) { + onActivityRemovedFromStack(record); } final int taskNdx = mTaskHistory.indexOf(task); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index f16849ddc672..b72cd73e85cf 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -164,6 +164,7 @@ import android.view.Display; import android.view.InputEvent; import android.view.Surface; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.ReferrerIntent; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -173,6 +174,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.am.ActivityStack.ActivityState; +import com.android.server.wm.StackWindowController; import com.android.server.wm.WindowManagerService; import java.io.FileDescriptor; @@ -553,9 +555,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - public ActivityStackSupervisor(ActivityManagerService service) { + public ActivityStackSupervisor(ActivityManagerService service, Looper looper) { mService = service; - mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper()); + mHandler = new ActivityStackSupervisorHandler(looper); mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext); mKeyguardController = new KeyguardController(service, this); } @@ -722,7 +724,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } if (prev != null) { - prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); + prev.getTask().setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); } mHomeStack.moveHomeStackTaskToTop(); @@ -1314,7 +1316,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.updateLruProcessLocked(app, true, null); mService.updateOomAdjLocked(); - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV) { setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "mLockTaskAuth==LAUNCHABLE", false); @@ -2622,7 +2624,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } for (int k = 0; k < proc.activities.size(); k++) { - TaskRecord otherTask = proc.activities.get(k).task; + TaskRecord otherTask = proc.activities.get(k).getTask(); if (tr.taskId != otherTask.taskId && otherTask.inRecents) { // Don't kill process(es) that has an activity in a different task that is // also in recents. @@ -2837,7 +2839,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP); try { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (r == task.getStack().getVisibleBehindActivity()) { // An activity can't be pinned and visible behind at the same time. Go ahead and @@ -2910,7 +2912,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final ActivityStack stack = r.getStack(); if (stack == null) { Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: r=" @@ -3203,7 +3205,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Called when WindowManager has finished animating the launchingBehind activity to the back. private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final ActivityStack stack = task.getStack(); r.mLaunchTaskBehind = false; @@ -3216,7 +3218,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // task has been shown briefly final ActivityRecord top = stack.topActivity(); if (top != null) { - top.task.touchActiveTime(); + top.getTask().touchActiveTime(); } } @@ -3315,17 +3317,19 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r); continue; } - if (r.task != null) { - if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + r.task + + final TaskRecord task = r.getTask(); + if (task != null) { + if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task + " from " + r); if (firstTask == null) { - firstTask = r.task; - } else if (firstTask != r.task) { + firstTask = task; + } else if (firstTask != task) { if (tasks == null) { tasks = new ArraySet<>(); tasks.add(firstTask); } - tasks.add(r.task); + tasks.add(task); } } } @@ -3664,8 +3668,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D pw.println(header2); header2 = null; } - if (lastTask != r.task) { - lastTask = r.task; + if (lastTask != r.getTask()) { + lastTask = r.getTask(); pw.print(prefix); pw.print(full ? "* " : " "); pw.println(lastTask); @@ -4080,7 +4084,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } final ActivityRecord r = topRunningActivityLocked(); - final TaskRecord task = r != null ? r.task : null; + final TaskRecord task = r != null ? r.getTask() : null; if (mLockTaskModeTasks.isEmpty() && task != null && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) { // This task must have just been authorized. @@ -4390,19 +4394,24 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D synchronized (mService) { mStackId = stackId; mActivityDisplay = activityDisplay; - switch (mStackId) { - case PINNED_STACK_ID: - new PinnedActivityStack(this, mRecentTasks, onTop); - break; - default: - new ActivityStack(this, mRecentTasks, onTop); - break; - } mIdString = "ActivtyContainer{" + mStackId + "}"; + + createStack(stackId, onTop); if (DEBUG_STACK) Slog.d(TAG_STACK, "Creating " + this); } } + protected void createStack(int stackId, boolean onTop) { + switch (stackId) { + case PINNED_STACK_ID: + new PinnedActivityStack(this, mRecentTasks, onTop); + break; + default: + new ActivityStack(this, mRecentTasks, onTop); + break; + } + } + /** * Adds the stack to specified display. Also calls WindowManager to do the same from * {@link ActivityStack#reparent(ActivityDisplay, boolean)}. @@ -4926,7 +4935,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.mActivityStarter.postStartActivityProcessing(task.getTopActivity(), ActivityManager.START_TASK_TO_FRONT, - sourceRecord != null ? sourceRecord.task.getStackId() : INVALID_STACK_ID, + sourceRecord != null ? sourceRecord.getTask().getStackId() : INVALID_STACK_ID, sourceRecord, task.getStack()); return ActivityManager.START_TASK_TO_FRONT; } diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java index 547161ac4169..cafc4f0ecc96 100644 --- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java @@ -188,10 +188,10 @@ class ActivityStartInterceptor { } ActivityRecord homeActivityRecord = mSupervisor.getHomeActivity(); - if (homeActivityRecord != null && homeActivityRecord.task != null) { + if (homeActivityRecord != null && homeActivityRecord.getTask() != null) { // Showing credential confirmation activity in home task to avoid stopping multi-windowed // mode after showing the full-screen credential confirmation activity. - mActivityOptions.setLaunchTaskId(homeActivityRecord.task.taskId); + mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId); } final UserInfo parent = mUserManager.getProfileParent(mUserId); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 18e74080490d..b4085697f2da 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -79,7 +79,6 @@ import static com.android.server.am.ActivityStack.ActivityState.RESUMED; import static com.android.server.am.ActivityStack.STACK_INVISIBLE; import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED; import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; -import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS; @@ -87,8 +86,6 @@ import static com.android.server.am.EventLogTags.AM_NEW_INTENT; import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; -import static java.lang.Integer.MAX_VALUE; - import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -335,7 +332,7 @@ class ActivityStarter { } if (err == ActivityManager.START_SUCCESS && sourceRecord != null - && sourceRecord.task.voiceSession != null) { + && sourceRecord.getTask().voiceSession != null) { // If this activity is being launched as part of a voice session, we need // to ensure that it is safe to do so. If the upcoming activity will also // be part of the voice session, we can only launch it if it has explicitly @@ -575,7 +572,7 @@ class ActivityStarter { // visibility instead of using this flag. final boolean noDisplayActivityOverHome = sourceRecord != null && sourceRecord.noDisplay - && sourceRecord.task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE; + && sourceRecord.getTask().getTaskToReturnTo() == HOME_ACTIVITY_TYPE; if (startedActivityStackId == DOCKED_STACK_ID && (prevFocusedStackId == HOME_STACK_ID || noDisplayActivityOverHome)) { final ActivityStack homeStack = mSupervisor.getStack(HOME_STACK_ID); @@ -625,7 +622,7 @@ class ActivityStarter { FLAG_ACTIVITY_TASK_ON_HOME); ActivityOptions options = (optionsBundle != null ? new ActivityOptions(optionsBundle) : ActivityOptions.makeBasic()); - options.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId); + options.setLaunchTaskId(mSupervisor.getHomeActivity().getTask().taskId); mService.mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT); } @@ -763,7 +760,7 @@ class ActivityStarter { newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName); newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, - hist.task.taskId); + hist.getTask().taskId); } newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP, aInfo.packageName); @@ -967,8 +964,8 @@ class ActivityStarter { // If we are not able to proceed, disassociate the activity from the task. Leaving an // activity in an incomplete state can lead to issues, such as performing operations // without a window container. - if (result != START_SUCCESS && mStartActivity.task != null) { - mStartActivity.task.removeActivity(mStartActivity); + if (result != START_SUCCESS && mStartActivity.getTask() != null) { + mStartActivity.getTask().removeActivity(mStartActivity); } mService.mWindowManager.continueSurfaceLayout(); } @@ -1002,7 +999,7 @@ class ActivityStarter { // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but // still needs to be a lock task mode violation since the task gets cleared out and // the device would otherwise leave the locked task. - if (mSupervisor.isLockTaskModeViolation(mReusedActivity.task, + if (mSupervisor.isLockTaskModeViolation(mReusedActivity.getTask(), (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) { mSupervisor.showLockTaskToast(); @@ -1010,13 +1007,13 @@ class ActivityStarter { return START_RETURN_LOCK_TASK_MODE_VIOLATION; } - if (mStartActivity.task == null) { - mStartActivity.task = mReusedActivity.task; + if (mStartActivity.getTask() == null) { + mStartActivity.setTask(mReusedActivity.getTask()); } - if (mReusedActivity.task.intent == null) { + if (mReusedActivity.getTask().intent == null) { // This task was started because of movement of the activity based on affinity... // Now that we are actually launching it, we can assign the base intent. - mReusedActivity.task.setIntent(mStartActivity); + mReusedActivity.getTask().setIntent(mStartActivity); } // This code path leads to delivering a new intent, we want to make sure we schedule it @@ -1025,7 +1022,7 @@ class ActivityStarter { if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || isDocumentLaunchesIntoExisting(mLaunchFlags) || mLaunchSingleInstance || mLaunchSingleTask) { - final TaskRecord task = mReusedActivity.task; + final TaskRecord task = mReusedActivity.getTask(); // In this situation we want to remove all activities from the task up to the one // being started. In most cases this means we are resetting the task to its initial @@ -1037,17 +1034,17 @@ class ActivityStarter { // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The // task reference is needed in the call below to // {@link setTargetStackAndMoveToFrontIfNeeded}. - if (mReusedActivity.task == null) { - mReusedActivity.task = task; + if (mReusedActivity.getTask() == null) { + mReusedActivity.setTask(task); } if (top != null) { if (top.frontOfTask) { // Activity aliases may mean we use different intents for the top activity, // so make sure the task now has the identity of the new intent. - top.task.setIntent(mStartActivity); + top.getTask().setIntent(mStartActivity); } - ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask()); top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); } @@ -1098,7 +1095,7 @@ class ActivityStarter { && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop || mLaunchSingleTask); if (dontStart) { - ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask()); // For paranoia, make sure we have correctly resumed the top activity. topStack.mLastPausedActivity = null; if (mDoResume) { @@ -1116,14 +1113,14 @@ class ActivityStarter { // Don't use mStartActivity.task to show the toast. We're not starting a new activity // but reusing 'top'. Fields in mStartActivity may not be fully initialized. mSupervisor.handleNonResizableTaskIfNeeded( - top.task, preferredLaunchStackId, topStack.mStackId); + top.getTask(), preferredLaunchStackId, topStack.mStackId); return START_DELIVERED_TO_TOP; } boolean newTask = false; final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null) - ? mSourceRecord.task : null; + ? mSourceRecord.getTask() : null; // Should this be considered a new task? int result = START_SUCCESS; @@ -1150,14 +1147,15 @@ class ActivityStarter { mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent, mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid)); if (mSourceRecord != null) { - mStartActivity.task.setTaskToReturnTo(mSourceRecord); + mStartActivity.getTask().setTaskToReturnTo(mSourceRecord); } if (newTask) { EventLog.writeEvent( - EventLogTags.AM_CREATE_TASK, mStartActivity.userId, mStartActivity.task.taskId); + EventLogTags.AM_CREATE_TASK, mStartActivity.userId, + mStartActivity.getTask().taskId); } ActivityStack.logStartActivity( - EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task); + EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask()); mTargetStack.mLastPausedActivity = null; sendPowerHintForLaunchStartIfNeeded(false /* forceSend */); @@ -1165,7 +1163,8 @@ class ActivityStarter { mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition, mOptions); if (mDoResume) { - final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked(); + final ActivityRecord topTaskActivity = + mStartActivity.getTask().topRunningActivityLocked(); if (!mTargetStack.isFocusable() || (topTaskActivity != null && topTaskActivity.mTaskOverlay && mStartActivity != topTaskActivity)) { @@ -1197,7 +1196,7 @@ class ActivityStarter { mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); mSupervisor.handleNonResizableTaskIfNeeded( - mStartActivity.task, preferredLaunchStackId, mTargetStack.mStackId); + mStartActivity.getTask(), preferredLaunchStackId, mTargetStack.mStackId); return START_SUCCESS; } @@ -1424,7 +1423,7 @@ class ActivityStarter { + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent); mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK; mNewTaskInfo = mSourceRecord.info; - mNewTaskIntent = mSourceRecord.task.intent; + mNewTaskIntent = mSourceRecord.getTask().intent; } mSourceRecord = null; mSourceStack = null; @@ -1516,15 +1515,16 @@ class ActivityStarter { ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); - if (curTop != null - && (curTop.task != intentActivity.task || curTop.task != focusStack.topTask()) + final TaskRecord topTask = curTop != null ? curTop.getTask() : null; + if (topTask != null + && (topTask != intentActivity.getTask() || topTask != focusStack.topTask()) && !mAvoidMoveToFront) { mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); if (mSourceRecord == null || (mSourceStack.topActivity() != null && - mSourceStack.topActivity().task == mSourceRecord.task)) { + mSourceStack.topActivity().getTask() == mSourceRecord.getTask())) { // We really do want to push this one into the user's face, right now. if (mLaunchTaskBehind && mSourceRecord != null) { - intentActivity.setTaskToAffiliateWith(mSourceRecord.task); + intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask()); } mMovedOtherTask = true; @@ -1539,13 +1539,13 @@ class ActivityStarter { == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); if (!willClearTask) { final ActivityStack launchStack = getLaunchStack( - mStartActivity, mLaunchFlags, mStartActivity.task, mOptions); + mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions); + final TaskRecord intentTask = intentActivity.getTask(); if (launchStack == null || launchStack == mTargetStack) { // We only want to move to the front, if we aren't going to launch on a // different stack. If we launch on a different stack, we will put the // task on top there. - mTargetStack.moveTaskToFrontLocked( - intentActivity.task, mNoAnimation, mOptions, + mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "bringingFoundTaskToFront"); mMovedToFront = true; } else if (launchStack.mStackId == DOCKED_STACK_ID @@ -1553,7 +1553,7 @@ class ActivityStarter { if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { // If we want to launch adjacent and mTargetStack is not the computed // launch stack - move task to top of computed stack. - intentActivity.task.reparent(launchStack.mStackId, ON_TOP, + intentTask.reparent(launchStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "launchToSide"); } else { @@ -1561,8 +1561,8 @@ class ActivityStarter { // We choose to move task to front instead of launching it adjacent // when specific stack was requested explicitly and it appeared to be // adjacent stack, but FLAG_ACTIVITY_LAUNCH_ADJACENT was not set. - mTargetStack.moveTaskToFrontLocked(intentActivity.task, mNoAnimation, - mOptions, mStartActivity.appTimeTracker, + mTargetStack.moveTaskToFrontLocked(intentTask, + mNoAnimation, mOptions, mStartActivity.appTimeTracker, "bringToFrontInsteadOfAdjacentLaunch"); } mMovedToFront = true; @@ -1570,7 +1570,7 @@ class ActivityStarter { // Target and computed stacks are on different displays and we've // found a matching task - move the existing instance to that display and // move it to front. - intentActivity.task.reparent(launchStack.mStackId, ON_TOP, + intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "reparentToDisplay"); mMovedToFront = true; @@ -1582,7 +1582,7 @@ class ActivityStarter { intentActivity.showStartingWindow(null /* prev */, false /* newTask */, true /* taskSwitch */); } - updateTaskReturnToType(intentActivity.task, mLaunchFlags, focusStack); + updateTaskReturnToType(intentActivity.getTask(), mLaunchFlags, focusStack); } } if (!mMovedToFront && mDoResume) { @@ -1591,7 +1591,7 @@ class ActivityStarter { mTargetStack.moveToFront("intentActivityFound"); } - mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.task, INVALID_STACK_ID, + mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), INVALID_STACK_ID, mTargetStack.mStackId); // If the caller has requested that the target task be reset, then do so. @@ -1635,7 +1635,7 @@ class ActivityStarter { // launching another activity. // TODO(b/36119896): We shouldn't trigger activity launches in this path since we are // already launching one. - final TaskRecord task = intentActivity.task; + final TaskRecord task = intentActivity.getTask(); task.performClearTaskLocked(); mReuseTask = task; mReuseTask.setIntent(mStartActivity); @@ -1646,7 +1646,7 @@ class ActivityStarter { mMovedOtherTask = true; } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || mLaunchSingleInstance || mLaunchSingleTask) { - ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity, + ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity, mLaunchFlags); if (top == null) { // A special case: we need to start the activity because it is not currently @@ -1655,11 +1655,11 @@ class ActivityStarter { mAddingToTask = true; // We are no longer placing the activity in the task we previously thought we were. - mStartActivity.task = null; + mStartActivity.setTask(null); // Now pretend like this activity is being started by the top of its task, so it // is put in the right place. mSourceRecord = intentActivity; - final TaskRecord task = mSourceRecord.task; + final TaskRecord task = mSourceRecord.getTask(); if (task != null && task.getStack() == null) { // Target stack got cleared when we all activities were removed above. // Go ahead and reset it. @@ -1669,7 +1669,7 @@ class ActivityStarter { !mLaunchTaskBehind /* toTop */, "startActivityUnchecked"); } } - } else if (mStartActivity.realActivity.equals(intentActivity.task.realActivity)) { + } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) { // In this case the top activity on the task is the same as the one being launched, // so we take that as a request to bring the task to the foreground. If the top // activity in the task is the root activity, deliver this new intent to it if it @@ -1677,13 +1677,13 @@ class ActivityStarter { if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop) && intentActivity.realActivity.equals(mStartActivity.realActivity)) { ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, - intentActivity.task); + intentActivity.getTask()); if (intentActivity.frontOfTask) { - intentActivity.task.setIntent(mStartActivity); + intentActivity.getTask().setIntent(mStartActivity); } intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); - } else if (!intentActivity.task.isSameIntentFilter(mStartActivity)) { + } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) { // In this case we are launching the root activity of the task, but with a // different intent. We should start a new instance on top. mAddingToTask = true; @@ -1696,13 +1696,13 @@ class ActivityStarter { // current task. mAddingToTask = true; mSourceRecord = intentActivity; - } else if (!intentActivity.task.rootWasReset) { + } else if (!intentActivity.getTask().rootWasReset) { // In this case we are launching into an existing task that has not yet been started // from its front door. The current task has been brought to the front. Ideally, // we'd probably like to place this new task at the bottom of its stack, but that's // a little hard to do with the current organization of the code so for now we'll // just drop it. - intentActivity.task.setIntent(mStartActivity); + intentActivity.getTask().setIntent(mStartActivity); } } @@ -1736,11 +1736,11 @@ class ActivityStarter { mService.resizeStack( stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1); } else { - mStartActivity.task.updateOverrideConfiguration(mLaunchBounds); + mStartActivity.getTask().updateOverrideConfiguration(mLaunchBounds); } } if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in new task " + mStartActivity.task); + + " in new task " + mStartActivity.getTask()); } else { addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask"); } @@ -1749,7 +1749,7 @@ class ActivityStarter { mStartActivity.setTaskToAffiliateWith(taskToAffiliate); } - if (mSupervisor.isLockTaskModeViolation(mStartActivity.task)) { + if (mSupervisor.isLockTaskModeViolation(mStartActivity.getTask())) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } @@ -1758,7 +1758,7 @@ class ActivityStarter { // If stack id is specified in activity options, usually it means that activity is // launched not from currently focused stack (e.g. from SysUI or from shell) - in // that case we check the target stack. - updateTaskReturnToType(mStartActivity.task, mLaunchFlags, + updateTaskReturnToType(mStartActivity.getTask(), mLaunchFlags, preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : topStack); } if (mDoResume) { @@ -1768,19 +1768,19 @@ class ActivityStarter { } private int setTaskFromSourceRecord() { - if (mSupervisor.isLockTaskModeViolation(mSourceRecord.task)) { + if (mSupervisor.isLockTaskModeViolation(mSourceRecord.getTask())) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } - final TaskRecord sourceTask = mSourceRecord.task; + final TaskRecord sourceTask = mSourceRecord.getTask(); final ActivityStack sourceStack = mSourceRecord.getStack(); // We only want to allow changing stack if the target task is not the top one, // otherwise we would move the launching task to the other side, rather than show // two side by side. final boolean moveStackAllowed = sourceStack.topTask() != sourceTask; if (moveStackAllowed) { - mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.task, + mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions); } @@ -1805,7 +1805,7 @@ class ActivityStarter { ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags); mKeepCurTransition = true; if (top != null) { - ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask()); top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); // For paranoia, make sure we have correctly resumed the top activity. mTargetStack.mLastPausedActivity = null; @@ -1821,7 +1821,7 @@ class ActivityStarter { // stack if so. final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity); if (top != null) { - final TaskRecord task = top.task; + final TaskRecord task = top.getTask(); task.moveActivityToFrontLocked(top); top.updateOptionsLocked(mOptions); ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task); @@ -1838,7 +1838,7 @@ class ActivityStarter { // the same task as the one that is starting it. addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord"); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in existing task " + mStartActivity.task + " from source " + mSourceRecord); + + " in existing task " + mStartActivity.getTask() + " from source " + mSourceRecord); return START_SUCCESS; } @@ -1861,7 +1861,7 @@ class ActivityStarter { || mLaunchSingleTop || mLaunchSingleTask) { mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "inTaskToFront"); - ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask()); if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { // We don't need to start a new activity, and the client said not to do // anything if that is the case, so this is it! @@ -1901,7 +1901,7 @@ class ActivityStarter { addOrReparentStartingActivity(mInTask, "setTaskFromInTask"); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in explicit task " + mStartActivity.task); + + " in explicit task " + mStartActivity.getTask()); return START_SUCCESS; } @@ -1913,17 +1913,17 @@ class ActivityStarter { mTargetStack.moveToFront("addingToTopTask"); } final ActivityRecord prev = mTargetStack.topActivity(); - final TaskRecord task = (prev != null) ? prev.task : mTargetStack.createTaskRecord( + final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord( mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info, mIntent, null, null, true, mStartActivity.mActivityType); addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask"); mTargetStack.positionChildWindowContainerAtTop(task); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in new guessed " + mStartActivity.task); + + " in new guessed " + mStartActivity.getTask()); } private void addOrReparentStartingActivity(TaskRecord parent, String reason) { - if (mStartActivity.task == null || mStartActivity.task == parent) { + if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) { parent.addActivityToTop(mStartActivity); } else { mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason); @@ -1973,7 +1973,7 @@ class ActivityStarter { private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds, int launchFlags, ActivityOptions aOptions) { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions); if (stack != null) { return stack; diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 21c131c3616f..ba72dcf23c98 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -98,6 +98,7 @@ class AppErrors { AppErrors(Context context, ActivityManagerService service) { + context.assertRuntimeOverlayThemable(); mService = service; mContext = context; } diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java index 5f7f67a8a5e8..347a357aae04 100644 --- a/services/core/java/com/android/server/am/BaseErrorDialog.java +++ b/services/core/java/com/android/server/am/BaseErrorDialog.java @@ -34,6 +34,7 @@ class BaseErrorDialog extends AlertDialog { public BaseErrorDialog(Context context) { super(context, com.android.internal.R.style.Theme_Dialog_AppError); + context.assertRuntimeOverlayThemable(); getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 27a2461a8827..c7f20b9ff904 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -660,17 +660,6 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta // we are coming from in WM before we reparent because it became empty. mWindowContainerController.reparent(toStack.getWindowContainerController(), position); - // Reset the resumed activity on the previous stack - if (wasResumed) { - sourceStack.mResumedActivity = null; - } - - // Reset the paused activity on the previous stack - if (wasPaused) { - sourceStack.mPausingActivity = null; - sourceStack.removeTimeoutsForActivityLocked(r); - } - // Move the task sourceStack.removeTask(this, reason, REMOVE_TASK_MODE_MOVING); toStack.addTask(this, position, false /* schedulePictureInPictureModeChange */, reason); @@ -1212,14 +1201,13 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta * be in the current task or unparented to any task. */ void addActivityAtIndex(int index, ActivityRecord r) { - if (r.task != null && r.task != this) { + TaskRecord task = r.getTask(); + if (task != null && task != this) { throw new IllegalArgumentException("Can not add r=" + " to task=" + this - + " current parent=" + r.task); + + " current parent=" + task); } - // TODO(b/36505427): Maybe make task private to ActivityRecord so we can also do - // onParentChanged() within the setter? - r.task = this; - r.onParentChanged(); + + r.setTask(this); // Remove r first, and if it wasn't already in the list and it's fullscreen, count it. if (!mActivities.remove(r) && r.fullscreen) { @@ -1274,15 +1262,21 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta } /** - * @return true if this was the last activity in the task + * Removes the specified activity from this task. + * @param r The {@link ActivityRecord} to remove. + * @return true if this was the last activity in the task. */ boolean removeActivity(ActivityRecord r) { - if (r.task != this) { + return removeActivity(r, false /*reparenting*/); + } + + boolean removeActivity(ActivityRecord r, boolean reparenting) { + if (r.getTask() != this) { throw new IllegalArgumentException( "Activity=" + r + " does not belong to task=" + this); } - r.task = null; + r.setTask(null /*task*/, reparenting); if (mActivities.remove(r) && r.fullscreen) { // Was previously in list. @@ -1437,7 +1431,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta TaskThumbnail getTaskThumbnailLocked() { if (mStack != null) { final ActivityRecord resumedActivity = mStack.mResumedActivity; - if (resumedActivity != null && resumedActivity.task == this) { + if (resumedActivity != null && resumedActivity.getTask() == this) { final Bitmap thumbnail = resumedActivity.screenshotActivityLocked(); setLastThumbnailLocked(thumbnail); } @@ -1619,6 +1613,9 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta String iconFilename = null; int colorPrimary = 0; int colorBackground = 0; + int statusBarColor = 0; + int navigationBarColor = 0; + boolean topActivity = true; for (--activityNdx; activityNdx >= 0; --activityNdx) { final ActivityRecord r = mActivities.get(activityNdx); if (r.taskDescription != null) { @@ -1631,13 +1628,16 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta if (colorPrimary == 0) { colorPrimary = r.taskDescription.getPrimaryColor(); } - if (colorBackground == 0) { + if (topActivity) { colorBackground = r.taskDescription.getBackgroundColor(); + statusBarColor = r.taskDescription.getStatusBarColor(); + navigationBarColor = r.taskDescription.getNavigationBarColor(); } } + topActivity = false; } lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary, - colorBackground); + colorBackground, statusBarColor, navigationBarColor); if (mWindowContainerController != null) { mWindowContainerController.setTaskDescription(lastTaskDescription); } @@ -1953,7 +1953,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta task.updateOverrideConfiguration(bounds); for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { - activities.get(activityNdx).task = task; + activities.get(activityNdx).setTask(task); } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 70e56b09de13..c11f5316f769 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -55,6 +55,7 @@ import android.hardware.hdmi.HdmiTvClient; import android.hardware.usb.UsbManager; import android.media.AudioAttributes; import android.media.AudioDevicePort; +import android.media.AudioFocusInfo; import android.media.AudioSystem; import android.media.AudioFormat; import android.media.AudioManager; @@ -5634,8 +5635,9 @@ public class AudioService extends IAudioService.Stub clientId, callingPackageName, flags); } - public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa) { - return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa); + public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa, + String callingPackageName) { + return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName); } public void unregisterAudioFocusClient(String clientId) { @@ -5650,6 +5652,7 @@ public class AudioService extends IAudioService.Stub return mMediaFocusControl.getFocusRampTimeMs(focusGain, attr); } + //========================================================================================== private boolean readCameraSoundForced() { return SystemProperties.getBoolean("audio.camerasound.force", false) || mContext.getResources().getBoolean( @@ -6430,7 +6433,7 @@ public class AudioService extends IAudioService.Stub // Audio policy management //========================================================================================== public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb, - boolean hasFocusListener) { + boolean hasFocusListener, boolean isFocusPolicy) { AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback); if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder() @@ -6452,7 +6455,8 @@ public class AudioService extends IAudioService.Stub Slog.e(TAG, "Cannot re-register policy"); return null; } - AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener); + AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener, + isFocusPolicy); pcb.asBinder().linkToDeath(app, 0/*flags*/); regId = app.getRegistrationId(); mAudioPolicies.put(pcb.asBinder(), app); @@ -6650,15 +6654,21 @@ public class AudioService extends IAudioService.Stub * is handling ducking for audio focus. */ int mFocusDuckBehavior = AudioPolicy.FOCUS_POLICY_DUCKING_DEFAULT; + boolean mIsFocusPolicy = false; AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token, - boolean hasFocusListener) { + boolean hasFocusListener, boolean isFocusPolicy) { super(config); setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++)); mPolicyCallback = token; mHasFocusListener = hasFocusListener; if (mHasFocusListener) { mMediaFocusControl.addFocusFollower(mPolicyCallback); + // can only ever be true if there is a focus listener + if (isFocusPolicy) { + mIsFocusPolicy = true; + mMediaFocusControl.setFocusPolicy(mPolicyCallback); + } } connectMixes(); } @@ -6676,6 +6686,9 @@ public class AudioService extends IAudioService.Stub } void release() { + if (mIsFocusPolicy) { + mMediaFocusControl.unsetFocusPolicy(mPolicyCallback); + } if (mFocusDuckBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) { mMediaFocusControl.setDuckingInExtPolicyAvailable(false); } @@ -6690,6 +6703,22 @@ public class AudioService extends IAudioService.Stub } }; + //====================== + // Audio policy: focus + //====================== + /** */ + public int dispatchFocusChange(AudioFocusInfo afi, int focusChange, IAudioPolicyCallback pcb) { + synchronized (mAudioPolicies) { + if (!mAudioPolicies.containsKey(pcb.asBinder())) { + throw new IllegalStateException("Unregistered AudioPolicy for focus dispatch"); + } + return mMediaFocusControl.dispatchFocusChange(afi, focusChange); + } + } + + //====================== + // misc + //====================== private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies = new HashMap<IBinder, AudioPolicyProxy>(); private int mAudioPolicyCounter = 0; // always accessed synchronized on mAudioPolicies diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java index 5275c0524d54..bcaa29593304 100644 --- a/services/core/java/com/android/server/audio/FocusRequester.java +++ b/services/core/java/com/android/server/audio/FocusRequester.java @@ -33,7 +33,7 @@ import java.io.PrintWriter; * @hide * Class to handle all the information about a user of audio focus. The lifecycle of each * instance is managed by android.media.MediaFocusControl, from its addition to the audio focus - * stack to its release. + * stack, or the map of focus owners for an external focus policy, to its release. */ public class FocusRequester { @@ -101,6 +101,21 @@ public class FocusRequester { mFocusController = ctlr; } + FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl, + IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr) { + mAttributes = afi.getAttributes(); + mClientId = afi.getClientId(); + mPackageName = afi.getPackageName(); + mCallingUid = afi.getClientUid(); + mFocusGainRequest = afi.getGainRequest(); + mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; + mGrantFlags = afi.getFlags(); + + mFocusDispatcher = afl; + mSourceRef = source; + mDeathHandler = hdlr; + mFocusController = ctlr; + } boolean hasSameClient(String otherClient) { try { @@ -118,6 +133,10 @@ public class FocusRequester { return (mSourceRef != null) && mSourceRef.equals(ib); } + boolean hasSameDispatcher(IAudioFocusDispatcher fd) { + return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd); + } + boolean hasSamePackage(String pack) { try { return mPackageName.compareTo(pack) == 0; @@ -369,6 +388,35 @@ public class FocusRequester { } } + int dispatchFocusChange(int focusChange) { + if (mFocusDispatcher == null) { + if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: no focus dispatcher"); } + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + if (focusChange == AudioManager.AUDIOFOCUS_NONE) { + if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: AUDIOFOCUS_NONE"); } + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } else if ((focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK + || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE + || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT + || focusChange == AudioManager.AUDIOFOCUS_GAIN) + && (mFocusGainRequest != focusChange)){ + Log.w(TAG, "focus gain was requested with " + mFocusGainRequest + + ", dispatching " + focusChange); + } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK + || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT + || focusChange == AudioManager.AUDIOFOCUS_LOSS) { + mFocusLossReceived = focusChange; + } + try { + mFocusDispatcher.dispatchAudioFocusChange(focusChange, mClientId); + } catch (android.os.RemoteException e) { + Log.v(TAG, "dispatchFocusChange: error talking to focus listener", e); + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; + } + AudioFocusInfo toAudioFocusInfo() { return new AudioFocusInfo(mAttributes, mCallingUid, mClientId, mPackageName, mFocusGainRequest, mFocusLossReceived, mGrantFlags); diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index b3f1548d4d92..821e78a27e33 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -16,6 +16,7 @@ package com.android.server.audio; +import android.annotation.NonNull; import android.app.AppOpsManager; import android.content.Context; import android.media.AudioAttributes; @@ -23,6 +24,7 @@ import android.media.AudioFocusInfo; import android.media.AudioManager; import android.media.AudioSystem; import android.media.IAudioFocusDispatcher; +import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.IBinder; @@ -32,7 +34,10 @@ import android.util.Log; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; import java.util.Stack; import java.text.DateFormat; @@ -43,6 +48,7 @@ import java.text.DateFormat; public class MediaFocusControl implements PlayerFocusEnforcer { private static final String TAG = "MediaFocusControl"; + static final boolean DEBUG = false; /** * set to true so the framework enforces ducking itself, without communicating to apps @@ -155,6 +161,13 @@ public class MediaFocusControl implements PlayerFocusEnforcer { while(stackIterator.hasNext()) { stackIterator.next().dump(pw); } + pw.println("\n"); + if (mFocusPolicy == null) { + pw.println("No external focus policy\n"); + } else { + pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n"); + dumpExtFocusPolicyFocusOwners(pw); + } } pw.println("\n"); pw.println(" Notify on duck: " + mNotifyFocusOwnerOnDuck + "\n"); @@ -234,6 +247,31 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } /** + * Helper function for external focus policy: + * Called synchronized on mAudioFocusLock + * Remove focus listeners from the list of potential focus owners for a particular client when + * it has died. + */ + private void removeFocusEntryForExtPolicy(IBinder cb) { + if (mFocusOwnersForFocusPolicy.isEmpty()) { + return; + } + boolean released = false; + final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet(); + final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator(); + while (ownerIterator.hasNext()) { + final Entry<String, FocusRequester> owner = ownerIterator.next(); + final FocusRequester fr = owner.getValue(); + if (fr.hasSameBinder(cb)) { + ownerIterator.remove(); + fr.release(); + notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo()); + break; + } + } + } + + /** * Helper function: * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. * The implementation guarantees that a state where focus cannot be immediately reassigned @@ -297,7 +335,11 @@ public class MediaFocusControl implements PlayerFocusEnforcer { public void binderDied() { synchronized(mAudioFocusLock) { - removeFocusStackEntryOnDeath(mCb); + if (mFocusPolicy != null) { + removeFocusEntryForExtPolicy(mCb); + } else { + removeFocusStackEntryOnDeath(mCb); + } } } } @@ -353,6 +395,34 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } } + private IAudioPolicyCallback mFocusPolicy = null; + + // Since we don't have a stack of focus owners when using an external focus policy, we keep + // track of all the focus requesters in this map, with their clientId as the key. This is + // used both for focus dispatch and death handling + private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy = + new HashMap<String, FocusRequester>(); + + void setFocusPolicy(IAudioPolicyCallback policy) { + if (policy == null) { + return; + } + synchronized (mAudioFocusLock) { + mFocusPolicy = policy; + } + } + + void unsetFocusPolicy(IAudioPolicyCallback policy) { + if (policy == null) { + return; + } + synchronized (mAudioFocusLock) { + if (mFocusPolicy == policy) { + mFocusPolicy = null; + } + } + } + /** * @param pcb non null */ @@ -409,6 +479,100 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } } + /** + * Called synchronized on mAudioFocusLock + * @param afi + * @param requestResult + * @return true if the external audio focus policy (if any) is handling the focus request + */ + boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, int requestResult, + IAudioFocusDispatcher fd, IBinder cb) { + if (mFocusPolicy == null) { + return false; + } + if (DEBUG) { + Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId() + + " dispatcher=" + fd); + } + final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); + if (existingFr != null) { + if (!existingFr.hasSameDispatcher(fd)) { + existingFr.release(); + final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb); + mFocusOwnersForFocusPolicy.put(afi.getClientId(), + new FocusRequester(afi, fd, cb, hdlr, this)); + } + } else if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED + || requestResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { + // new focus (future) focus owner to keep track of + final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb); + mFocusOwnersForFocusPolicy.put(afi.getClientId(), + new FocusRequester(afi, fd, cb, hdlr, this)); + } + try { + //oneway + mFocusPolicy.notifyAudioFocusRequest(afi, requestResult); + } catch (RemoteException e) { + Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback " + + mFocusPolicy.asBinder(), e); + } + return true; + } + + /** + * Called synchronized on mAudioFocusLock + * @param afi + * @param requestResult + * @return true if the external audio focus policy (if any) is handling the focus request + */ + boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) { + if (mFocusPolicy == null) { + return false; + } + final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId()); + if (fr != null) { + fr.release(); + } + try { + //oneway + mFocusPolicy.notifyAudioFocusAbandon(afi); + } catch (RemoteException e) { + Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback " + + mFocusPolicy.asBinder(), e); + } + return true; + } + + /** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */ + int dispatchFocusChange(AudioFocusInfo afi, int focusChange) { + if (DEBUG) { + Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client=" + + afi.getClientId()); + } + synchronized (mAudioFocusLock) { + if (mFocusPolicy == null) { + if (DEBUG) { Log.v(TAG, "> failed: no focus policy" ); } + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + final FocusRequester fr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); + if (fr == null) { + if (DEBUG) { Log.v(TAG, "> failed: no such focus requester known" ); } + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + return fr.dispatchFocusChange(focusChange); + } + } + + private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) { + final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet(); + final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator(); + while (ownerIterator.hasNext()) { + final Entry<String, FocusRequester> owner = ownerIterator.next(); + final FocusRequester fr = owner.getValue(); + fr.dump(pw); + } + } + protected int getCurrentAudioFocus() { synchronized(mAudioFocusLock) { if (mFocusStack.empty()) { @@ -487,10 +651,23 @@ public class MediaFocusControl implements PlayerFocusEnforcer { & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); if (enteringRingOrCall) { mRingOrCallActive = true; } + final AudioFocusInfo afiForExtPolicy; + if (mFocusPolicy != null) { + // construct AudioFocusInfo as it will be communicated to audio focus policy + afiForExtPolicy = new AudioFocusInfo(aa, Binder.getCallingUid(), + clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/, + flags); + } else { + afiForExtPolicy = null; + } + + // handle delayed focus boolean focusGrantDelayed = false; if (!canReassignAudioFocus()) { if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) { - return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + final int result = AudioManager.AUDIOFOCUS_REQUEST_FAILED; + notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, result, fd, cb); + return result; } else { // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be // granted right now, so the requester will be inserted in the focus stack @@ -499,6 +676,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } } + // external focus policy: delay request for focus gain? + final int resultWithExtPolicy = AudioManager.AUDIOFOCUS_REQUEST_DELAYED; + if (notifyExtFocusPolicyFocusRequest_syncAf( + afiForExtPolicy, resultWithExtPolicy, fd, cb)) { + // stop handling focus request here as it is handled by external audio focus policy + return resultWithExtPolicy; + } + // handle the potential premature death of the new holder of the focus // (premature death == death before abandoning focus) // Register for client death notification @@ -569,7 +754,8 @@ public class MediaFocusControl implements PlayerFocusEnforcer { /** * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes) * */ - protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa) { + protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, + String callingPackageName) { // AudioAttributes are currently ignored, to be used for zones Log.i(TAG, " AudioFocus abandonAudioFocus() from uid/pid " + Binder.getCallingUid() + "/" + Binder.getCallingPid() @@ -577,6 +763,16 @@ public class MediaFocusControl implements PlayerFocusEnforcer { try { // this will take care of notifying the new focus owner if needed synchronized(mAudioFocusLock) { + // external focus policy? + if (mFocusPolicy != null) { + final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(), + clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/, + 0 /*flags*/); + if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) { + return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; + } + } + boolean exitingRingOrCall = mRingOrCallActive & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); if (exitingRingOrCall) { mRingOrCallActive = false; } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index a1a74377cf62..ddd918fdcc5d 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -339,6 +339,18 @@ public final class DisplayManagerService extends SystemService { } } + /** + * @see DisplayManagerInternal#getNonOverrideDisplayInfo(int, DisplayInfo) + */ + private void getNonOverrideDisplayInfoInternal(int displayId, DisplayInfo outInfo) { + synchronized (mSyncRoot) { + final LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null) { + display.getNonOverrideDisplayInfoLocked(outInfo); + } + } + } + private void performTraversalInTransactionFromWindowManagerInternal() { synchronized (mSyncRoot) { if (!mPendingTraversal) { @@ -1663,6 +1675,11 @@ public final class DisplayManagerService extends SystemService { } @Override + public void getNonOverrideDisplayInfo(int displayId, DisplayInfo outInfo) { + getNonOverrideDisplayInfoInternal(displayId, outInfo); + } + + @Override public void performTraversalInTransactionFromWindowManager() { performTraversalInTransactionFromWindowManagerInternal(); } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index a947b4106794..addad0b413a3 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -17,6 +17,7 @@ package com.android.server.display; import android.graphics.Rect; +import android.hardware.display.DisplayManagerInternal; import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; @@ -62,7 +63,18 @@ final class LogicalDisplay { private final int mDisplayId; private final int mLayerStack; - private DisplayInfo mOverrideDisplayInfo; // set by the window manager + /** + * Override information set by the window manager. Will be reported instead of {@link #mInfo} + * if not null. + * @see #setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo) + * @see #getDisplayInfoLocked() + */ + private DisplayInfo mOverrideDisplayInfo; + /** + * Current display info. Initialized with {@link #mBaseDisplayInfo}. Set to {@code null} if + * needs to be updated. + * @see #getDisplayInfoLocked() + */ private DisplayInfo mInfo; // The display device that this logical display is based on and which @@ -142,6 +154,13 @@ final class LogicalDisplay { } /** + * @see DisplayManagerInternal#getNonOverrideDisplayInfo(int, DisplayInfo) + */ + void getNonOverrideDisplayInfoLocked(DisplayInfo outInfo) { + outInfo.copyFrom(mBaseDisplayInfo); + } + + /** * Sets overridden logical display information from the window manager. * This method can be used to adjust application insets, rotation, and other * properties that the window manager takes care of. diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 9e4432d25c0b..dc2ebb4451b2 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -27,4 +27,15 @@ public abstract class NetworkPolicyManagerInternal { * Resets all policies associated with a given user. */ public abstract void resetUserState(int userId); + + /** + * @return true if the given uid is restricted from doing networking on metered networks. + */ + public abstract boolean isUidRestrictedOnMeteredNetworks(int uid); + + /** + * @return true if networking is blocked on the given interface for the given uid according + * to current networking policies. + */ + public abstract boolean isUidNetworkingBlocked(int uid, String ifname); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 4e1166b71d67..02e106e92aeb 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -248,8 +248,8 @@ import java.util.concurrent.TimeUnit; */ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { static final String TAG = "NetworkPolicy"; - private static final boolean LOGD = false; - private static final boolean LOGV = false; + private static final boolean LOGD = true; // UNDO + private static final boolean LOGV = true; // UNDO private static final int VERSION_INIT = 1; private static final int VERSION_ADDED_SNOOZE = 2; @@ -428,9 +428,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidState = new SparseIntArray(); - /** Higher priority listener before general event dispatch */ - private INetworkPolicyListener mConnectivityListener; - private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<>(); @@ -2237,15 +2234,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override - public void setConnectivityListener(INetworkPolicyListener listener) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - if (mConnectivityListener != null) { - throw new IllegalStateException("Connectivity listener already registered"); - } - mConnectivityListener = listener; - } - - @Override public void registerListener(INetworkPolicyListener listener) { // TODO: create permission for observing network policy mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); @@ -3556,7 +3544,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { case MSG_RULES_CHANGED: { final int uid = msg.arg1; final int uidRules = msg.arg2; - dispatchUidRulesChanged(mConnectivityListener, uid, uidRules); final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); @@ -3567,7 +3554,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } case MSG_METERED_IFACES_CHANGED: { final String[] meteredIfaces = (String[]) msg.obj; - dispatchMeteredIfacesChanged(mConnectivityListener, meteredIfaces); final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); @@ -3598,7 +3584,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } case MSG_RESTRICT_BACKGROUND_CHANGED: { final boolean restrictBackground = msg.arg1 != 0; - dispatchRestrictBackgroundChanged(mConnectivityListener, restrictBackground); final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); @@ -3616,7 +3601,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int policy = msg.arg2; final Boolean notifyApp = (Boolean) msg.obj; // First notify internal listeners... - dispatchUidPoliciesChanged(mConnectivityListener, uid, policy); final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); @@ -4049,6 +4033,74 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } + + /** + * @return true if the given uid is restricted from doing networking on metered networks. + */ + @Override + public boolean isUidRestrictedOnMeteredNetworks(int uid) { + final int uidRules; + final boolean isBackgroundRestricted; + synchronized (mUidRulesFirstLock) { + uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); + isBackgroundRestricted = mRestrictBackground; + } + return isBackgroundRestricted + && !hasRule(uidRules, RULE_ALLOW_METERED) + && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED); + } + + /** + * @return true if networking is blocked on the given interface for the given uid according + * to current networking policies. + */ + @Override + public boolean isUidNetworkingBlocked(int uid, String ifname) { + final int uidRules; + final boolean isBackgroundRestricted; + final boolean isNetworkMetered; + synchronized (mUidRulesFirstLock) { + uidRules = mUidRules.get(uid, RULE_NONE); + isBackgroundRestricted = mRestrictBackground; + synchronized (mNetworkPoliciesSecondLock) { + isNetworkMetered = mMeteredIfaces.contains(ifname); + } + } + if (hasRule(uidRules, RULE_REJECT_ALL)) { + if (LOGV) logUidStatus(uid, "blocked by power restrictions"); + return true; + } + if (!isNetworkMetered) { + if (LOGV) logUidStatus(uid, "allowed on unmetered network"); + return false; + } + if (hasRule(uidRules, RULE_REJECT_METERED)) { + if (LOGV) logUidStatus(uid, "blacklisted on metered network"); + return true; + } + if (hasRule(uidRules, RULE_ALLOW_METERED)) { + if (LOGV) logUidStatus(uid, "whitelisted on metered network"); + return false; + } + if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { + if (LOGV) logUidStatus(uid, "temporary whitelisted on metered network"); + return false; + } + if (isBackgroundRestricted) { + if (LOGV) logUidStatus(uid, "blocked when background is restricted"); + return true; + } + if (LOGV) logUidStatus(uid, "allowed by default"); + return false; + } + } + + private static boolean hasRule(int uidRules, int rule) { + return (uidRules & rule) != 0; + } + + private static void logUidStatus(int uid, String descr) { + Slog.d(TAG, String.format("uid %d is %s", uid, descr)); } /** diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 5bdef9ea7d49..3705946008f1 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -325,13 +325,11 @@ final class OverlayManagerServiceImpl { // Disable all other overlays. allOverlays.remove(oi); for (int i = 0; i < allOverlays.size(); i++) { - mSettings.setEnabled(allOverlays.get(i).packageName, userId, false); + // TODO: Optimize this to only send updates after all changes. + setEnabled(allOverlays.get(i).packageName, false, userId); } - final PackageInfo targetPackage = - mPackageManager.getPackageInfo(oi.targetPackageName, userId); - mSettings.setEnabled(packageName, userId, enable); - updateState(targetPackage, overlayPackage, userId); + setEnabled(packageName, enable, userId); return true; } catch (OverlayManagerSettings.BadKeyException e) { return false; diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java index b0730efe635c..f6e96b2bf876 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java +++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java @@ -27,6 +27,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.IBinder.DeathRecipient; import android.os.IRemoteCallback; import android.os.RemoteException; import android.os.SystemClock; @@ -47,7 +48,7 @@ import java.util.concurrent.TimeoutException; * service and handling all interactions in a timely manner. * @hide */ -final class EphemeralResolverConnection { +final class EphemeralResolverConnection implements DeathRecipient { // This is running in a critical section and the timeout must be sufficiently low private static final long BIND_SERVICE_TIMEOUT_MS = ("eng".equals(Build.TYPE)) ? 300 : 200; @@ -65,7 +66,7 @@ final class EphemeralResolverConnection { public EphemeralResolverConnection(Context context, ComponentName componentName) { mContext = context; - mIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE).setComponent(componentName); + mIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE).setComponent(componentName); } public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[], @@ -171,6 +172,15 @@ final class EphemeralResolverConnection { } } + @Override + public void binderDied() { + if (mRemoteInstance != null) { + mRemoteInstance.asBinder().unlinkToDeath(this, 0 /*flags*/); + } + mRemoteInstance = null; + mBindRequested = false; + } + /** * Asynchronous callback when results come back from ephemeral resolution phase two. */ @@ -183,7 +193,11 @@ final class EphemeralResolverConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { - mRemoteInstance = IInstantAppResolver.Stub.asInterface(service); + try { + service.linkToDeath(EphemeralResolverConnection.this, 0 /*flags*/); + mRemoteInstance = IInstantAppResolver.Stub.asInterface(service); + } catch (RemoteException e) { + } mLock.notifyAll(); } } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 7eef7adc7e12..9f7c4a2793db 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -66,6 +66,7 @@ public class Installer extends SystemService { public static final int FLAG_FREE_CACHE_V2 = 1 << 13; public static final int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; public static final int FLAG_FREE_CACHE_NOOP = 1 << 15; + public static final int FLAG_FORCE = 1 << 16; private final boolean mIsolated; @@ -202,6 +203,15 @@ public class Installer extends SystemService { } } + public void fixupAppData(String uuid, int flags) throws InstallerException { + if (!checkBeforeRemote()) return; + try { + mInstalld.fixupAppData(uuid, flags); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + public void moveCompleteApp(String fromUuid, String toUuid, String packageName, String dataAppName, int appId, String seInfo, int targetSdkVersion) throws InstallerException { diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 0b40fc533b5f..f79f6f40294c 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -242,18 +242,8 @@ public class LauncherAppsService extends SystemService { try { UserInfo callingUserInfo = mUm.getUserInfo(callingUserId); if (callingUserInfo.isManagedProfile()) { - - // STOPSHIP Remove the whitelist. - if ("com.google.android.talk".equals(callingPackage) - || "com.google.android.quicksearchbox".equals(callingPackage) - || "com.google.android.googlequicksearchbox".equals(callingPackage) - ) { - return false; - } - // STOPSHIP Change it to 'e'. - Slog.wtfStack(TAG, message + " by " + callingPackage + " for another profile " + Slog.w(TAG, message + " by " + callingPackage + " for another profile " + targetUserId + " from " + callingUserId); - return false; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 365a8e87d3ed..7da11e2a2978 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -842,11 +842,11 @@ public class PackageManagerService extends IPackageManager.Stub { /** The service connection to the ephemeral resolver */ final EphemeralResolverConnection mInstantAppResolverConnection; + /** Component used to show resolver settings for Instant Apps */ + final ComponentName mInstantAppResolverSettingsComponent; /** Component used to install ephemeral applications */ ComponentName mInstantAppInstallerComponent; - /** Component used to show resolver settings for Instant Apps */ - ComponentName mInstantAppResolverSettingsComponent; ActivityInfo mInstantAppInstallerActivity; final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo(); @@ -2385,6 +2385,10 @@ public class PackageManagerService extends IPackageManager.Stub { final VersionInfo ver = mSettings.getInternalVersion(); mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); + if (mIsUpgrade) { + logCriticalInfo(Log.INFO, + "Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT); + } // when upgrading from pre-M, promote system app permissions from install to runtime mPromoteSystemApps = @@ -2713,6 +2717,15 @@ public class PackageManagerService extends IPackageManager.Stub { UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */, true /* onlyCoreApps */); mPrepareAppDataFuture = SystemServerInitThreadPool.get().submit(() -> { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "fixup"); + try { + mInstaller.fixupAppData(StorageManager.UUID_PRIVATE_INTERNAL, + StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); + } catch (InstallerException e) { + Slog.w(TAG, "Trouble fixing GIDs", e); + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + if (deferPackages == null || deferPackages.isEmpty()) { return; } @@ -2803,11 +2816,13 @@ public class PackageManagerService extends IPackageManager.Stub { } mInstantAppResolverConnection = new EphemeralResolverConnection(mContext, ephemeralResolverComponent); + mInstantAppResolverSettingsComponent = + getEphemeralResolverSettingsLPr(ephemeralResolverComponent); } else { mInstantAppResolverConnection = null; + mInstantAppResolverSettingsComponent = null; } updateInstantAppInstallerLocked(); - mInstantAppResolverSettingsComponent = getEphemeralResolverSettingsLPr(); // Read and update the usage of dex files. // Do this at the end of PM init so that all the packages have their @@ -3041,7 +3056,7 @@ public class PackageManagerService extends IPackageManager.Stub { MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); - final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE); + final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE); final List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null, resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); @@ -3083,7 +3098,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private @Nullable ActivityInfo getEphemeralInstallerLPr() { - final Intent intent = new Intent(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE); + final Intent intent = new Intent(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); @@ -3115,35 +3130,18 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private @Nullable ComponentName getEphemeralResolverSettingsLPr() { - final Intent intent = new Intent(Intent.ACTION_EPHEMERAL_RESOLVER_SETTINGS); - intent.addCategory(Intent.CATEGORY_DEFAULT); - final int resolveFlags = - MATCH_DIRECT_BOOT_AWARE - | MATCH_DIRECT_BOOT_UNAWARE - | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); - final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, - resolveFlags, UserHandle.USER_SYSTEM); - Iterator<ResolveInfo> iter = matches.iterator(); - while (iter.hasNext()) { - final ResolveInfo rInfo = iter.next(); - final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName); - if (ps != null) { - final PermissionsState permissionsState = ps.getPermissionsState(); - if (permissionsState.hasPermission(Manifest.permission.ACCESS_INSTANT_APPS, 0)) { - continue; - } - } - iter.remove(); - } - if (matches.size() == 0) { + private @Nullable ComponentName getEphemeralResolverSettingsLPr( + @NonNull ComponentName resolver) { + final Intent intent = new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS) + .addCategory(Intent.CATEGORY_DEFAULT) + .setPackage(resolver.getPackageName()); + final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; + final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags, + UserHandle.USER_SYSTEM); + if (matches.isEmpty()) { return null; - } else if (matches.size() == 1) { - return matches.get(0).getComponentInfo().getComponentName(); - } else { - throw new RuntimeException( - "There must be at most one ephemeral resolver settings; found " + matches); } + return matches.get(0).getComponentInfo().getComponentName(); } private void primeDomainVerificationsLPw(int userId) { @@ -16961,6 +16959,15 @@ public class PackageManagerService extends IPackageManager.Stub { return; } + // Shared libraries for the package need to be updated. + synchronized (mPackages) { + try { + updateSharedLibrariesLPr(pkg, null); + } catch (PackageManagerException e) { + Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage()); + } + } + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Do not run PackageDexOptimizer through the local performDexOpt // method because `pkg` may not be in `mPackages` yet. @@ -17009,6 +17016,7 @@ public class PackageManagerService extends IPackageManager.Stub { args.user, installerPackageName, volumeUuid, res, args.installReason); } } + synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { @@ -20854,13 +20862,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); mSettings.dumpRestoredPermissionGrantsLPr(pw, dumpState); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) { - // XXX should handle packageName != null by dumping only install data that - // the given package is involved with. - if (dumpState.onTitlePrinted()) pw.println(); - mInstallerService.dump(new IndentingPrintWriter(pw, " ", 120)); - } - if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) { // XXX should handle packageName != null by dumping only install data that // the given package is involved with. @@ -20931,6 +20932,14 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } } + + // PackageInstaller should be called outside of mPackages lock + if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) { + // XXX should handle packageName != null by dumping only install data that + // the given package is involved with. + if (dumpState.onTitlePrinted()) pw.println(); + mInstallerService.dump(new IndentingPrintWriter(pw, " ", 120)); + } } private void dumpProto(FileDescriptor fd) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 6fb056a48f7e..554deaeb45c7 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4777,7 +4777,7 @@ final class Settings { pw.print(" notLaunched="); pw.print(ps.getNotLaunched(user.id)); pw.print(" enabled="); - pw.println(ps.getEnabled(user.id)); + pw.print(ps.getEnabled(user.id)); pw.print(" instant="); pw.println(ps.getInstantApp(user.id)); String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index b507df0b8447..95fb5af37b60 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2945,6 +2945,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } mStatusBar = win; mStatusBarController.setWindow(win); + setKeyguardOccludedLw(mKeyguardOccluded, true /* force */); break; case TYPE_NAVIGATION_BAR: mContext.enforceCallingOrSelfPermission( @@ -3832,7 +3833,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPendingKeyguardOccluded = occluded; mKeyguardOccludedChanged = true; } else { - setKeyguardOccludedLw(occluded); + setKeyguardOccludedLw(occluded, false /* force */); } } @@ -3841,7 +3842,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded=" + mPendingKeyguardOccluded); mKeyguardOccludedChanged = false; - if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) { + if (setKeyguardOccludedLw(mPendingKeyguardOccluded, false /* force */)) { return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER; } } @@ -5254,11 +5255,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - // Don't allow snapshots to influence SystemUI visibility flags. - // TODO: Revisit this once SystemUI flags for snapshots are handled correctly boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW - && attrs.type < FIRST_SYSTEM_WINDOW - && (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0; + && attrs.type < FIRST_SYSTEM_WINDOW; final int stackId = win.getStackId(); if (mTopFullscreenOpaqueWindowState == null && visible) { if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { @@ -5475,23 +5473,27 @@ public class PhoneWindowManager implements WindowManagerPolicy { * * @return Whether the flags have changed and we have to redo the layout. */ - private boolean setKeyguardOccludedLw(boolean isOccluded) { + private boolean setKeyguardOccludedLw(boolean isOccluded, boolean force) { if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded); boolean wasOccluded = mKeyguardOccluded; boolean showing = mKeyguardDelegate.isShowing(); if (wasOccluded && !isOccluded && showing) { mKeyguardOccluded = false; mKeyguardDelegate.setOccluded(false, true /* animate */); - mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD; - if (!mKeyguardDelegate.hasLockscreenWallpaper()) { - mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER; + if (mStatusBar != null) { + mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD; + if (!mKeyguardDelegate.hasLockscreenWallpaper()) { + mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER; + } } return true; } else if (!wasOccluded && isOccluded && showing) { mKeyguardOccluded = true; mKeyguardDelegate.setOccluded(true, false /* animate */); - mStatusBar.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD; - mStatusBar.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER; + if (mStatusBar != null) { + mStatusBar.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD; + mStatusBar.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER; + } return true; } else if (wasOccluded != isOccluded) { mKeyguardOccluded = isOccluded; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 9c4e700c87d3..a60dae7c7914 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -2671,11 +2671,11 @@ public final class PowerManagerService extends SystemService public void run() { synchronized (this) { if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) { - ShutdownThread.rebootSafeMode(mContext, confirm); + ShutdownThread.rebootSafeMode(getUiContext(), confirm); } else if (haltMode == HALT_MODE_REBOOT) { - ShutdownThread.reboot(mContext, reason, confirm); + ShutdownThread.reboot(getUiContext(), reason, confirm); } else { - ShutdownThread.shutdown(mContext, reason, confirm); + ShutdownThread.shutdown(getUiContext(), reason, confirm); } } } diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index 841e2a158c4f..864e83ef1f86 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -79,7 +79,7 @@ public final class ShutdownThread extends Thread { private static final int SHUTDOWN_VIBRATE_MS = 500; // state tracking - private static Object sIsStartedGuard = new Object(); + private static final Object sIsStartedGuard = new Object(); private static boolean sIsStarted = false; private static boolean mReboot; @@ -121,7 +121,8 @@ public final class ShutdownThread extends Thread { * state etc. Must be called from a Looper thread in which its UI * is shown. * - * @param context Context used to display the shutdown progress dialog. + * @param context Context used to display the shutdown progress dialog. This must be a context + * suitable for displaying UI (aka Themable). * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. * @param confirm true if user confirmation is needed before shutting down. */ @@ -132,7 +133,11 @@ public final class ShutdownThread extends Thread { shutdownInner(context, confirm); } - static void shutdownInner(final Context context, boolean confirm) { + private static void shutdownInner(final Context context, boolean confirm) { + // ShutdownThread is called from many places, so best to verify here that the context passed + // in is themed. + context.assertRuntimeOverlayThemable(); + // ensure that only one thread is trying to power down. // any additional calls are just returned synchronized (sIsStartedGuard) { @@ -204,7 +209,8 @@ public final class ShutdownThread extends Thread { * state etc. Must be called from a Looper thread in which its UI * is shown. * - * @param context Context used to display the shutdown progress dialog. + * @param context Context used to display the shutdown progress dialog. This must be a context + * suitable for displaying UI (aka Themable). * @param reason code to pass to the kernel (e.g. "recovery"), or null. * @param confirm true if user confirmation is needed before shutting down. */ @@ -220,7 +226,8 @@ public final class ShutdownThread extends Thread { * Request a reboot into safe mode. Must be called from a Looper thread in which its UI * is shown. * - * @param context Context used to display the shutdown progress dialog. + * @param context Context used to display the shutdown progress dialog. This must be a context + * suitable for displaying UI (aka Themable). * @param confirm true if user confirmation is needed before shutting down. */ public static void rebootSafeMode(final Context context, boolean confirm) { diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 212bd61dbc9a..32871bb0ba2c 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -16,6 +16,7 @@ package com.android.server.statusbar; +import android.app.ActivityThread; import android.app.StatusBarManager; import android.content.ComponentName; import android.content.Context; @@ -61,6 +62,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { private static final boolean SPEW = false; private final Context mContext; + private final WindowManagerService mWindowManager; private Handler mHandler = new Handler(); private NotificationDelegate mNotificationDelegate; @@ -761,8 +763,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub { enforceStatusBarService(); long identity = Binder.clearCallingIdentity(); try { + // ShutdownThread displays UI, so give it a UI context. mHandler.post(() -> - ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, false)); + ShutdownThread.shutdown(getUiContext(), + PowerManager.SHUTDOWN_USER_REQUESTED, false)); } finally { Binder.restoreCallingIdentity(identity); } @@ -777,10 +781,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub { long identity = Binder.clearCallingIdentity(); try { mHandler.post(() -> { + // ShutdownThread displays UI, so give it a UI context. if (safeMode) { - ShutdownThread.rebootSafeMode(mContext, false); + ShutdownThread.rebootSafeMode(getUiContext(), false); } else { - ShutdownThread.reboot(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, false); + ShutdownThread.reboot(getUiContext(), + PowerManager.SHUTDOWN_USER_REQUESTED, false); } }); } finally { @@ -1014,4 +1020,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } } } + + private static final Context getUiContext() { + return ActivityThread.currentActivityThread().getSystemUiContext(); + } } diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index 4b4be40880ee..2bc3c5f9abba 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -566,7 +566,7 @@ public class AppWindowContainerController return false; } - mContainer.startingData = new SnapshotStartingData(mService, snapshot.getSnapshot()); + mContainer.startingData = new SnapshotStartingData(mService, snapshot); scheduleAddStartingWindow(); return true; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 3c68e4ff4e7f..4ebf1fcb7206 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -180,11 +180,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Mapping from a token IBinder to a WindowToken object on this display. private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap(); + // Initial display metrics. int mInitialDisplayWidth = 0; int mInitialDisplayHeight = 0; int mInitialDisplayDensity = 0; + + /** + * Overridden display size. Initialized with {@link #mInitialDisplayWidth} + * and {@link #mInitialDisplayHeight}, but can be set via shell command "adb shell wm size". + * @see WindowManagerService#setForcedDisplaySize(int, int, int) + */ int mBaseDisplayWidth = 0; int mBaseDisplayHeight = 0; + /** + * Overridden display density for current user. Initialized with {@link #mInitialDisplayDensity} + * but can be set from Settings or via shell command "adb shell wm density". + * @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int) + */ int mBaseDisplayDensity = 0; boolean mDisplayScalingDisabled; private final DisplayInfo mDisplayInfo = new DisplayInfo(); @@ -1497,8 +1509,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } void updateDisplayInfo() { + // Check if display metrics changed and update base values if needed. + updateBaseDisplayMetricsIfNeeded(); + mDisplay.getDisplayInfo(mDisplayInfo); mDisplay.getMetrics(mDisplayMetrics); + for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) { mTaskStackContainers.get(i).updateDisplayInfo(null); } @@ -1514,10 +1530,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth; - mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight; - mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi; - mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight); + updateBaseDisplayMetrics(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight, + mDisplayInfo.logicalDensityDpi); + mInitialDisplayWidth = mDisplayInfo.logicalWidth; + mInitialDisplayHeight = mDisplayInfo.logicalHeight; + mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi; } void getLogicalDisplayRect(Rect out) { @@ -1547,6 +1564,42 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + /** + * If display metrics changed, overrides are not set and it's not just a rotation - update base + * values. + */ + private void updateBaseDisplayMetricsIfNeeded() { + // Get real display metrics without overrides from WM. + mService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo); + final int orientation = mDisplayInfo.rotation; + final boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270); + final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth; + final int newHeight = rotated ? mDisplayInfo.logicalWidth : mDisplayInfo.logicalHeight; + final int newDensity = mDisplayInfo.logicalDensityDpi; + + final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth + || mInitialDisplayHeight != newHeight + || mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi; + + if (displayMetricsChanged) { + // Check if display size or density is forced. + final boolean isDisplaySizeForced = mBaseDisplayWidth != mInitialDisplayWidth + || mBaseDisplayHeight != mInitialDisplayHeight; + final boolean isDisplayDensityForced = mBaseDisplayDensity != mInitialDisplayDensity; + + // If there is an override set for base values - use it, otherwise use new values. + updateBaseDisplayMetrics(isDisplaySizeForced ? mBaseDisplayWidth : newWidth, + isDisplaySizeForced ? mBaseDisplayHeight : newHeight, + isDisplayDensityForced ? mBaseDisplayDensity : newDensity); + + // Real display metrics changed, so we should also update initial values. + mInitialDisplayWidth = newWidth; + mInitialDisplayHeight = newHeight; + mInitialDisplayDensity = newDensity; + mService.reconfigureDisplayLocked(this); + } + } + /** Sets the maximum width the screen resolution can be */ void setMaxUiWidth(int width) { if (DEBUG_DISPLAY) { @@ -2867,7 +2920,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (stack != null) { stack.getBounds(frame); } - } else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) { + + // We want to screenshot with the exact bounds of the surface of the app. Thus, + // intersect it with the frame. + frame.intersect(w.mFrame); + }else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) { final Rect wf = w.mFrame; final Rect cr = w.mContentInsets; int left = wf.left + cr.left; diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java index e73d4d2559fb..35f35db5ada3 100644 --- a/services/core/java/com/android/server/wm/SnapshotStartingData.java +++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import android.app.ActivityManager.TaskSnapshot; import android.graphics.GraphicBuffer; import android.view.WindowManagerPolicy.StartingSurface; @@ -25,9 +26,9 @@ import android.view.WindowManagerPolicy.StartingSurface; class SnapshotStartingData extends StartingData { private final WindowManagerService mService; - private final GraphicBuffer mSnapshot; + private final TaskSnapshot mSnapshot; - SnapshotStartingData(WindowManagerService service, GraphicBuffer snapshot) { + SnapshotStartingData(WindowManagerService service, TaskSnapshot snapshot) { super(service); mService = service; mSnapshot = snapshot; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 3ffb093ba386..b816d8199aa6 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -17,15 +17,14 @@ package com.android.server.wm; import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; -import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; +import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static com.android.server.EventLogTags.WM_TASK_REMOVED; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -40,7 +39,6 @@ import android.view.DisplayInfo; import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.EventLogTags; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index b8d0b8c096fe..48b01f40fc65 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -28,6 +28,7 @@ import android.app.ActivityManager.StackId; import android.app.ActivityManager.TaskSnapshot; import android.graphics.Canvas; import android.graphics.GraphicBuffer; +import android.graphics.Rect; import android.os.Environment; import android.util.ArraySet; import android.view.WindowManagerPolicy.StartingSurface; @@ -152,7 +153,7 @@ class TaskSnapshotController { * MANAGER LOCK WHEN CALLING THIS METHOD! */ StartingSurface createStartingSurface(AppWindowToken token, - GraphicBuffer snapshot) { + TaskSnapshot snapshot) { return TaskSnapshotSurface.create(mService, token, snapshot); } @@ -166,8 +167,17 @@ class TaskSnapshotController { if (buffer == null) { return null; } + final WindowState mainWindow = top.findMainWindow(); return new TaskSnapshot(buffer, top.getConfiguration().orientation, - top.findMainWindow().mStableInsets, false /* reduced */, 1f /* scale */); + minRect(mainWindow.mContentInsets, mainWindow.mStableInsets), false /* reduced */, + 1f /* scale */); + } + + private Rect minRect(Rect rect1, Rect rect2) { + return new Rect(Math.min(rect1.left, rect2.left), + Math.min(rect1.top, rect2.top), + Math.min(rect1.right, rect2.right), + Math.min(rect1.bottom, rect2.bottom)); } /** diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 04403e2712c1..1591e480af4f 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -16,20 +16,35 @@ package com.android.server.wm; -import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.graphics.Color.WHITE; +import static android.graphics.Color.alpha; +import static android.view.SurfaceControl.HIDDEN; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; +import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; +import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; +import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.FLAG_SCALED; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES; +import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; +import static com.android.internal.policy.DecorView.getColorViewLeftInset; +import static com.android.internal.policy.DecorView.getColorViewTopInset; +import static com.android.internal.policy.DecorView.getNavigationBarRect; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.app.ActivityManager.TaskDescription; -import android.graphics.Bitmap; +import android.app.ActivityManager.TaskSnapshot; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.GraphicBuffer; import android.graphics.Paint; import android.graphics.Rect; @@ -37,17 +52,22 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.SystemClock; import android.util.MergedConfiguration; import android.util.Slog; import android.view.IWindowSession; import android.view.Surface; +import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy.StartingSurface; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.policy.DecorView; import com.android.internal.view.BaseIWindow; /** @@ -57,19 +77,57 @@ import com.android.internal.view.BaseIWindow; */ class TaskSnapshotSurface implements StartingSurface { + private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450; + + /** + * When creating the starting window, we use the exact same layout flags such that we end up + * with a window with the exact same dimensions etc. However, these flags are not used in layout + * and might cause other side effects so we exclude them. + */ + private static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE + | FLAG_NOT_TOUCHABLE + | FLAG_NOT_TOUCH_MODAL + | FLAG_ALT_FOCUSABLE_IM + | FLAG_NOT_FOCUSABLE + | FLAG_HARDWARE_ACCELERATED + | FLAG_IGNORE_CHEEK_PRESSES + | FLAG_LOCAL_FOCUS_MODE + | FLAG_SLIPPERY + | FLAG_WATCH_OUTSIDE_TOUCH + | FLAG_SPLIT_TOUCH + | FLAG_SCALED + | FLAG_SECURE; + private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM; private static final int MSG_REPORT_DRAW = 0; private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; private final Window mWindow; private final Surface mSurface; + private SurfaceControl mChildSurfaceControl; private final IWindowSession mSession; private final WindowManagerService mService; + private final Rect mTaskBounds; + private final Rect mStableInsets = new Rect(); + private final Rect mContentInsets = new Rect(); + private final Rect mFrame = new Rect(); + private final TaskSnapshot mSnapshot; + private final CharSequence mTitle; private boolean mHasDrawn; private boolean mReportNextDraw; - private Paint mFillBackgroundPaint = new Paint(); + private long mShownTime; + private final Handler mHandler; + private final boolean mSizeMismatch; + private final Paint mBackgroundPaint = new Paint(); + private final Paint mStatusBarPaint = new Paint(); + private final Paint mNavigationBarPaint = new Paint(); + private final int mStatusBarColor; + private final int mNavigationBarColor; + private final int mSysUiVis; + private final int mWindowFlags; + private final int mWindowPrivateFlags; static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token, - GraphicBuffer snapshot) { + TaskSnapshot snapshot) { final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); final Window window = new Window(); @@ -78,32 +136,51 @@ class TaskSnapshotSurface implements StartingSurface { final Surface surface = new Surface(); final Rect tmpRect = new Rect(); final Rect tmpFrame = new Rect(); + final Rect taskBounds; + final Rect tmpContentInsets = new Rect(); + final Rect tmpStableInsets = new Rect(); final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); - int fillBackgroundColor = Color.WHITE; + int backgroundColor = WHITE; + int statusBarColor = 0; + int navigationBarColor = 0; + final int sysUiVis; + final int windowFlags; + final int windowPrivateFlags; synchronized (service.mWindowMap) { + final WindowState mainWindow = token.findMainWindow(); + if (mainWindow == null) { + Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token=" + + token); + return null; + } + sysUiVis = mainWindow.getSystemUiVisibility(); + windowFlags = mainWindow.getAttrs().flags; + windowPrivateFlags = mainWindow.getAttrs().privateFlags; + layoutParams.type = TYPE_APPLICATION_STARTING; - layoutParams.format = snapshot.getFormat(); - layoutParams.flags = FLAG_LAYOUT_INSET_DECOR - | FLAG_LAYOUT_IN_SCREEN + layoutParams.format = snapshot.getSnapshot().getFormat(); + layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES) | FLAG_NOT_FOCUSABLE - | FLAG_NOT_TOUCHABLE - | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + | FLAG_NOT_TOUCHABLE; layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT; layoutParams.token = token.token; layoutParams.width = LayoutParams.MATCH_PARENT; layoutParams.height = LayoutParams.MATCH_PARENT; - - // TODO: Inherit behavior whether to draw behind status bar/nav bar. - layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + layoutParams.systemUiVisibility = sysUiVis; final Task task = token.getTask(); if (task != null) { - layoutParams.setTitle(String.format(TITLE_FORMAT,task.mTaskId)); + layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId)); final TaskDescription taskDescription = task.getTaskDescription(); if (taskDescription != null) { - fillBackgroundColor = taskDescription.getBackgroundColor(); + backgroundColor = taskDescription.getBackgroundColor(); + statusBarColor = taskDescription.getStatusBarColor(); + navigationBarColor = taskDescription.getNavigationBarColor(); } + taskBounds = new Rect(); + task.getBounds(taskBounds); + } else { + taskBounds = null; } } try { @@ -118,31 +195,57 @@ class TaskSnapshotSurface implements StartingSurface { // Local call. } final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, - surface, fillBackgroundColor); + surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor, + navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds); window.setOuter(snapshotSurface); try { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame, - tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpMergedConfiguration, - surface); + tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect, + tmpMergedConfiguration, surface); } catch (RemoteException e) { // Local call. } - snapshotSurface.drawSnapshot(snapshot); + snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets); + snapshotSurface.drawSnapshot(); return snapshotSurface; } @VisibleForTesting TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface, - int fillBackgroundColor) { + TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor, + int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags, + Rect taskBounds) { mService = service; + mHandler = new Handler(mService.mH.getLooper()); mSession = WindowManagerGlobal.getWindowSession(); mWindow = window; mSurface = surface; - mFillBackgroundPaint.setColor(fillBackgroundColor); + mSnapshot = snapshot; + mTitle = title; + mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); + mTaskBounds = taskBounds; + mSysUiVis = sysUiVis; + mWindowFlags = windowFlags; + mWindowPrivateFlags = windowPrivateFlags; + mSizeMismatch = (mFrame.width() != snapshot.getSnapshot().getWidth() + || mFrame.height() != snapshot.getSnapshot().getHeight()); + mStatusBarColor = DecorView.calculateStatusBarColor(windowFlags, + service.mContext.getColor(R.color.system_bar_background_semi_transparent), + statusBarColor); + mNavigationBarColor = navigationBarColor; + mStatusBarPaint.setColor(mStatusBarColor); + mNavigationBarPaint.setColor(navigationBarColor); } @Override public void remove() { + synchronized (mService.mWindowMap) { + final long now = SystemClock.uptimeMillis(); + if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) { + mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS); + return; + } + } try { mSession.remove(mWindow); } catch (RemoteException e) { @@ -150,31 +253,149 @@ class TaskSnapshotSurface implements StartingSurface { } } - private void drawSnapshot(GraphicBuffer snapshot) { - mSurface.attachAndQueueBuffer(snapshot); + @VisibleForTesting + void setFrames(Rect frame, Rect contentInsets, Rect stableInsets) { + mFrame.set(frame); + mContentInsets.set(contentInsets); + mStableInsets.set(stableInsets); + } + + private void drawSnapshot() { + final GraphicBuffer buffer = mSnapshot.getSnapshot(); + if (mSizeMismatch) { + // The dimensions of the buffer and the window don't match, so attaching the buffer + // will fail. Better create a child window with the exact dimensions and fill the parent + // window with the background color! + drawSizeMismatchSnapshot(buffer); + } else { + drawSizeMatchSnapshot(buffer); + } final boolean reportNextDraw; synchronized (mService.mWindowMap) { + mShownTime = SystemClock.uptimeMillis(); mHasDrawn = true; reportNextDraw = mReportNextDraw; } if (reportNextDraw) { reportDrawn(); } + } + + private void drawSizeMatchSnapshot(GraphicBuffer buffer) { + mSurface.attachAndQueueBuffer(buffer); + mSurface.release(); + } + + private void drawSizeMismatchSnapshot(GraphicBuffer buffer) { + final SurfaceSession session = new SurfaceSession(mSurface); + + // Keep a reference to it such that it doesn't get destroyed when finalized. + mChildSurfaceControl = new SurfaceControl(session, + mTitle + " - task-snapshot-surface", + buffer.getWidth(), buffer.getHeight(), buffer.getFormat(), HIDDEN); + Surface surface = new Surface(); + surface.copyFrom(mChildSurfaceControl); + + // Clip off ugly navigation bar. + final Rect crop = calculateSnapshotCrop(); + final Rect frame = calculateSnapshotFrame(crop); + SurfaceControl.openTransaction(); + try { + // We can just show the surface here as it will still be hidden as the parent is + // still hidden. + mChildSurfaceControl.show(); + mChildSurfaceControl.setWindowCrop(crop); + mChildSurfaceControl.setPosition(frame.left, frame.top); + } finally { + SurfaceControl.closeTransaction(); + } + surface.attachAndQueueBuffer(buffer); + surface.release(); + + final Canvas c = mSurface.lockCanvas(null); + drawBackgroundAndBars(c, frame); + mSurface.unlockCanvasAndPost(c); mSurface.release(); } @VisibleForTesting - void fillEmptyBackground(Canvas c, Bitmap b) { - final boolean fillHorizontally = c.getWidth() > b.getWidth(); - final boolean fillVertically = c.getHeight() > b.getHeight(); + Rect calculateSnapshotCrop() { + final Rect rect = new Rect(); + rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight()); + final Rect insets = mSnapshot.getContentInsets(); + + // Let's remove all system decorations except the status bar, but only if the task is at the + // very top of the screen. + rect.inset(insets.left, mTaskBounds.top != 0 ? insets.top : 0, insets.right, insets.bottom); + return rect; + } + + @VisibleForTesting + Rect calculateSnapshotFrame(Rect crop) { + final Rect frame = new Rect(crop); + + // By default, offset it to to top/left corner + frame.offsetTo(-crop.left, -crop.top); + + // However, we also need to make space for the navigation bar on the left side. + final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left, + mContentInsets.left); + frame.offset(colorViewLeftInset, 0); + return frame; + } + + @VisibleForTesting + void drawBackgroundAndBars(Canvas c, Rect frame) { + final int statusBarHeight = getStatusBarColorViewHeight(); + final boolean fillHorizontally = c.getWidth() > frame.right; + final boolean fillVertically = c.getHeight() > frame.bottom; if (fillHorizontally) { - c.drawRect(b.getWidth(), 0, c.getWidth(), fillVertically - ? b.getHeight() - : c.getHeight(), - mFillBackgroundPaint); + c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0, + c.getWidth(), fillVertically + ? frame.bottom + : c.getHeight(), + mBackgroundPaint); } if (fillVertically) { - c.drawRect(0, b.getHeight(), c.getWidth(), c.getHeight(), mFillBackgroundPaint); + c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint); + } + drawStatusBarBackground(c, frame, statusBarHeight); + drawNavigationBarBackground(c); + } + + private int getStatusBarColorViewHeight() { + final boolean forceStatusBarBackground = + (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; + if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( + mSysUiVis, mStatusBarColor, mWindowFlags, forceStatusBarBackground)) { + return getColorViewTopInset(mStableInsets.top, mContentInsets.top); + } else { + return 0; + } + } + + private boolean isNavigationBarColorViewVisible() { + return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( + mSysUiVis, mNavigationBarColor, mWindowFlags, false /* force */); + } + + @VisibleForTesting + void drawStatusBarBackground(Canvas c, Rect frame, int statusBarHeight) { + if (statusBarHeight > 0 && c.getWidth() > frame.right) { + final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right, + mContentInsets.right); + c.drawRect(frame.right, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint); + } + } + + @VisibleForTesting + void drawNavigationBarBackground(Canvas c) { + final Rect navigationBarRect = new Rect(); + getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets, + navigationBarRect); + final boolean visible = isNavigationBarColorViewVisible(); + if (visible && !navigationBarRect.isEmpty()) { + c.drawRect(navigationBarRect, mNavigationBarPaint); } } @@ -211,10 +432,10 @@ class TaskSnapshotSurface implements StartingSurface { } }; - private static class Window extends BaseIWindow { + @VisibleForTesting + static class Window extends BaseIWindow { private TaskSnapshotSurface mOuter; - public void setOuter(TaskSnapshotSurface outer) { mOuter = outer; } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 57fb81ce0b2d..57eaa2b2ad8d 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -17,17 +17,15 @@ package com.android.server.wm; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION; -import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE; import android.content.Context; +import android.os.Handler; import android.os.Trace; import android.util.Slog; import android.util.SparseArray; @@ -36,6 +34,9 @@ import android.view.Choreographer; import android.view.SurfaceControl; import android.view.WindowManagerPolicy; +import com.android.internal.view.SurfaceFlingerVsyncChoreographer; +import com.android.server.DisplayThread; + import java.io.PrintWriter; /** @@ -82,20 +83,31 @@ public class WindowAnimator { // check if some got replaced and can be removed. private boolean mRemoveReplacedWindows = false; + private long mCurrentFrameTime; + private final Runnable mAnimationTick; + private final SurfaceFlingerVsyncChoreographer mSfChoreographer; + WindowAnimator(final WindowManagerService service) { mService = service; mContext = service.mContext; mPolicy = service.mPolicy; mWindowPlacerLocked = service.mWindowPlacerLocked; - - mAnimationFrameCallback = new Choreographer.FrameCallback() { - public void doFrame(long frameTimeNs) { - synchronized (mService.mWindowMap) { - mService.mAnimationScheduled = false; - animateLocked(frameTimeNs); - } + final Handler handler = DisplayThread.getHandler(); + + // TODO: Multi-display: If displays have different vsync tick, have a separate tick per + // display. + mSfChoreographer = new SurfaceFlingerVsyncChoreographer(handler, + mService.getDefaultDisplayContentLocked().getDisplay()); + mAnimationTick = () -> { + synchronized (mService.mWindowMap) { + mService.mAnimationScheduled = false; + animateLocked(mCurrentFrameTime); } }; + mAnimationFrameCallback = frameTimeNs -> { + mCurrentFrameTime = frameTimeNs; + mSfChoreographer.scheduleAtSfVsync(mAnimationTick); + }; } void addDisplayLocked(final int displayId) { diff --git a/services/core/java/com/android/server/wm/WindowContainerController.java b/services/core/java/com/android/server/wm/WindowContainerController.java index 84ffc35b4651..c4a68377bc36 100644 --- a/services/core/java/com/android/server/wm/WindowContainerController.java +++ b/services/core/java/com/android/server/wm/WindowContainerController.java @@ -33,7 +33,7 @@ class WindowContainerController<E extends WindowContainer, I extends WindowConta final WindowManagerService mService; final RootWindowContainer mRoot; - final HashMap<IBinder, WindowState> mWindowMap; + final WindowHashMap mWindowMap; // The window container this controller owns. E mContainer; diff --git a/services/core/java/com/android/server/wm/WindowHashMap.java b/services/core/java/com/android/server/wm/WindowHashMap.java new file mode 100644 index 000000000000..49bba4145996 --- /dev/null +++ b/services/core/java/com/android/server/wm/WindowHashMap.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import android.os.IBinder; + +import java.util.HashMap; + +/** + * Subclass of HashMap such that we can instruct the compiler to boost our thread priority when + * locking this class. See makefile. + */ +class WindowHashMap extends HashMap<IBinder, WindowState> { +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 95fbbb89a649..1be051270e16 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.Manifest.permission.MANAGE_APP_TOKENS; +import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; @@ -25,6 +26,11 @@ import static android.app.StatusBarManager.DISABLE_MASK; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_USER_HANDLE; +import static android.os.Process.ROOT_UID; +import static android.os.Process.SHELL_UID; +import static android.os.Process.SYSTEM_UID; +import static android.os.Process.THREAD_PRIORITY_DISPLAY; +import static android.os.Process.myPid; import static android.os.UserHandle.USER_NULL; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.DOCKED_INVALID; @@ -60,6 +66,8 @@ import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.LockGuard.INDEX_WINDOW; +import static com.android.server.LockGuard.installLock; import static com.android.server.wm.AppTransition.TRANSIT_UNSET; import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END; import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START; @@ -102,6 +110,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityManagerInternal; +import android.app.ActivityThread; import android.app.AppOpsManager; import android.app.IActivityManager; import android.content.BroadcastReceiver; @@ -124,7 +133,6 @@ import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.input.InputManager; import android.net.Uri; -import android.os.PowerSaveState; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -138,7 +146,7 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.PowerManagerInternal; -import android.os.Process; +import android.os.PowerSaveState; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; @@ -150,10 +158,10 @@ import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; import android.util.ArraySet; -import android.util.MergedConfiguration; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; +import android.util.MergedConfiguration; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -214,7 +222,7 @@ import com.android.server.DisplayThread; import com.android.server.EventLogTags; import com.android.server.FgThread; import com.android.server.LocalServices; -import com.android.server.LockGuard; +import com.android.server.ThreadPriorityBooster; import com.android.server.UiThread; import com.android.server.Watchdog; import com.android.server.input.InputManagerService; @@ -238,10 +246,7 @@ import java.net.Socket; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; - -import static android.Manifest.permission.READ_FRAME_BUFFER; /** {@hide} */ public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { @@ -407,7 +412,7 @@ public class WindowManagerService extends IWindowManager.Stub * This is also used as the lock for all of our state. * NOTE: Never call into methods that lock ActivityManagerService while holding this object. */ - final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>(); + final WindowHashMap mWindowMap = new WindowHashMap(); /** * List of window tokens that have finished starting their application, @@ -847,6 +852,16 @@ public class WindowManagerService extends IWindowManager.Stub // since they won't be notified through the app window animator. final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>(); + private static ThreadPriorityBooster sThreadPriorityBooster = new ThreadPriorityBooster( + THREAD_PRIORITY_DISPLAY, INDEX_WINDOW); + + static void boostPriorityForLockedSection() { + sThreadPriorityBooster.boost(); + } + + static void resetPriorityAfterLockedSection() { + sThreadPriorityBooster.reset(); + } void openSurfaceTransaction() { synchronized (mWindowMap) { @@ -935,7 +950,7 @@ public class WindowManagerService extends IWindowManager.Stub private WindowManagerService(Context context, InputManagerService inputManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy) { - LockGuard.installLock(this, LockGuard.INDEX_WINDOW); + installLock(this, INDEX_WINDOW); mRoot = new RootWindowContainer(this); mContext = context; mHaveInputMethods = haveInputMethods; @@ -1580,7 +1595,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void enableSurfaceTrace(ParcelFileDescriptor pfd) { final int callingUid = Binder.getCallingUid(); - if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) { + if (callingUid != SHELL_UID && callingUid != ROOT_UID) { throw new SecurityException("Only shell can call enableSurfaceTrace"); } @@ -1592,8 +1607,8 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void disableSurfaceTrace() { final int callingUid = Binder.getCallingUid(); - if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID && - callingUid != Process.SYSTEM_UID) { + if (callingUid != SHELL_UID && callingUid != ROOT_UID && + callingUid != SYSTEM_UID) { throw new SecurityException("Only shell can call disableSurfaceTrace"); } synchronized (mWindowMap) { @@ -1607,7 +1622,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setScreenCaptureDisabled(int userId, boolean disabled) { int callingUid = Binder.getCallingUid(); - if (callingUid != Process.SYSTEM_UID) { + if (callingUid != SYSTEM_UID) { throw new SecurityException("Only system can call setScreenCaptureDisabled."); } @@ -2263,7 +2278,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean checkCallingPermission(String permission, String func) { // Quick check: if the calling permission is me, it's all okay. - if (Binder.getCallingPid() == Process.myPid()) { + if (Binder.getCallingPid() == myPid()) { return true; } @@ -2916,7 +2931,7 @@ public class WindowManagerService extends IWindowManager.Stub } // If this isn't coming from the system then don't allow disabling the lockscreen // to bypass security. - if (Binder.getCallingUid() != Process.SYSTEM_UID && isKeyguardSecure()) { + if (Binder.getCallingUid() != SYSTEM_UID && isKeyguardSecure()) { Log.d(TAG_WM, "current mode is SecurityMode, ignore disableKeyguard"); return; } @@ -3196,19 +3211,25 @@ public class WindowManagerService extends IWindowManager.Stub // Called by window manager policy. Not exposed externally. @Override public void shutdown(boolean confirm) { - ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm); + // Pass in the UI context, since ShutdownThread requires it (to show UI). + ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(), + PowerManager.SHUTDOWN_USER_REQUESTED, confirm); } // Called by window manager policy. Not exposed externally. @Override public void reboot(boolean confirm) { - ShutdownThread.reboot(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm); + // Pass in the UI context, since ShutdownThread requires it (to show UI). + ShutdownThread.reboot(ActivityThread.currentActivityThread().getSystemUiContext(), + PowerManager.SHUTDOWN_USER_REQUESTED, confirm); } // Called by window manager policy. Not exposed externally. @Override public void rebootSafeMode(boolean confirm) { - ShutdownThread.rebootSafeMode(mContext, confirm); + // Pass in the UI context, since ShutdownThread requires it (to show UI). + ShutdownThread.rebootSafeMode(ActivityThread.currentActivityThread().getSystemUiContext(), + confirm); } public void setCurrentProfileIds(final int[] currentProfileIds) { @@ -5312,8 +5333,8 @@ public class WindowManagerService extends IWindowManager.Stub if (displayContent.mBaseDisplayWidth != width || displayContent.mBaseDisplayHeight != height) { Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height); - displayContent.mBaseDisplayWidth = width; - displayContent.mBaseDisplayHeight = height; + displayContent.updateBaseDisplayMetrics(width, height, + displayContent.mBaseDisplayDensity); } } catch (NumberFormatException ex) { } @@ -5338,8 +5359,7 @@ public class WindowManagerService extends IWindowManager.Stub // displayContent must not be null private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) { Slog.i(TAG_WM, "Using new display size: " + width + "x" + height); - displayContent.mBaseDisplayWidth = width; - displayContent.mBaseDisplayHeight = height; + displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity); reconfigureDisplayLocked(displayContent); } @@ -7052,7 +7072,7 @@ public class WindowManagerService extends IWindowManager.Stub throw new IllegalStateException("Magnification callbacks not set!"); } } - if (Binder.getCallingPid() != android.os.Process.myPid()) { + if (Binder.getCallingPid() != myPid()) { spec.recycle(); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 6fd95a4d4a4f..8098eeaa3c11 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2395,6 +2395,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (requestAnim) { mService.scheduleAnimationLocked(); } + if ((mAttrs.flags & FLAG_NOT_FOCUSABLE) == 0) { + mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */); + } return true; } @@ -2437,6 +2440,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (requestAnim) { mService.scheduleAnimationLocked(); } + if (mService.mCurrentFocus == this) { + mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */); + } return true; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index da49eb37ebec..a5eac46281f3 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -475,6 +475,9 @@ public final class SystemServer { ActivityThread activityThread = ActivityThread.systemMain(); mSystemContext = activityThread.getSystemContext(); mSystemContext.setTheme(DEFAULT_SYSTEM_THEME); + + final Context systemUiContext = activityThread.getSystemUiContext(); + systemUiContext.setTheme(DEFAULT_SYSTEM_THEME); } /** @@ -1014,12 +1017,6 @@ public final class SystemServer { traceEnd(); } - if (!disableNonCoreServices) { - traceBeginAndSlog("StartFontServiceManager"); - mSystemServiceManager.startService(FontManagerService.Lifecycle.class); - traceEnd(); - } - if (!disableNonCoreServices && !disableTextServices) { traceBeginAndSlog("StartTextServicesManager"); mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class); diff --git a/services/profile-classes b/services/profile-classes index 1b304e14d8fd..b0d2da7aeba6 100644 --- a/services/profile-classes +++ b/services/profile-classes @@ -2481,7 +2481,6 @@ Landroid/text/FontConfig$Family; Landroid/text/FontConfig$Family$1; Landroid/text/FontConfig$Font; Landroid/text/FontConfig$Font$1; -Landroid/text/FontManager; Landroid/text/format/DateFormat; Landroid/text/format/Time; Landroid/text/format/Time$TimeCalculator; @@ -3132,8 +3131,6 @@ Lcom/android/internal/content/PackageHelper; Lcom/android/internal/content/PackageMonitor; Lcom/android/internal/content/ReferrerIntent; Lcom/android/internal/content/ReferrerIntent$1; -Lcom/android/internal/font/IFontManager; -Lcom/android/internal/font/IFontManager$Stub; Lcom/android/internal/graphics/drawable/AnimationScaleListDrawable; Lcom/android/internal/graphics/drawable/AnimationScaleListDrawable$AnimationScaleListState; Lcom/android/internal/hardware/AmbientDisplayConfiguration; @@ -4377,8 +4374,6 @@ Lcom/android/server/firewall/StringFilter$7; Lcom/android/server/firewall/StringFilter$8; Lcom/android/server/firewall/StringFilter$9; Lcom/android/server/firewall/StringFilter$ValueProvider; -Lcom/android/server/FontManagerService; -Lcom/android/server/FontManagerService$Lifecycle; Lcom/android/server/GestureLauncherService; Lcom/android/server/GestureLauncherService$1; Lcom/android/server/GestureLauncherService$2; diff --git a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java index 9197ccf972c4..0694eaefd288 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java @@ -22,6 +22,7 @@ import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; @@ -170,6 +171,7 @@ public class NetworkScorerAppManagerTest { mockScoreNetworksGranted(recoComponent.getPackageName()); mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, enableUseOpenWifiComponent.getPackageName()); + mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent); final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer(); assertNotNull(activeScorer); @@ -350,6 +352,173 @@ public class NetworkScorerAppManagerTest { eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), any()); } + @Test + public void testMigrateNetworkScorerAppSettingIfNeeded_networkScorerAppIsNull() + throws Exception { + when(mSettingsFacade.getString(mMockContext, + Settings.Global.NETWORK_SCORER_APP)).thenReturn(null); + + mNetworkScorerAppManager.migrateNetworkScorerAppSettingIfNeeded(); + + verify(mSettingsFacade, never()).putString(eq(mMockContext), + eq(Settings.Global.USE_OPEN_WIFI_PACKAGE), anyString()); + } + + @Test + public void testMigrateNetworkScorerAppSettingIfNeeded_networkScorerAppIsEmpty() + throws Exception { + when(mSettingsFacade.getString(mMockContext, + Settings.Global.NETWORK_SCORER_APP)).thenReturn(""); + + mNetworkScorerAppManager.migrateNetworkScorerAppSettingIfNeeded(); + + verify(mSettingsFacade, never()).putString(eq(mMockContext), + eq(Settings.Global.USE_OPEN_WIFI_PACKAGE), anyString()); + } + + @Test + public void testMigrateNetworkScorerAppSettingIfNeeded_networkScorerIsNotActive() + throws Exception { + when(mSettingsFacade.getString(mMockContext, + Settings.Global.NETWORK_SCORER_APP)).thenReturn("com.foo.package"); + // Make getActiveScorer() return null. + setRecommendationsEnabledSetting(NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF); + + mNetworkScorerAppManager.migrateNetworkScorerAppSettingIfNeeded(); + + verify(mSettingsFacade, never()).putString(eq(mMockContext), + eq(Settings.Global.USE_OPEN_WIFI_PACKAGE), anyString()); + } + + @Test + public void testMigrateNetworkScorerAppSettingIfNeeded_useOpenWifiSettingIsNotEmpty() + throws Exception { + final ComponentName recoComponent = new ComponentName("package1", "class1"); + final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2"); + setNetworkRecoPackageSetting(recoComponent.getPackageName()); + mockScoreNetworksGranted(recoComponent.getPackageName()); + mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, + enableUseOpenWifiComponent.getPackageName()); + mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent); + when(mSettingsFacade.getString(mMockContext, + Settings.Global.NETWORK_SCORER_APP)) + .thenReturn(enableUseOpenWifiComponent.getPackageName()); + // The setting has a value so the migration shouldn't touch it. + when(mSettingsFacade.getString(mMockContext, + Settings.Global.USE_OPEN_WIFI_PACKAGE)) + .thenReturn(enableUseOpenWifiComponent.getPackageName()); + + mNetworkScorerAppManager.migrateNetworkScorerAppSettingIfNeeded(); + + verify(mSettingsFacade, never()).putString(eq(mMockContext), + eq(Settings.Global.USE_OPEN_WIFI_PACKAGE), anyString()); + verify(mSettingsFacade).putString(eq(mMockContext), + eq(Settings.Global.NETWORK_SCORER_APP), eq(null)); + } + + @Test + public void testMigrateNetworkScorerAppSettingIfNeeded_useOpenWifiActivityNotAvail() + throws Exception { + final ComponentName recoComponent = new ComponentName("package1", "class1"); + final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2"); + setNetworkRecoPackageSetting(recoComponent.getPackageName()); + mockScoreNetworksGranted(recoComponent.getPackageName()); + // The active component doesn't have an open wifi activity so the migration shouldn't + // set USE_OPEN_WIFI_PACKAGE. + mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, + null /*useOpenWifiActivityPackage*/); + when(mSettingsFacade.getString(mMockContext, + Settings.Global.NETWORK_SCORER_APP)) + .thenReturn(enableUseOpenWifiComponent.getPackageName()); + when(mSettingsFacade.getString(mMockContext, + Settings.Global.USE_OPEN_WIFI_PACKAGE)).thenReturn(null); + + mNetworkScorerAppManager.migrateNetworkScorerAppSettingIfNeeded(); + + verify(mSettingsFacade, never()).putString(eq(mMockContext), + eq(Settings.Global.USE_OPEN_WIFI_PACKAGE), anyString()); + verify(mSettingsFacade).putString(eq(mMockContext), + eq(Settings.Global.NETWORK_SCORER_APP), eq(null)); + } + + @Test + public void testMigrateNetworkScorerAppSettingIfNeeded_packageMismatch_activity() + throws Exception { + final ComponentName recoComponent = new ComponentName("package1", "class1"); + final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2"); + setNetworkRecoPackageSetting(recoComponent.getPackageName()); + mockScoreNetworksGranted(recoComponent.getPackageName()); + mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, + enableUseOpenWifiComponent.getPackageName()); + mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent); + // The older network scorer app setting doesn't match the new use open wifi activity package + // so the migration shouldn't set USE_OPEN_WIFI_PACKAGE. + when(mSettingsFacade.getString(mMockContext, + Settings.Global.NETWORK_SCORER_APP)) + .thenReturn(enableUseOpenWifiComponent.getPackageName() + ".diff"); + when(mSettingsFacade.getString(mMockContext, + Settings.Global.USE_OPEN_WIFI_PACKAGE)).thenReturn(null); + + mNetworkScorerAppManager.migrateNetworkScorerAppSettingIfNeeded(); + + verify(mSettingsFacade, never()).putString(eq(mMockContext), + eq(Settings.Global.USE_OPEN_WIFI_PACKAGE), anyString()); + verify(mSettingsFacade).putString(eq(mMockContext), + eq(Settings.Global.NETWORK_SCORER_APP), eq(null)); + } + + @Test + public void testMigrateNetworkScorerAppSettingIfNeeded_packageMismatch_service() + throws Exception { + final ComponentName recoComponent = new ComponentName("package1", "class1"); + final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2"); + setNetworkRecoPackageSetting(recoComponent.getPackageName()); + mockScoreNetworksGranted(recoComponent.getPackageName()); + mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, + enableUseOpenWifiComponent.getPackageName()); + mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent); + // The older network scorer app setting doesn't match the active package so the migration + // shouldn't set USE_OPEN_WIFI_PACKAGE. + when(mSettingsFacade.getString(mMockContext, + Settings.Global.NETWORK_SCORER_APP)) + .thenReturn(recoComponent.getPackageName() + ".diff"); + when(mSettingsFacade.getString(mMockContext, + Settings.Global.USE_OPEN_WIFI_PACKAGE)).thenReturn(null); + + mNetworkScorerAppManager.migrateNetworkScorerAppSettingIfNeeded(); + + verify(mSettingsFacade, never()).putString(eq(mMockContext), + eq(Settings.Global.USE_OPEN_WIFI_PACKAGE), anyString()); + verify(mSettingsFacade).putString(eq(mMockContext), + eq(Settings.Global.NETWORK_SCORER_APP), eq(null)); + } + + @Test + public void testMigrateNetworkScorerAppSettingIfNeeded_packageMatch_activity() + throws Exception { + final ComponentName recoComponent = new ComponentName("package1", "class1"); + final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2"); + setNetworkRecoPackageSetting(recoComponent.getPackageName()); + mockScoreNetworksGranted(recoComponent.getPackageName()); + mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, + enableUseOpenWifiComponent.getPackageName()); + mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent); + // Old setting matches the new activity package, migration should happen. + when(mSettingsFacade.getString(mMockContext, + Settings.Global.NETWORK_SCORER_APP)) + .thenReturn(enableUseOpenWifiComponent.getPackageName()); + when(mSettingsFacade.getString(mMockContext, + Settings.Global.USE_OPEN_WIFI_PACKAGE)).thenReturn(null); + + mNetworkScorerAppManager.migrateNetworkScorerAppSettingIfNeeded(); + + verify(mSettingsFacade).putString(eq(mMockContext), + eq(Settings.Global.USE_OPEN_WIFI_PACKAGE), + eq(enableUseOpenWifiComponent.getPackageName())); + verify(mSettingsFacade).putString(eq(mMockContext), + eq(Settings.Global.NETWORK_SCORER_APP), eq(null)); + } + private void setRecommendationsEnabledSetting(int value) { when(mSettingsFacade.getInt(eq(mMockContext), eq(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED), anyInt())).thenReturn(value); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java new file mode 100644 index 000000000000..54ecab3af542 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.MediumTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.runner.RunWith; +import org.junit.Test; + +/** + * Tests for the {@link ActivityRecord} class. + * + * Build/Install/Run: + * bit FrameworksServicesTests:com.android.server.am.ActivityRecordTests + */ +@MediumTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class ActivityRecordTests extends ActivityTestsBase { + private final ComponentName testActivityComponent = + ComponentName.unflattenFromString("com.foo/.BarActivity"); + @Test + public void testStackCleanupOnClearingTask() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + final TestActivityStack testStack = new ActivityStackBuilder(service).build(); + final TaskRecord task = createTask(service, testActivityComponent, testStack); + final ActivityRecord record = createActivity(service, testActivityComponent, task); + + record.setTask(null); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + } + + @Test + public void testStackCleanupOnActivityRemoval() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + final TestActivityStack testStack = new ActivityStackBuilder(service).build(); + final TaskRecord task = createTask(service, testActivityComponent, testStack); + final ActivityRecord record = createActivity(service, testActivityComponent, task); + + task.removeActivity(record); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + } + + @Test + public void testStackCleanupOnTaskRemoval() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + final TestActivityStack testStack = new ActivityStackBuilder(service).build(); + final TaskRecord task = createTask(service, testActivityComponent, testStack); + final ActivityRecord record = createActivity(service, testActivityComponent, task); + + testStack.removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + } + + @Test + public void testNoCleanupMovingActivityInSameStack() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + final TestActivityStack testStack = new ActivityStackBuilder(service).build(); + final TaskRecord oldTask = createTask(service, testActivityComponent, testStack); + final ActivityRecord record = createActivity(service, testActivityComponent, oldTask); + final TaskRecord newTask = createTask(service, testActivityComponent, testStack); + + record.reparent(newTask, 0, null /*reason*/); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 0); + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java new file mode 100644 index 000000000000..c5cc2ff22abd --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import static org.mockito.Mockito.mock; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.support.test.InstrumentationRegistry; +import com.android.server.AttributeCache; +import com.android.server.wm.AppWindowContainerController; +import com.android.server.wm.StackWindowController; + +import com.android.server.wm.WindowManagerService; +import com.android.server.wm.WindowTestUtils; +import org.junit.Before; +import org.mockito.MockitoAnnotations; + +/** + * A base class to handle common operations in activity related unit tests. + */ +public class ActivityTestsBase { + private final Context mContext = InstrumentationRegistry.getContext(); + private static boolean sLooperPrepared; + private Handler mHandler; + + // Grabbing an instance of {@link WindowManagerService} creates it if not present so this must + // be called at before any tests. + private final WindowManagerService mWms = WindowTestUtils.getWindowManagerService(mContext); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + if (!sLooperPrepared) { + sLooperPrepared = true; + Looper.prepare(); + } + } + + protected ActivityManagerService createActivityManagerService() { + return new TestActivityManagerService(mContext); + } + + protected static TestActivityStack createActivityStack(ActivityManagerService service, + int stackId, int displayId, boolean onTop) { + if (service.mStackSupervisor instanceof TestActivityStackSupervisor) { + final TestActivityStack stack = ((TestActivityStackSupervisor) service.mStackSupervisor) + .createTestStack(stackId, onTop); + return stack; + } + + return null; + } + + protected static ActivityRecord createActivity(ActivityManagerService service, + ComponentName component, TaskRecord task) { + Intent intent = new Intent(); + intent.setComponent(component); + final ActivityInfo aInfo = new ActivityInfo(); + aInfo.applicationInfo = new ApplicationInfo(); + aInfo.applicationInfo.packageName = component.getPackageName(); + AttributeCache.init(service.mContext); + final ActivityRecord activity = new ActivityRecord(service, null /* caller */, + 0 /* launchedFromPid */, 0, null, intent, null, + aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */, + 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */, + service.mStackSupervisor, null /* container */, null /* options */, + null /* sourceRecord */); + activity.mWindowContainerController = mock(AppWindowContainerController.class); + + if (task != null) { + task.addActivityToTop(activity); + } + + return activity; + } + + protected static TaskRecord createTask(ActivityManagerService service, + ComponentName component, ActivityStack stack) { + final ActivityInfo aInfo = new ActivityInfo(); + aInfo.applicationInfo = new ApplicationInfo(); + aInfo.applicationInfo.packageName = component.getPackageName(); + + Intent intent = new Intent(); + intent.setComponent(component); + + final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/, + null /*_taskDescription*/, null /*thumbnailInfo*/); + stack.addTask(task, true, "creating test task"); + task.setStack(stack); + task.createWindowContainer(true, true); + + return task; + } + + /** + * An {@link ActivityManagerService} subclass which provides a test + * {@link ActivityStackSupervisor}. + */ + protected static class TestActivityManagerService extends ActivityManagerService { + public TestActivityManagerService(Context context) { + super(context); + } + + @Override + protected ActivityStackSupervisor createStackSupervisor() { + return new TestActivityStackSupervisor(this, new Handler().getLooper()); + } + } + + /** + * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on + * setup not available in the test environment. Also specifies an injector for + */ + protected static class TestActivityStackSupervisor extends ActivityStackSupervisor { + public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) { + super(service, looper); + } + + // Invoked during {@link ActivityStack} creation. + @Override + void updateUIDsPresentOnDisplay() { + } + + public TestActivityStack createTestStack(int stackId, boolean onTop) { + final ActivityDisplay display = new ActivityDisplay(); + final TestActivityContainer container = + new TestActivityContainer(stackId, display, onTop); + return container.getStack(); + } + + private class TestActivityContainer extends ActivityContainer { + private TestActivityStack mStack; + TestActivityContainer(int stackId, ActivityDisplay activityDisplay, boolean onTop) { + super(stackId, activityDisplay, onTop); + } + + @Override + protected void createStack(int stackId, boolean onTop) { + mStack = new TestActivityStack(this, null /*recentTasks*/, onTop); + } + + public TestActivityStack getStack() { + return mStack; + } + } + } + + /** + * Override of {@link ActivityStack} that tracks test metrics, such as the number of times a + * method is called. Note that its functionality depends on the implementations of the + * construction arguments. + */ + protected static class TestActivityStack<T extends StackWindowController> + extends ActivityStack<T> { + private int mOnActivityRemovedFromStackCount = 0; + private T mContainerController; + TestActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer, + RecentTasks recentTasks, boolean onTop) { + super(activityContainer, recentTasks, onTop); + } + + @Override + void onActivityRemovedFromStack(ActivityRecord r) { + mOnActivityRemovedFromStackCount++; + super.onActivityRemovedFromStack(r); + } + + // Returns the number of times {@link #onActivityRemovedFromStack} has been called + public int onActivityRemovedFromStackInvocationCount() { + return mOnActivityRemovedFromStackCount; + } + + @Override + protected T createStackWindowController(int displayId, boolean onTop, + Rect outBounds) { + mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController(); + return mContainerController; + } + + @Override + T getWindowContainerController() { + return mContainerController; + } + } + + protected static class ActivityStackBuilder { + private boolean mOnTop = true; + private int mStackId = 0; + private int mDisplayId = 1; + + private final ActivityManagerService mService; + + public ActivityStackBuilder(ActivityManagerService ams) { + mService = ams; + } + + public ActivityStackBuilder setOnTop(boolean onTop) { + mOnTop = onTop; + return this; + } + + public ActivityStackBuilder setStackId(int id) { + mStackId = id; + return this; + } + + public ActivityStackBuilder setDisplayId(int id) { + mDisplayId = id; + return this; + } + + public TestActivityStack build() { + return createActivityStack(mService, mStackId, mDisplayId, mOnTop); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java index 2ccaefc4512e..25004de60676 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java @@ -44,7 +44,8 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer() throws Exception { - final TestAppWindowContainerController controller = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller = + createAppWindowController(); // Assert token was added to display. assertNotNull(sDisplayContent.getWindowToken(controller.mToken.asBinder())); @@ -61,7 +62,8 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testSetOrientation() throws Exception { - final TestAppWindowContainerController controller = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller = + createAppWindowController(); // Assert orientation is unspecified to start. assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation()); @@ -92,7 +94,8 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testCreateRemoveStartingWindow() throws Exception { - final TestAppWindowContainerController controller = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller = + createAppWindowController(); controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false); waitUntilHandlerIdle(); @@ -105,8 +108,10 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testTransferStartingWindow() throws Exception { - final TestAppWindowContainerController controller1 = createAppWindowController(); - final TestAppWindowContainerController controller2 = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller1 = + createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller2 = + createAppWindowController(); controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false); waitUntilHandlerIdle(); @@ -120,8 +125,10 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testTransferStartingWindowWhileCreating() throws Exception { - final TestAppWindowContainerController controller1 = createAppWindowController(); - final TestAppWindowContainerController controller2 = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller1 = + createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller2 = + createAppWindowController(); sPolicy.setRunnableWhenAddingSplashScreen(() -> { // Surprise, ...! Transfer window in the middle of the creation flow. @@ -140,16 +147,16 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { public void testReparent() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController1 = - new TestTaskWindowContainerController(stackController); - final TestAppWindowContainerController appWindowController1 = createAppWindowController( - taskController1); - final TestTaskWindowContainerController taskController2 = - new TestTaskWindowContainerController(stackController); - final TestAppWindowContainerController appWindowController2 = createAppWindowController( - taskController2); - final TestTaskWindowContainerController taskController3 = - new TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestTaskWindowContainerController taskController1 = + new WindowTestUtils.TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestAppWindowContainerController appWindowController1 = + createAppWindowController(taskController1); + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestAppWindowContainerController appWindowController2 = + createAppWindowController(taskController2); + final WindowTestUtils.TestTaskWindowContainerController taskController3 = + new WindowTestUtils.TestTaskWindowContainerController(stackController); try { appWindowController1.reparent(taskController1, 0); @@ -169,16 +176,18 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { // Reparent the app window and ensure that it is moved appWindowController1.reparent(taskController2, 0); assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent()); - assertEquals(0, ((TestAppWindowToken) appWindowController1.mContainer).positionInParent()); - assertEquals(1, ((TestAppWindowToken) appWindowController2.mContainer).positionInParent()); + assertEquals(0, ((WindowTestUtils.TestAppWindowToken) appWindowController1.mContainer) + .positionInParent()); + assertEquals(1, ((WindowTestUtils.TestAppWindowToken) appWindowController2.mContainer) + .positionInParent()); } - private TestAppWindowContainerController createAppWindowController() { - return createAppWindowController(new TestTaskWindowContainerController()); + private WindowTestUtils.TestAppWindowContainerController createAppWindowController() { + return createAppWindowController(new WindowTestUtils.TestTaskWindowContainerController()); } - private TestAppWindowContainerController createAppWindowController( - TestTaskWindowContainerController taskController) { - return new TestAppWindowContainerController(taskController); + private WindowTestUtils.TestAppWindowContainerController createAppWindowController( + WindowTestUtils.TestTaskWindowContainerController taskController) { + return new WindowTestUtils.TestAppWindowContainerController(taskController); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java index 2003b91bcfad..7a7ca3f4000a 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -51,7 +51,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testAddWindow_Order() throws Exception { - final TestAppWindowToken token = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken token = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); assertEquals(0, token.getWindowsCount()); @@ -78,7 +79,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testFindMainWindow() throws Exception { - final TestAppWindowToken token = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken token = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); assertNull(token.findMainWindow()); @@ -102,12 +104,13 @@ public class AppWindowTokenTests extends WindowTestsBase { // Create an app window with token on a display. final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken appWindowToken = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken appWindowToken = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task.addChild(appWindowToken, 0); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.setTitle("AppWindow"); - final TestWindowState appWindow = new TestWindowState(attrs, appWindowToken); + final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken); appWindowToken.addWindow(appWindow); // Set initial orientation and update. @@ -137,12 +140,13 @@ public class AppWindowTokenTests extends WindowTestsBase { final DisplayContent defaultDisplayContent = sWm.getDefaultDisplayContentLocked(); final TaskStack stack = createTaskStackOnDisplay(defaultDisplayContent); final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken appWindowToken = new TestAppWindowToken(defaultDisplayContent); + final WindowTestUtils.TestAppWindowToken appWindowToken = + new WindowTestUtils.TestAppWindowToken(defaultDisplayContent); task.addChild(appWindowToken, 0); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.setTitle("AppWindow"); - final TestWindowState appWindow = new TestWindowState(attrs, appWindowToken); + final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken); appWindowToken.addWindow(appWindow); // Set initial orientation and update. @@ -165,7 +169,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testGetOrientation() throws Exception { - final TestAppWindowToken token = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken token = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); token.setFillsParent(false); diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java index e3ccd6eef105..d7d365e72480 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -175,7 +175,7 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(dc, stack.getDisplayContent()); final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken token = new TestAppWindowToken(dc); + final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(dc); task.addChild(token, 0); assertEquals(dc, task.getDisplayContent()); assertEquals(dc, token.getDisplayContent()); diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java index b0eba0b99567..13098f64bfac 100644 --- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java @@ -16,12 +16,7 @@ package com.android.server.wm; -import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; - import android.graphics.Rect; -import android.hardware.display.DisplayManagerGlobal; -import android.view.Display; -import android.view.DisplayInfo; import org.junit.Test; import org.junit.runner.RunWith; @@ -48,8 +43,8 @@ public class StackWindowControllerTests extends WindowTestsBase { public void testRemoveContainer() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController); final TaskStack stack = stackController.mContainer; final Task task = taskController.mContainer; @@ -68,11 +63,11 @@ public class StackWindowControllerTests extends WindowTestsBase { public void testRemoveContainer_deferRemoval() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController); final TaskStack stack = stackController.mContainer; - final TestTask task = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer; // Stack removal is deferred if one of its child is animating. task.setLocalIsAnimating(true); @@ -96,9 +91,9 @@ public class StackWindowControllerTests extends WindowTestsBase { final StackWindowController stack1Controller = createStackControllerOnDisplay(sDisplayContent); final TaskStack stack1 = stack1Controller.mContainer; - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stack1Controller); - final TestTask task1 = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stack1Controller); + final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer; task1.mOnDisplayChangedCalled = false; // Create second display and put second stack on it. diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index aab75ee1699b..717ddf26eb2f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; +import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; @@ -24,15 +27,19 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; +import android.app.ActivityManager.TaskSnapshot; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.GraphicBuffer; +import android.graphics.PixelFormat; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.view.Surface; + +import com.android.server.wm.TaskSnapshotSurface.Window; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -48,59 +55,174 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { private TaskSnapshotSurface mSurface; - @Before - public void setUp() { - mSurface = new TaskSnapshotSurface(null, null, null, Color.WHITE); + private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis, + int windowFlags, Rect taskBounds) { + final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888, + GraphicBuffer.USAGE_SW_READ_NEVER | GraphicBuffer.USAGE_SW_WRITE_NEVER); + final TaskSnapshot snapshot = new TaskSnapshot(buffer, + ORIENTATION_PORTRAIT, contentInsets, false, 1.0f); + mSurface = new TaskSnapshotSurface(sWm, new Window(), new Surface(), snapshot, "Test", + Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds); + } + + private void setupSurface(int width, int height) { + setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, width, height)); } @Test public void fillEmptyBackground_fillHorizontally() throws Exception { + setupSurface(200, 100); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(200); when(mockCanvas.getHeight()).thenReturn(100); - final Bitmap b = Bitmap.createBitmap(100, 200, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200)); verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); } @Test public void fillEmptyBackground_fillVertically() throws Exception { + setupSurface(100, 200); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(200); - final Bitmap b = Bitmap.createBitmap(200, 100, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100)); verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any()); } @Test public void fillEmptyBackground_fillBoth() throws Exception { + setupSurface(200, 200); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(200); when(mockCanvas.getHeight()).thenReturn(200); - final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any()); } @Test public void fillEmptyBackground_dontFill_sameSize() throws Exception { + setupSurface(100, 100); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); } @Test public void fillEmptyBackground_dontFill_bitmapLarger() throws Exception { + setupSurface(100, 100); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200)); + verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); + } + + @Test + public void testCalculateSnapshotCrop() { + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(0, 0, 100, 90), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotCrop_taskNotOnTop() { + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100)); + assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotCrop_navBarLeft() { + setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(10, 0, 100, 100), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotCrop_navBarRight() { + setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(0, 0, 90, 100), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotFrame() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 0, 10); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + assertEquals(new Rect(0, -10, 100, 70), + mSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90))); + } + + @Test + public void testCalculateSnapshotFrame_navBarLeft() { + setupSurface(100, 100); + final Rect insets = new Rect(10, 10, 0, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + assertEquals(new Rect(0, -10, 90, 80), + mSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100))); + } + + @Test + public void testDrawStatusBarBackground() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 10, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - final Bitmap b = Bitmap.createBitmap(200, 200, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100), 10); + verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any()); + } + + @Test + public void testDrawStatusBarBackground_nope() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 10, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100), 10); verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); } + + @Test + public void testDrawNavigationBarBackground() { + final Rect insets = new Rect(0, 10, 0, 10); + setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, 100, 100)); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawNavigationBarBackground(mockCanvas); + verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any()); + } + + @Test + public void testDrawNavigationBarBackground_left() { + final Rect insets = new Rect(10, 10, 0, 0); + setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, 100, 100)); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawNavigationBarBackground(mockCanvas); + verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any()); + } + + @Test + public void testDrawNavigationBarBackground_right() { + final Rect insets = new Rect(0, 10, 10, 0); + setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, 100, 100)); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawNavigationBarBackground(mockCanvas); + verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any()); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java index 462bd68dc420..82ea2313e8d7 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java @@ -56,7 +56,7 @@ public class TaskStackContainersTests extends WindowTestsBase { // Stack should contain visible app window to be considered visible. final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */); assertFalse(mPinnedStack.isVisible()); - final TestAppWindowToken pinnedApp = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken pinnedApp = new WindowTestUtils.TestAppWindowToken(sDisplayContent); pinnedTask.addChild(pinnedApp, 0 /* addPos */); assertTrue(mPinnedStack.isVisible()); } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java index 9dbd8a617eec..267e5f77e709 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java @@ -16,9 +16,6 @@ package com.android.server.wm; -import android.content.pm.ActivityInfo; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; import org.junit.Test; import org.junit.runner.RunWith; @@ -67,12 +64,14 @@ public class TaskStackTests extends WindowTestsBase { public void testClosingAppDifferentStackOrientation() throws Exception { final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); final Task task1 = createTaskInStack(stack, 0 /* userId */); - TestAppWindowToken appWindowToken1 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken1 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task1.addChild(appWindowToken1, 0); appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); final Task task2 = createTaskInStack(stack, 1 /* userId */); - TestAppWindowToken appWindowToken2 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken2 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task2.addChild(appWindowToken2, 0); appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); @@ -85,12 +84,14 @@ public class TaskStackTests extends WindowTestsBase { public void testMoveTaskToBackDifferentStackOrientation() throws Exception { final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); final Task task1 = createTaskInStack(stack, 0 /* userId */); - TestAppWindowToken appWindowToken1 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken1 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task1.addChild(appWindowToken1, 0); appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); final Task task2 = createTaskInStack(stack, 1 /* userId */); - TestAppWindowToken appWindowToken2 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken2 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task2.addChild(appWindowToken2, 0); appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java index f79908e906c7..1819c56735eb 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java @@ -16,14 +16,9 @@ package com.android.server.wm; -import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; - -import android.hardware.display.DisplayManagerGlobal; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.view.Display; -import android.view.DisplayInfo; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,10 +40,10 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer() throws Exception { - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(); - final TestAppWindowContainerController appController = - new TestAppWindowContainerController(taskController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(); + final WindowTestUtils.TestAppWindowContainerController appController = + new WindowTestUtils.TestAppWindowContainerController(taskController); taskController.removeContainer(); // Assert that the container was removed. @@ -58,12 +53,12 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer_deferRemoval() throws Exception { - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(); - final TestAppWindowContainerController appController = - new TestAppWindowContainerController(taskController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(); + final WindowTestUtils.TestAppWindowContainerController appController = + new WindowTestUtils.TestAppWindowContainerController(taskController); - final TestTask task = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer; final AppWindowToken app = appController.mContainer; task.mShouldDeferRemoval = true; @@ -85,12 +80,12 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { public void testReparent() throws Exception { final StackWindowController stackController1 = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stackController1); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController1); final StackWindowController stackController2 = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController2 = - new TestTaskWindowContainerController(stackController2); + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stackController2); boolean gotException = false; try { @@ -114,8 +109,8 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { taskController.reparent(stackController2, 0); assertEquals(stackController2.mContainer, taskController.mContainer.getParent()); - assertEquals(0, ((TestTask) taskController.mContainer).positionInParent()); - assertEquals(1, ((TestTask) taskController2.mContainer).positionInParent()); + assertEquals(0, ((WindowTestUtils.TestTask) taskController.mContainer).positionInParent()); + assertEquals(1, ((WindowTestUtils.TestTask) taskController2.mContainer).positionInParent()); } @Test @@ -124,9 +119,9 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { final StackWindowController stack1Controller = createStackControllerOnDisplay(sDisplayContent); final TaskStack stack1 = stack1Controller.mContainer; - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stack1Controller); - final TestTask task1 = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stack1Controller); + final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer; task1.mOnDisplayChangedCalled = false; assertEquals(sDisplayContent, stack1.getDisplayContent()); @@ -134,9 +129,10 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { final DisplayContent dc = createNewDisplay(); final StackWindowController stack2Controller = createStackControllerOnDisplay(dc); final TaskStack stack2 = stack2Controller.mContainer; - final TestTaskWindowContainerController taskController2 = - new TestTaskWindowContainerController(stack2Controller); - final TestTask task2 = (TestTask) taskController2.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stack2Controller); + final WindowTestUtils.TestTask task2 = + (WindowTestUtils.TestTask) taskController2.mContainer; // Reparent and check state taskController.reparent(stack2Controller, 0); diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java index cf8af6716d34..0eaf5bc3e940 100644 --- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -46,7 +46,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testFlow() throws Exception { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token); sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token); sWm.mUnknownAppVisibilityController.notifyRelayouted(token); @@ -58,8 +58,8 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testMultiple() throws Exception { - final AppWindowToken token1 = new TestAppWindowToken(sDisplayContent); - final AppWindowToken token2 = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token1 = new WindowTestUtils.TestAppWindowToken(sDisplayContent); + final AppWindowToken token2 = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token1); sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token1); sWm.mUnknownAppVisibilityController.notifyLaunched(token2); @@ -74,7 +74,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testClear() throws Exception { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token); sWm.mUnknownAppVisibilityController.clear();; assertTrue(sWm.mUnknownAppVisibilityController.allResolved()); @@ -82,7 +82,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testAppRemoved() throws Exception { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token); sWm.mUnknownAppVisibilityController.appRemoved(token); assertTrue(sWm.mUnknownAppVisibilityController.allResolved()); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java index 2e6eac091ba8..a2aa058ac063 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java @@ -102,7 +102,7 @@ public class WindowFrameTests extends WindowTestsBase { // Just any non zero value. sWm.mSystemDecorLayer = 10000; - mWindowToken = new TestAppWindowToken(sWm.getDefaultDisplayContentLocked()); + mWindowToken = new WindowTestUtils.TestAppWindowToken(sWm.getDefaultDisplayContentLocked()); mStubStack = new TaskStack(sWm, 0); } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java new file mode 100644 index 000000000000..3a443575332e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Binder; +import android.os.IBinder; +import android.view.IApplicationToken; +import android.view.IWindow; +import android.view.WindowManager; + +import static android.app.AppOpsManager.OP_NONE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.res.Configuration.EMPTY; +import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static org.mockito.Mockito.mock; + +/** + * A collection of static functions that can be referenced by other test packages to provide access + * to WindowManager related test functionality. + */ +public class WindowTestUtils { + public static int sNextTaskId = 0; + + /** + * Retrieves an instance of {@link WindowManagerService}, creating it if necessary. + */ + public static WindowManagerService getWindowManagerService(Context context) { + return TestWindowManagerPolicy.getWindowManagerService(context); + } + + /** + * Creates a mock instance of {@link StackWindowController}. + */ + public static StackWindowController createMockStackWindowContainerController() { + StackWindowController controller = mock(StackWindowController.class); + controller.mContainer = mock(TestTaskStack.class); + return controller; + } + + /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ + public static Task createTaskInStack(WindowManagerService service, TaskStack stack, + int userId) { + final Task newTask = new Task(WindowTestUtils.sNextTaskId++, stack, userId, service, null, + EMPTY, 0, false, + false, new ActivityManager.TaskDescription(), null); + stack.addTask(newTask, POSITION_TOP); + return newTask; + } + + /** + * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not + * normally be mocked out. + */ + public static class TestTaskStack extends TaskStack { + TestTaskStack(WindowManagerService service, int stackId) { + super(service, stackId); + } + + @Override + void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) { + // Do nothing. + } + } + + /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ + public static class TestAppWindowToken extends AppWindowToken { + + TestAppWindowToken(DisplayContent dc) { + super(WindowTestsBase.sWm, null, false, dc, true /* fillsParent */, + null /* overrideConfig */, null /* bounds */); + } + + TestAppWindowToken(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable, AppWindowContainerController controller, + Configuration overrideConfig, Rect bounds) { + super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, + showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, + launchTaskBehind, alwaysFocusable, controller, overrideConfig, bounds); + } + + int getWindowsCount() { + return mChildren.size(); + } + + boolean hasWindow(WindowState w) { + return mChildren.contains(w); + } + + WindowState getFirstChild() { + return mChildren.getFirst(); + } + + WindowState getLastChild() { + return mChildren.getLast(); + } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } + } + + /* Used so we can gain access to some protected members of the {@link WindowToken} class */ + public static class TestWindowToken extends WindowToken { + int adj = 0; + + TestWindowToken(int type, DisplayContent dc) { + this(type, dc, false /* persistOnEmpty */); + } + + TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { + super(WindowTestsBase.sWm, mock(IBinder.class), type, persistOnEmpty, dc, + false /* ownerCanManageAppTokens */); + } + + int getWindowsCount() { + return mChildren.size(); + } + + boolean hasWindow(WindowState w) { + return mChildren.contains(w); + } + + @Override + int getAnimLayerAdjustment() { + return adj; + } + } + + /* Used so we can gain access to some protected members of the {@link Task} class */ + public static class TestTask extends Task { + boolean mShouldDeferRemoval = false; + boolean mOnDisplayChangedCalled = false; + private boolean mUseLocalIsAnimating = false; + private boolean mIsAnimating = false; + + TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, + Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, + boolean homeTask, TaskWindowContainerController controller) { + super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode, + supportsPictureInPicture, homeTask, new ActivityManager.TaskDescription(), + controller); + } + + boolean shouldDeferRemoval() { + return mShouldDeferRemoval; + } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } + + @Override + void onDisplayChanged(DisplayContent dc) { + super.onDisplayChanged(dc); + mOnDisplayChangedCalled = true; + } + + @Override + boolean isAnimating() { + return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating(); + } + + void setLocalIsAnimating(boolean isAnimating) { + mUseLocalIsAnimating = true; + mIsAnimating = isAnimating; + } + } + + /** + * Used so we can gain access to some protected members of {@link TaskWindowContainerController} + * class. + */ + public static class TestTaskWindowContainerController extends TaskWindowContainerController { + + TestTaskWindowContainerController() { + this(WindowTestsBase.createStackControllerOnDisplay(WindowTestsBase.sDisplayContent)); + } + + TestTaskWindowContainerController(StackWindowController stackController) { + super(sNextTaskId++, new TaskWindowContainerListener() { + @Override + public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) { + + } + + @Override + public void requestResize(Rect bounds, int resizeMode) { + + } + }, stackController, 0 /* userId */, null /* bounds */, + EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, + false /* supportsPictureInPicture */, false /* homeTask*/, true /* toTop*/, + true /* showForAllUsers */, new ActivityManager.TaskDescription(), WindowTestsBase.sWm); + } + + @Override + TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, + Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, + boolean homeTask, ActivityManager.TaskDescription taskDescription) { + return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode, + supportsPictureInPicture, homeTask, this); + } + } + + public static class TestAppWindowContainerController extends AppWindowContainerController { + + final IApplicationToken mToken; + + TestAppWindowContainerController(TestTaskWindowContainerController taskController) { + this(taskController, new TestIApplicationToken()); + } + + TestAppWindowContainerController(TestTaskWindowContainerController taskController, + IApplicationToken token) { + super(taskController, token, null /* listener */, 0 /* index */, + SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */, + true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */, + false /* launchTaskBehind */, false /* alwaysFocusable */, + 0 /* targetSdkVersion */, 0 /* rotationAnimationHint */, + 0 /* inputDispatchingTimeoutNanos */, WindowTestsBase.sWm, null /* overrideConfig */, + null /* bounds */); + mToken = token; + } + + @Override + AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable, AppWindowContainerController controller, + Configuration overrideConfig, Rect bounds) { + return new TestAppWindowToken(service, token, voiceInteraction, dc, + inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, + orientation, + rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, + controller, overrideConfig, bounds); + } + + AppWindowToken getAppWindowToken() { + return (AppWindowToken) WindowTestsBase.sDisplayContent.getWindowToken(mToken.asBinder()); + } + } + + public static class TestIApplicationToken implements IApplicationToken { + + private final Binder mBinder = new Binder(); + @Override + public IBinder asBinder() { + return mBinder; + } + } + + /** Used to track resize reports. */ + public static class TestWindowState extends WindowState { + boolean resizeReported; + + TestWindowState(WindowManagerService service, Session session, IWindow window, + WindowManager.LayoutParams attrs, WindowToken token) { + super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, + false /* ownerCanAddInternalSystemWindow */); + } + + @Override + void reportResized() { + super.reportResized(); + resizeReported = true; + } + + @Override + public boolean isGoneForLayoutLw() { + return false; + } + + @Override + void updateResizingWindowIfNeeded() { + // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive + // the system that it can actually update the window. + boolean hadSurface = mHasSurface; + mHasSurface = true; + + super.updateResizingWindowIfNeeded(); + + mHasSurface = hadSurface; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index a9d930f5c893..eaf4ac4baf4f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -19,21 +19,16 @@ package com.android.server.wm; import static android.view.View.VISIBLE; import android.app.ActivityManager.TaskDescription; -import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; -import android.os.Binder; import android.view.Display; import android.view.DisplayInfo; -import android.view.IApplicationToken; import org.junit.Assert; import org.junit.After; import org.junit.Before; import org.mockito.MockitoAnnotations; -import android.app.ActivityManager.TaskSnapshot; import android.content.Context; -import android.os.IBinder; import android.support.test.InstrumentationRegistry; import android.view.IWindow; import android.view.WindowManager; @@ -41,10 +36,6 @@ import android.view.WindowManager; import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.AppOpsManager.OP_NONE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.content.res.Configuration.EMPTY; -import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; @@ -58,7 +49,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.mockito.Mockito.mock; import com.android.server.AttributeCache; @@ -78,8 +68,7 @@ class WindowTestsBase { // make sure we don't collide with any existing display. If we run into no other display, the // added display should be treated as default. This cannot be the default display private static int sNextDisplayId = Display.DEFAULT_DISPLAY + 1; - static int sNextStackId = FIRST_DYNAMIC_STACK_ID; - private static int sNextTaskId = 0; + private static int sNextStackId = FIRST_DYNAMIC_STACK_ID; private static boolean sOneTimeSetupDone = false; static DisplayContent sDisplayContent; @@ -184,14 +173,14 @@ class WindowTestsBase { private static WindowToken createWindowToken(DisplayContent dc, int stackId, int type) { if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { - return new TestWindowToken(type, dc); + return new WindowTestUtils.TestWindowToken(type, dc); } final TaskStack stack = stackId == INVALID_STACK_ID ? createTaskStackOnDisplay(dc) : createStackControllerOnStackOnDisplay(stackId, dc).mContainer; final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken token = new TestAppWindowToken(dc); + final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(dc); task.addChild(token, 0); return token; } @@ -209,7 +198,7 @@ class WindowTestsBase { } WindowState createAppWindow(Task task, int type, String name) { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); task.addChild(token, 0); return createWindow(null, type, token, name); } @@ -260,10 +249,7 @@ class WindowTestsBase { /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ static Task createTaskInStack(TaskStack stack, int userId) { - final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, 0, false, - false, new TaskDescription(), null); - stack.addTask(newTask, POSITION_TOP); - return newTask; + return WindowTestUtils.createTaskInStack(sWm, stack, userId); } /** Creates a {@link DisplayContent} and adds it to the system. */ @@ -274,227 +260,10 @@ class WindowTestsBase { return new DisplayContent(display, sWm, sLayersController, new WallpaperController(sWm)); } - /* Used so we can gain access to some protected members of the {@link WindowToken} class */ - static class TestWindowToken extends WindowToken { - int adj = 0; - - TestWindowToken(int type, DisplayContent dc) { - this(type, dc, false /* persistOnEmpty */); - } - - TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { - super(sWm, mock(IBinder.class), type, persistOnEmpty, dc, - false /* ownerCanManageAppTokens */); - } - - int getWindowsCount() { - return mChildren.size(); - } - - boolean hasWindow(WindowState w) { - return mChildren.contains(w); - } - - @Override - int getAnimLayerAdjustment() { - return adj; - } - } - - /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ - static class TestAppWindowToken extends AppWindowToken { - - TestAppWindowToken(DisplayContent dc) { - super(sWm, null, false, dc, true /* fillsParent */, null /* overrideConfig */, - null /* bounds */); - } - - TestAppWindowToken(WindowManagerService service, IApplicationToken token, - boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, - boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, - int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller, - Configuration overrideConfig, Rect bounds) { - super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, - showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, - launchTaskBehind, alwaysFocusable, controller, overrideConfig, bounds); - } - - int getWindowsCount() { - return mChildren.size(); - } - - boolean hasWindow(WindowState w) { - return mChildren.contains(w); - } - - WindowState getFirstChild() { - return mChildren.getFirst(); - } - - WindowState getLastChild() { - return mChildren.getLast(); - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - } - - /* Used so we can gain access to some protected members of the {@link Task} class */ - class TestTask extends Task { - - boolean mShouldDeferRemoval = false; - boolean mOnDisplayChangedCalled = false; - private boolean mUseLocalIsAnimating = false; - private boolean mIsAnimating = false; - - TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, - Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, - boolean homeTask, TaskWindowContainerController controller) { - super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode, - supportsPictureInPicture, homeTask, new TaskDescription(), controller); - } - - boolean shouldDeferRemoval() { - return mShouldDeferRemoval; - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - - @Override - void onDisplayChanged(DisplayContent dc) { - super.onDisplayChanged(dc); - mOnDisplayChangedCalled = true; - } - - @Override - boolean isAnimating() { - return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating(); - } - - void setLocalIsAnimating(boolean isAnimating) { - mUseLocalIsAnimating = true; - mIsAnimating = isAnimating; - } - } - - /** - * Used so we can gain access to some protected members of {@link TaskWindowContainerController} - * class. - */ - class TestTaskWindowContainerController extends TaskWindowContainerController { - - TestTaskWindowContainerController() { - this(createStackControllerOnDisplay(sDisplayContent)); - } - - TestTaskWindowContainerController(StackWindowController stackController) { - super(sNextTaskId++, new TaskWindowContainerListener() { - @Override - public void onSnapshotChanged(TaskSnapshot snapshot) { - - } - - @Override - public void requestResize(Rect bounds, int resizeMode) { - - } - }, stackController, 0 /* userId */, null /* bounds */, - EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, - false /* supportsPictureInPicture */, false /* homeTask*/, true /* toTop*/, - true /* showForAllUsers */, new TaskDescription(), sWm); - } - - @Override - TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, - Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, - boolean homeTask, TaskDescription taskDescription) { - return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode, - supportsPictureInPicture, homeTask, this); - } - } - - class TestAppWindowContainerController extends AppWindowContainerController { - - final IApplicationToken mToken; - - TestAppWindowContainerController(TestTaskWindowContainerController taskController) { - this(taskController, new TestIApplicationToken()); - } - - TestAppWindowContainerController(TestTaskWindowContainerController taskController, - IApplicationToken token) { - super(taskController, token, null /* listener */, 0 /* index */, - SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */, - true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */, - false /* launchTaskBehind */, false /* alwaysFocusable */, - 0 /* targetSdkVersion */, 0 /* rotationAnimationHint */, - 0 /* inputDispatchingTimeoutNanos */, sWm, null /* overrideConfig */, - null /* bounds */); - mToken = token; - } - - @Override - AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, - boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, - boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, - int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller, - Configuration overrideConfig, Rect bounds) { - return new TestAppWindowToken(service, token, voiceInteraction, dc, - inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, - orientation, - rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, - controller, overrideConfig, bounds); - } - - AppWindowToken getAppWindowToken() { - return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder()); - } + /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */ + WindowTestUtils.TestWindowState createWindowState(WindowManager.LayoutParams attrs, + WindowToken token) { + return new WindowTestUtils.TestWindowState(sWm, sMockSession, sIWindow, attrs, token); } - class TestIApplicationToken implements IApplicationToken { - - private final Binder mBinder = new Binder(); - @Override - public IBinder asBinder() { - return mBinder; - } - } - - /** Used to track resize reports. */ - class TestWindowState extends WindowState { - boolean resizeReported; - - TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) { - super(sWm, sMockSession, sIWindow, token, null, OP_NONE, 0, attrs, 0, 0, - false /* ownerCanAddInternalSystemWindow */); - } - - @Override - void reportResized() { - super.reportResized(); - resizeReported = true; - } - - @Override - public boolean isGoneForLayoutLw() { - return false; - } - - @Override - void updateResizingWindowIfNeeded() { - // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive - // the system that it can actually update the window. - boolean hadSurface = mHasSurface; - mHasSurface = true; - - super.updateResizingWindowIfNeeded(); - - mHasSurface = hadSurface; - } - } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java index babb6d9db31f..4f7ad41050f1 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java @@ -46,7 +46,8 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testAddWindow() throws Exception { - final TestWindowToken token = new TestWindowToken(0, sDisplayContent); + final WindowTestUtils.TestWindowToken token = + new WindowTestUtils.TestWindowToken(0, sDisplayContent); assertEquals(0, token.getWindowsCount()); @@ -76,7 +77,7 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testChildRemoval() throws Exception { final DisplayContent dc = sDisplayContent; - final TestWindowToken token = new TestWindowToken(0, dc); + final WindowTestUtils.TestWindowToken token = new WindowTestUtils.TestWindowToken(0, dc); assertEquals(token, dc.getWindowToken(token.token)); @@ -95,7 +96,8 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testAdjustAnimLayer() throws Exception { - final TestWindowToken token = new TestWindowToken(0, sDisplayContent); + final WindowTestUtils.TestWindowToken token = + new WindowTestUtils.TestWindowToken(0, sDisplayContent); final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1"); final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11"); final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12"); @@ -135,8 +137,9 @@ public class WindowTokenTests extends WindowTestsBase { */ @Test public void testTokenRemovalProcess() throws Exception { - final TestWindowToken token = - new TestWindowToken(TYPE_TOAST, sDisplayContent, true /* persistOnEmpty */); + final WindowTestUtils.TestWindowToken token = + new WindowTestUtils.TestWindowToken(TYPE_TOAST, sDisplayContent, + true /* persistOnEmpty */); // Verify that the token is on the display assertNotNull(sDisplayContent.getWindowToken(token.token)); diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java index 7b8ebd44e629..01e36f57d1be 100644 --- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java @@ -1039,18 +1039,34 @@ class UsbProfileGroupSettingsManager { * Start the appropriate package when an device/accessory got attached. * * @param intent The intent to start the package - * @param matches The available resolutions of the intent + * @param rawMatches The available resolutions of the intent * @param defaultActivity The default activity for the device (if set) * @param device The device if a device was attached * @param accessory The accessory if a device was attached */ - private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches, + private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> rawMatches, @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device, @Nullable UsbAccessory accessory) { - int count = matches.size(); + final int numRawMatches = rawMatches.size(); + + // The raw matches contain the activities that can be started but also the intents to switch + // between the profiles + int numParentActivityMatches = 0; + int numNonParentActivityMatches = 0; + for (int i = 0; i < numRawMatches; i++) { + final ResolveInfo rawMatch = rawMatches.get(i); + if (!rawMatch.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { + if (UserHandle.getUserHandleForUid( + rawMatch.activityInfo.applicationInfo.uid).equals(mParentUser)) { + numParentActivityMatches++; + } else { + numNonParentActivityMatches++; + } + } + } // don't show the resolver activity if there are no choices available - if (count == 0) { + if (numParentActivityMatches + numNonParentActivityMatches == 0) { if (accessory != null) { String uri = accessory.getUri(); if (uri != null && uri.length() > 0) { @@ -1073,6 +1089,21 @@ class UsbProfileGroupSettingsManager { return; } + // If only one profile has activity matches, we need to remove all switch intents + ArrayList<ResolveInfo> matches; + if (numParentActivityMatches == 0 || numNonParentActivityMatches == 0) { + matches = new ArrayList<>(numParentActivityMatches + numNonParentActivityMatches); + + for (int i = 0; i < numRawMatches; i++) { + ResolveInfo rawMatch = rawMatches.get(i); + if (!rawMatch.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { + matches.add(rawMatch); + } + } + } else { + matches = rawMatches; + } + if (defaultActivity != null) { UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser( UserHandle.getUserId(defaultActivity.applicationInfo.uid)); @@ -1101,7 +1132,7 @@ class UsbProfileGroupSettingsManager { resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); UserHandle user; - if (count == 1) { + if (matches.size() == 1) { ResolveInfo rInfo = matches.get(0); // start UsbConfirmActivity if there is only one choice diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 061c0afa36aa..8357a2b535a4 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -877,19 +877,16 @@ public class TelephonyManager { /** * USSD return code success. - * @hide */ public static final int USSD_RETURN_SUCCESS = 100; /** * USSD return code for failure case. - * @hide */ public static final int USSD_RETURN_FAILURE = -1; /** * USSD return code for failure case. - * @hide */ public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; @@ -5113,16 +5110,32 @@ public class TelephonyManager { return new int[0]; } - public static abstract class OnReceiveUssdResponseCallback { + /* The caller of {@link #sendUssdRequest(String, UssdResponseCallback, Handler} provides + * once the network returns a USSD message or if there is failure. + * Either {@link #onReceiveUssdResponse(TelephonyManager, String, CharSequence} or + * {@link #onReceiveUssdResponseFailed(TelephonyManager, String, int} will be called. + */ + public static abstract class UssdResponseCallback { /** - ** Called when USSD has succeeded. + * Called when USSD has succeeded. The calling app can choose to either display the message + * or interpret the message. + * @param telephonyManager the TelephonyManager the callback is registered to. + * @param request the ussd code sent to the network. + * @param response the response from the network. **/ - public void onReceiveUssdResponse(String request, CharSequence response) {}; + public void onReceiveUssdResponse(final TelephonyManager telephonyManager, + String request, CharSequence response) {}; /** - ** Called when USSD has failed. + * Called when USSD has failed. + * @param telephonyManager the TelephonyManager the callback is registered to + * @param request the ussd code. + * @param failureCode failure code, should be either of + * {@link TelephonyManager#USSD_RETURN_FAILURE} or + * {@link TelephonyManager#USSD_ERROR_SERVICE_UNAVAIL}. **/ - public void onReceiveUssdResponseFailed(String request, int failureCode) {}; + public void onReceiveUssdResponseFailed(final TelephonyManager telephonyManager, + String request, int failureCode) {}; } /** @@ -5134,13 +5147,14 @@ public class TelephonyManager { * {@link android.Manifest.permission#CALL_PHONE} * @param ussdRequest the USSD command to be executed. * @param callback called by the framework to inform the caller of the result of executing the - * USSD request (see {@link OnReceiveUssdResponseCallback}). + * USSD request (see {@link UssdResponseCallback}). * @param handler the {@link Handler} to run the request on. */ @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String ussdRequest, - final OnReceiveUssdResponseCallback callback, Handler handler) { - checkNotNull(callback, "OnReceiveUssdResponseCallback cannot be null."); + final UssdResponseCallback callback, Handler handler) { + checkNotNull(callback, "UssdResponseCallback cannot be null."); + final TelephonyManager telephonyManager = this; ResultReceiver wrappedCallback = new ResultReceiver(handler) { @Override @@ -5150,10 +5164,11 @@ public class TelephonyManager { UssdResponse response = ussdResponse.getParcelable(USSD_RESPONSE); if (resultCode == USSD_RETURN_SUCCESS) { - callback.onReceiveUssdResponse(response.getUssdRequest(), + callback.onReceiveUssdResponse(telephonyManager, response.getUssdRequest(), response.getReturnMessage()); } else { - callback.onReceiveUssdResponseFailed(response.getUssdRequest(), resultCode); + callback.onReceiveUssdResponseFailed(telephonyManager, + response.getUssdRequest(), resultCode); } } }; @@ -5172,11 +5187,13 @@ public class TelephonyManager { } } - /* - * @return true, if the device is currently on a technology (e.g. UMTS or LTE) which can support - * voice and data simultaneously. This can change based on location or network condition. - */ - public boolean isConcurrentVoiceAndDataAllowed() { + /** + * Whether the device is currently on a technology (e.g. UMTS or LTE) which can support + * voice and data simultaneously. This can change based on location or network condition. + * + * @return {@code true} if simultaneous voice and data supported, and {@code false} otherwise. + */ + public boolean isConcurrentVoiceAndDataSupported() { try { ITelephony telephony = getITelephony(); return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed(mSubId)); diff --git a/tests/WindowManagerStressTest/Android.mk b/tests/WindowManagerStressTest/Android.mk new file mode 100644 index 000000000000..e4cbe939e521 --- /dev/null +++ b/tests/WindowManagerStressTest/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := WindowManagerStressTest + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_PACKAGE) diff --git a/tests/WindowManagerStressTest/AndroidManifest.xml b/tests/WindowManagerStressTest/AndroidManifest.xml new file mode 100644 index 000000000000..17e0f15c29a9 --- /dev/null +++ b/tests/WindowManagerStressTest/AndroidManifest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2017 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="test.windowmanagerstresstest"> + + <application + android:allowBackup="false" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:supportsRtl="true" + android:theme="@style/AppTheme"> + <activity android:name=".MainActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/tests/WindowManagerStressTest/res/layout/activity_main.xml b/tests/WindowManagerStressTest/res/layout/activity_main.xml new file mode 100644 index 000000000000..6cf82691155c --- /dev/null +++ b/tests/WindowManagerStressTest/res/layout/activity_main.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + tools:context="test.amslam.MainActivity"> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/run" + android:text="@string/run" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/output" /> + +</LinearLayout> diff --git a/tests/WindowManagerStressTest/res/mipmap-hdpi/ic_launcher.png b/tests/WindowManagerStressTest/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..cde69bcccec6 --- /dev/null +++ b/tests/WindowManagerStressTest/res/mipmap-hdpi/ic_launcher.png diff --git a/tests/WindowManagerStressTest/res/mipmap-mdpi/ic_launcher.png b/tests/WindowManagerStressTest/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..c133a0cbd379 --- /dev/null +++ b/tests/WindowManagerStressTest/res/mipmap-mdpi/ic_launcher.png diff --git a/tests/WindowManagerStressTest/res/mipmap-xhdpi/ic_launcher.png b/tests/WindowManagerStressTest/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..bfa42f0e7b91 --- /dev/null +++ b/tests/WindowManagerStressTest/res/mipmap-xhdpi/ic_launcher.png diff --git a/tests/WindowManagerStressTest/res/mipmap-xxhdpi/ic_launcher.png b/tests/WindowManagerStressTest/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..324e72cdd748 --- /dev/null +++ b/tests/WindowManagerStressTest/res/mipmap-xxhdpi/ic_launcher.png diff --git a/tests/WindowManagerStressTest/res/mipmap-xxxhdpi/ic_launcher.png b/tests/WindowManagerStressTest/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..aee44e138434 --- /dev/null +++ b/tests/WindowManagerStressTest/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/tests/WindowManagerStressTest/res/values/colors.xml b/tests/WindowManagerStressTest/res/values/colors.xml new file mode 100644 index 000000000000..4270ca68a860 --- /dev/null +++ b/tests/WindowManagerStressTest/res/values/colors.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <color name="colorPrimary">#3F51B5</color> + <color name="colorPrimaryDark">#303F9F</color> + <color name="colorAccent">#FF4081</color> +</resources> diff --git a/tests/WindowManagerStressTest/res/values/dimens.xml b/tests/WindowManagerStressTest/res/values/dimens.xml new file mode 100644 index 000000000000..ed4ccbcc700f --- /dev/null +++ b/tests/WindowManagerStressTest/res/values/dimens.xml @@ -0,0 +1,19 @@ +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> +</resources> diff --git a/tests/WindowManagerStressTest/res/values/strings.xml b/tests/WindowManagerStressTest/res/values/strings.xml new file mode 100644 index 000000000000..cef05dcb6584 --- /dev/null +++ b/tests/WindowManagerStressTest/res/values/strings.xml @@ -0,0 +1,19 @@ +<!-- + ~ Copyright (C) 2017 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<resources> + <string name="app_name">WmSlam</string> + <string name="run">Run</string> +</resources> diff --git a/tests/WindowManagerStressTest/res/values/styles.xml b/tests/WindowManagerStressTest/res/values/styles.xml new file mode 100644 index 000000000000..0983b2535878 --- /dev/null +++ b/tests/WindowManagerStressTest/res/values/styles.xml @@ -0,0 +1,23 @@ +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- Base application theme. --> + <style name="AppTheme" parent="@android:style/Theme.Material.Light.DarkActionBar"> + <!-- Customize your theme here. --> + <item name="android:colorPrimary">@color/colorPrimary</item> + <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item> + <item name="android:colorAccent">@color/colorAccent</item> + </style> +</resources> diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java new file mode 100644 index 000000000000..6b9bb31a75c7 --- /dev/null +++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package test.windowmanagerstresstest; + +import android.app.Activity; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; +import android.util.MergedConfiguration; +import android.view.Display; +import android.view.IWindowSession; +import android.view.Surface; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.WindowManagerGlobal; +import android.widget.TextView; + +import com.android.internal.view.BaseIWindow; + +import java.util.ArrayList; + +public class MainActivity extends Activity { + + private static final String TAG = "WmSlam"; + + private TextView mOutput; + private volatile boolean finished; + private final ArrayList<BaseIWindow> mWindows = new ArrayList<>(); + private final LayoutParams mLayoutParams = new LayoutParams(); + private final Rect mTmpRect = new Rect(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + mOutput = (TextView) findViewById(R.id.output); + + findViewById(R.id.run).setOnClickListener(view -> { + view.setEnabled(false); + mOutput.setText(""); + startBatch(); + }); + mLayoutParams.token = getActivityToken(); + } + + void startBatch() { + new Thread(() -> { + finished = false; + addWindows(); + startCpuRunnables(); + for (int i = 0; i < 5; i++) { + final long time = SystemClock.uptimeMillis(); + slamWm(); + log("Total: " + (SystemClock.uptimeMillis() - time) + " ms"); + } + removeWindows(); + finished = true; + }).start(); + } + + void startCpuRunnables() { + for (int i = 0; i < 10; i++) { + new Thread(mUseCpuRunnable).start(); + } + } + + private final Runnable mUseCpuRunnable = new Runnable() { + @Override + public void run() { + while (!finished) { + } + } + }; + + private void log(String text) { + mOutput.post(() -> mOutput.append(text + "\n")); + Log.d(TAG, text); + } + + private void slamWm() { + ArrayList<Thread> threads = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + for (BaseIWindow window : mWindows) { + Thread t = new Thread(() -> { + try { + WindowManagerGlobal.getWindowSession().relayout(window, + window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, mTmpRect, + mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, + new MergedConfiguration(), new Surface()); + } catch (RemoteException e) { + e.printStackTrace(); + } + }); + threads.add(t); + t.start(); + } + } + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + void addWindows() { + for (int i = 0; i < 50; i++) { + final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); + layoutParams.token = getActivityToken(); + final BaseIWindow window = new BaseIWindow(); + final IWindowSession session = WindowManagerGlobal.getWindowSession(); + final Rect tmpRect = new Rect(); + try { + final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq, layoutParams, + View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect); + } catch (RemoteException e) { + e.printStackTrace(); + } + mWindows.add(window); + } + } + + void removeWindows() { + for (BaseIWindow window : mWindows) { + try { + WindowManagerGlobal.getWindowSession().remove(window); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 04443a53527c..c562cb95ee31 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -80,6 +80,7 @@ import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult; import com.android.server.net.NetworkPinner; +import com.android.server.net.NetworkPolicyManagerInternal; import java.net.InetAddress; import java.util.ArrayList; @@ -713,6 +714,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { } mServiceContext = new MockContext(getContext()); + LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); + LocalServices.addService( + NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class)); mService = new WrappedConnectivityService(mServiceContext, mock(INetworkManagementService.class), mock(INetworkStatsService.class), diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index 7f715895e282..a33fd0673d98 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -1913,7 +1913,7 @@ int Link(const std::vector<StringPiece>& args) { std::vector<std::string> overlay_arg_list; std::vector<std::string> extra_java_packages; Maybe<std::string> package_id; - Maybe<std::string> configs; + std::vector<std::string> configs; Maybe<std::string> preferred_density; Maybe<std::string> product_list; bool legacy_x_flag = false; @@ -1971,7 +1971,7 @@ int Link(const std::vector<StringPiece>& args) { &legacy_x_flag) .OptionalSwitch("-z", "Require localization of strings marked 'suggested'", &require_localization) - .OptionalFlag("-c", + .OptionalFlagList("-c", "Comma separated list of configurations to include. The default\n" "is all configurations", &configs) @@ -2151,28 +2151,29 @@ int Link(const std::vector<StringPiece>& args) { } AxisConfigFilter filter; - if (configs) { - for (const StringPiece& config_str : util::Tokenize(configs.value(), ',')) { - ConfigDescription config; - LocaleValue lv; - if (lv.InitFromFilterString(config_str)) { - lv.WriteTo(&config); - } else if (!ConfigDescription::Parse(config_str, &config)) { - context.GetDiagnostics()->Error(DiagMessage() << "invalid config '" - << config_str - << "' for -c option"); - return 1; - } + if (configs.empty()) { + for (const std::string& config_arg : configs) { + for (const StringPiece& config_str : util::Tokenize(config_arg, ',')) { + ConfigDescription config; + LocaleValue lv; + if (lv.InitFromFilterString(config_str)) { + lv.WriteTo(&config); + } else if (!ConfigDescription::Parse(config_str, &config)) { + context.GetDiagnostics()->Error(DiagMessage() << "invalid config '" + << config_str + << "' for -c option"); + return 1; + } - if (config.density != 0) { - context.GetDiagnostics()->Warn(DiagMessage() << "ignoring density '" - << config - << "' for -c option"); - } else { - filter.AddConfig(config); + if (config.density != 0) { + context.GetDiagnostics()->Warn(DiagMessage() << "ignoring density '" + << config + << "' for -c option"); + } else { + filter.AddConfig(config); + } } } - options.table_splitter_options.config_filter = &filter; } diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java index 7fe464abbf32..a21fe68e9003 100644 --- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java @@ -26,6 +26,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.AssetManager; import android.content.res.BridgeAssetManager; +import android.graphics.fonts.FontVariationAxis; import android.text.FontConfig; import java.awt.Font; @@ -250,7 +251,7 @@ public class FontFamily_Delegate { // ---- delegate methods ---- @LayoutlibDelegate /*package*/ static boolean addFont(FontFamily thisFontFamily, String path, int ttcIndex, - FontConfig.Axis[] axes, int weight, int italic) { + FontVariationAxis[] axes, int weight, int italic) { if (thisFontFamily.mBuilderPtr == 0) { assert false : "Unable to call addFont after freezing."; return false; diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java index 80e3bada8ca3..c7818a3a9974 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java @@ -23,6 +23,7 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.annotation.NonNull; import android.graphics.FontFamily_Delegate.FontVariant; +import android.graphics.fonts.FontVariationAxis; import android.text.FontConfig; import java.awt.Font; @@ -163,7 +164,7 @@ public final class Typeface_Delegate { @LayoutlibDelegate /*package*/ static synchronized long nativeCreateFromTypefaceWithVariation(long native_instance, - List<FontConfig.Axis> axes) { + List<FontVariationAxis> axes) { long newInstance = nativeCreateFromTypeface(native_instance, 0); if (newInstance != 0) { |