diff options
243 files changed, 7392 insertions, 4348 deletions
diff --git a/Android.mk b/Android.mk index f1db6b7d4599..01fb73e0b5d1 100644 --- a/Android.mk +++ b/Android.mk @@ -224,6 +224,7 @@ LOCAL_SRC_FILES += \ core/java/android/net/IEthernetManager.aidl \ core/java/android/net/IEthernetServiceListener.aidl \ core/java/android/net/INetdEventCallback.aidl \ + core/java/android/net/IIpSecService.aidl \ core/java/android/net/INetworkManagementEventObserver.aidl \ core/java/android/net/INetworkPolicyListener.aidl \ core/java/android/net/INetworkPolicyManager.aidl \ @@ -1460,8 +1461,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 642a1d2f7c25..fe35f617680a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1609,6 +1609,7 @@ package android { field public static final int alert_light_frame = 17301505; // 0x1080001 field public static final int arrow_down_float = 17301506; // 0x1080002 field public static final int arrow_up_float = 17301507; // 0x1080003 + field public static final int autofilled_highlight = 17301684; // 0x10800b4 field public static final int bottom_bar = 17301658; // 0x108009a field public static final int btn_default = 17301508; // 0x1080004 field public static final int btn_default_small = 17301509; // 0x1080005 @@ -3578,7 +3579,7 @@ package android.app { method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public void enterPictureInPictureMode(); method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs); - method public android.view.View findViewById(int); + method public <T extends android.view.View> T findViewById(int); method public void finish(); method public void finishActivity(int); method public void finishActivityFromChild(android.app.Activity, int); @@ -4358,7 +4359,7 @@ package android.app { method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public boolean dispatchTouchEvent(android.view.MotionEvent); method public boolean dispatchTrackballEvent(android.view.MotionEvent); - method public android.view.View findViewById(int); + method public <T extends android.view.View> T findViewById(int); method public android.app.ActionBar getActionBar(); method public final android.content.Context getContext(); method public android.view.View getCurrentFocus(); @@ -4771,7 +4772,7 @@ package android.app { method public abstract android.app.FragmentManager.BackStackEntry getBackStackEntryAt(int); method public abstract int getBackStackEntryCount(); method public abstract android.app.Fragment getFragment(android.os.Bundle, java.lang.String); - method public abstract java.util.Collection<android.app.Fragment> getFragments(); + method public abstract java.util.List<android.app.Fragment> getFragments(); method public abstract android.app.Fragment getPrimaryNavigationFragment(); method public void invalidateOptionsMenu(); method public abstract boolean isDestroyed(); @@ -5116,6 +5117,7 @@ package android.app { method public java.lang.String getChannel(); method public java.lang.String getGroup(); method public android.graphics.drawable.Icon getLargeIcon(); + method public java.lang.CharSequence getSettingsText(); method public java.lang.String getShortcutId(); method public android.graphics.drawable.Icon getSmallIcon(); method public java.lang.String getSortKey(); @@ -5160,6 +5162,8 @@ package android.app { field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big"; field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession"; field public static final java.lang.String EXTRA_MESSAGES = "android.messages"; + field public static final java.lang.String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID"; + field public static final java.lang.String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG"; field public static final java.lang.String EXTRA_PEOPLE = "android.people"; field public static final java.lang.String EXTRA_PICTURE = "android.picture"; field public static final java.lang.String EXTRA_PROGRESS = "android.progress"; @@ -5345,6 +5349,7 @@ package android.app { method public android.app.Notification.Builder setProgress(int, int, boolean); method public android.app.Notification.Builder setPublicVersion(android.app.Notification); method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]); + method public android.app.Notification.Builder setSettingsText(java.lang.CharSequence); method public android.app.Notification.Builder setShortcutId(java.lang.String); method public android.app.Notification.Builder setShowWhen(boolean); method public android.app.Notification.Builder setSmallIcon(int); @@ -7044,6 +7049,7 @@ package android.appwidget { method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int); method public android.os.Bundle getAppWidgetOptions(int); method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders(); + method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForPackage(java.lang.String, android.os.UserHandle); method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle); method public static android.appwidget.AppWidgetManager getInstance(android.content.Context); method public boolean isRequestPinAppWidgetSupported(); @@ -20914,6 +20920,7 @@ package android.media { method public int getContentType(); method public int getFlags(); method public int getUsage(); + method public static deprecated int getVolumeControlStream(android.media.AudioAttributes); method public int getVolumeControlStream(); method public void writeToParcel(android.os.Parcel, int); field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3 @@ -24556,85 +24563,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 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_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 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 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"; } @@ -24721,22 +24649,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; + 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.BaseProgramColumns { + 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; @@ -24765,28 +24787,122 @@ 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 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 @@ -25447,7 +25563,7 @@ package android.net { method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform); method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform); - method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; + method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 } @@ -25481,7 +25597,6 @@ package android.net { method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm); method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm); method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int); - method public android.net.IpSecTransform.Builder setSpi(int, int); method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex); } @@ -34411,14 +34526,15 @@ package android.provider { public static final class FontsContract.Columns implements android.provider.BaseColumns { ctor public FontsContract.Columns(); + field public static final java.lang.String ITALIC = "font_italic"; field public static final java.lang.String RESULT_CODE = "result_code"; field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1 field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2 field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3 field public static final int RESULT_CODE_OK = 0; // 0x0 - field public static final java.lang.String STYLE = "font_style"; field public static final java.lang.String TTC_INDEX = "font_ttc_index"; field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings"; + field public static final java.lang.String WEIGHT = "font_weight"; } public final deprecated class LiveFolders implements android.provider.BaseColumns { @@ -36941,7 +37057,7 @@ package android.service.autofill { ctor public FillResponse.Builder(); method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset); method public android.service.autofill.FillResponse build(); - method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender, android.widget.RemoteViews); + method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews); method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle); method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo); } @@ -36957,8 +37073,10 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR; field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2 field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3 + field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5; // 0x5 field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0 field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1 + field public static final int SAVE_DATA_TYPE_USERNAME = 4; // 0x4 } public static final class SaveInfo.Builder { @@ -38640,6 +38758,7 @@ package android.telecom { method public java.lang.String getCallerDisplayName(); method public int getCallerDisplayNamePresentation(); method public final long getConnectTimeMillis(); + method public long getCreationTimeMillis(); method public android.telecom.DisconnectCause getDisconnectCause(); method public android.os.Bundle getExtras(); method public android.telecom.GatewayInfo getGatewayInfo(); @@ -41213,6 +41332,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 { @@ -41414,7 +41535,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); @@ -46493,7 +46614,7 @@ package android.view { method public void clearFlags(int); method public abstract void closeAllPanels(); method public abstract void closePanel(int); - method public android.view.View findViewById(int); + method public <T extends android.view.View> T findViewById(int); method public boolean getAllowEnterTransitionOverlap(); method public boolean getAllowReturnTransitionOverlap(); method public final android.view.WindowManager.LayoutParams getAttributes(); @@ -48618,7 +48739,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(); @@ -48667,7 +48788,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); @@ -48958,12 +49079,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); @@ -51224,7 +51345,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(); @@ -51337,7 +51458,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/removed.txt b/api/removed.txt index 42b2ae61e5cf..d20c08cf7a8f 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -296,6 +296,10 @@ package android.provider { field public static final deprecated java.lang.String TIMESTAMP = "timestamp"; } + public static final class FontsContract.Columns implements android.provider.BaseColumns { + field public static final java.lang.String STYLE = "font_style"; + } + public static final class Settings.Global extends android.provider.Settings.NameValueTable { field public static final deprecated java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync"; } diff --git a/api/system-current.txt b/api/system-current.txt index 23615193eb48..16e6f5b1e34d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1727,6 +1727,7 @@ package android { field public static final int alert_light_frame = 17301505; // 0x1080001 field public static final int arrow_down_float = 17301506; // 0x1080002 field public static final int arrow_up_float = 17301507; // 0x1080003 + field public static final int autofilled_highlight = 17301684; // 0x10800b4 field public static final int bottom_bar = 17301658; // 0x108009a field public static final int btn_default = 17301508; // 0x1080004 field public static final int btn_default_small = 17301509; // 0x1080005 @@ -3702,7 +3703,7 @@ package android.app { method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public void enterPictureInPictureMode(); method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs); - method public android.view.View findViewById(int); + method public <T extends android.view.View> T findViewById(int); method public void finish(); method public void finishActivity(int); method public void finishActivityFromChild(android.app.Activity, int); @@ -4509,7 +4510,7 @@ package android.app { method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public boolean dispatchTouchEvent(android.view.MotionEvent); method public boolean dispatchTrackballEvent(android.view.MotionEvent); - method public android.view.View findViewById(int); + method public <T extends android.view.View> T findViewById(int); method public android.app.ActionBar getActionBar(); method public final android.content.Context getContext(); method public android.view.View getCurrentFocus(); @@ -4621,6 +4622,7 @@ package android.app { method public android.database.Cursor query(android.app.DownloadManager.Query); method public int remove(long...); field public static final java.lang.String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE"; + field public static final java.lang.String ACTION_DOWNLOAD_COMPLETED = "android.intent.action.DOWNLOAD_COMPLETED"; field public static final java.lang.String ACTION_NOTIFICATION_CLICKED = "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"; field public static final java.lang.String ACTION_VIEW_DOWNLOADS = "android.intent.action.VIEW_DOWNLOADS"; field public static final java.lang.String COLUMN_BYTES_DOWNLOADED_SO_FAR = "bytes_so_far"; @@ -4930,7 +4932,7 @@ package android.app { method public abstract android.app.FragmentManager.BackStackEntry getBackStackEntryAt(int); method public abstract int getBackStackEntryCount(); method public abstract android.app.Fragment getFragment(android.os.Bundle, java.lang.String); - method public abstract java.util.Collection<android.app.Fragment> getFragments(); + method public abstract java.util.List<android.app.Fragment> getFragments(); method public abstract android.app.Fragment getPrimaryNavigationFragment(); method public void invalidateOptionsMenu(); method public abstract boolean isDestroyed(); @@ -5288,6 +5290,7 @@ package android.app { method public java.lang.String getGroup(); method public android.graphics.drawable.Icon getLargeIcon(); method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String); + method public java.lang.CharSequence getSettingsText(); method public java.lang.String getShortcutId(); method public android.graphics.drawable.Icon getSmallIcon(); method public java.lang.String getSortKey(); @@ -5333,6 +5336,8 @@ package android.app { field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big"; field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession"; field public static final java.lang.String EXTRA_MESSAGES = "android.messages"; + field public static final java.lang.String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID"; + field public static final java.lang.String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG"; field public static final java.lang.String EXTRA_PEOPLE = "android.people"; field public static final java.lang.String EXTRA_PICTURE = "android.picture"; field public static final java.lang.String EXTRA_PROGRESS = "android.progress"; @@ -5520,6 +5525,7 @@ package android.app { method public android.app.Notification.Builder setProgress(int, int, boolean); method public android.app.Notification.Builder setPublicVersion(android.app.Notification); method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]); + method public android.app.Notification.Builder setSettingsText(java.lang.CharSequence); method public android.app.Notification.Builder setShortcutId(java.lang.String); method public android.app.Notification.Builder setShowWhen(boolean); method public android.app.Notification.Builder setSmallIcon(int); @@ -7502,6 +7508,7 @@ package android.appwidget { method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int); method public android.os.Bundle getAppWidgetOptions(int); method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders(); + method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForPackage(java.lang.String, android.os.UserHandle); method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle); method public static android.appwidget.AppWidgetManager getInstance(android.content.Context); method public boolean isRequestPinAppWidgetSupported(); @@ -9796,6 +9803,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"; @@ -9808,7 +9816,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"; @@ -9867,6 +9878,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"; @@ -9876,6 +9889,7 @@ package android.content { field public static final java.lang.String ACTION_SEND = "android.intent.action.SEND"; field public static final java.lang.String ACTION_SENDTO = "android.intent.action.SENDTO"; field public static final java.lang.String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE"; + field public static final deprecated java.lang.String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE"; field public static final java.lang.String ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER"; field public static final java.lang.String ACTION_SHOW_APP_INFO = "android.intent.action.SHOW_APP_INFO"; field public static final java.lang.String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; @@ -9950,6 +9964,8 @@ package android.content { field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC"; field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT"; field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC"; + field public static final deprecated java.lang.String EXTRA_CDMA_DEFAULT_ROAMING_INDICATOR = "cdmaDefaultRoamingIndicator"; + field public static final deprecated java.lang.String EXTRA_CDMA_ROAMING_INDICATOR = "cdmaRoamingIndicator"; field public static final deprecated java.lang.String EXTRA_CHANGED_COMPONENT_NAME = "android.intent.extra.changed_component_name"; field public static final java.lang.String EXTRA_CHANGED_COMPONENT_NAME_LIST = "android.intent.extra.changed_component_name_list"; field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list"; @@ -9959,7 +9975,14 @@ package android.content { field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER"; field public static final java.lang.String EXTRA_CONTENT_ANNOTATIONS = "android.intent.extra.CONTENT_ANNOTATIONS"; + field public static final deprecated java.lang.String EXTRA_CSS_INDICATOR = "cssIndicator"; + field public static final deprecated java.lang.String EXTRA_DATA_OPERATOR_ALPHA_LONG = "data-operator-alpha-long"; + field public static final deprecated java.lang.String EXTRA_DATA_OPERATOR_ALPHA_SHORT = "data-operator-alpha-short"; + field public static final deprecated java.lang.String EXTRA_DATA_OPERATOR_NUMERIC = "data-operator-numeric"; + field public static final deprecated java.lang.String EXTRA_DATA_RADIO_TECH = "dataRadioTechnology"; + field public static final deprecated java.lang.String EXTRA_DATA_REG_STATE = "dataRegState"; field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED"; + field public static final deprecated java.lang.String EXTRA_DATA_ROAMING_TYPE = "dataRoamingType"; field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE"; field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2 field public static final int EXTRA_DOCK_STATE_DESK = 1; // 0x1 @@ -9968,6 +9991,7 @@ package android.content { field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0 field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP"; field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL"; + field public static final deprecated java.lang.String EXTRA_EMERGENCY_ONLY = "emergencyOnly"; field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS"; field public static final java.lang.String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET"; field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT"; @@ -9975,10 +9999,18 @@ package android.content { field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS"; field public static final java.lang.String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME"; field public static final java.lang.String EXTRA_INTENT = "android.intent.extra.INTENT"; + field public static final deprecated java.lang.String EXTRA_IS_DATA_ROAMING_FROM_REGISTRATION = "isDataRoamingFromRegistration"; + field public static final deprecated java.lang.String EXTRA_IS_USING_CARRIER_AGGREGATION = "isUsingCarrierAggregation"; field public static final java.lang.String EXTRA_KEY_EVENT = "android.intent.extra.KEY_EVENT"; field public static final java.lang.String EXTRA_LOCAL_ONLY = "android.intent.extra.LOCAL_ONLY"; + field public static final deprecated java.lang.String EXTRA_LTE_EARFCN_RSRP_BOOST = "LteEarfcnRsrpBoost"; + field public static final deprecated java.lang.String EXTRA_MANUAL = "manual"; field public static final java.lang.String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES"; + field public static final deprecated java.lang.String EXTRA_NETWORK_ID = "networkId"; field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE"; + field public static final deprecated java.lang.String EXTRA_OPERATOR_ALPHA_LONG = "operator-alpha-long"; + field public static final deprecated java.lang.String EXTRA_OPERATOR_ALPHA_SHORT = "operator-alpha-short"; + field public static final deprecated java.lang.String EXTRA_OPERATOR_NUMERIC = "operator-numeric"; field public static final java.lang.String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID"; field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI"; field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES"; @@ -10010,11 +10042,15 @@ package android.content { field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM"; field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT"; field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.intent.extra.SUBSCRIPTION_INDEX"; + field public static final deprecated java.lang.String EXTRA_SYSTEM_ID = "systemId"; field public static final java.lang.String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE"; field public static final java.lang.String EXTRA_TEXT = "android.intent.extra.TEXT"; field public static final java.lang.String EXTRA_TITLE = "android.intent.extra.TITLE"; field public static final java.lang.String EXTRA_UID = "android.intent.extra.UID"; field public static final java.lang.String EXTRA_USER = "android.intent.extra.USER"; + field public static final deprecated java.lang.String EXTRA_VOICE_RADIO_TECH = "radioTechnology"; + field public static final deprecated java.lang.String EXTRA_VOICE_REG_STATE = "voiceRegState"; + field public static final deprecated java.lang.String EXTRA_VOICE_ROAMING_TYPE = "voiceRoamingType"; field public static final int FILL_IN_ACTION = 1; // 0x1 field public static final int FILL_IN_CATEGORIES = 4; // 0x4 field public static final int FILL_IN_CLIP_DATA = 128; // 0x80 @@ -22654,6 +22690,7 @@ package android.media { method public int getContentType(); method public int getFlags(); method public int getUsage(); + method public static deprecated int getVolumeControlStream(android.media.AudioAttributes); method public int getVolumeControlStream(); method public void writeToParcel(android.os.Parcel, int); field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3 @@ -26501,85 +26538,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 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_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 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 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"; } @@ -26667,22 +26625,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; + 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.BaseProgramColumns { + 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; @@ -26711,28 +26763,122 @@ 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 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 @@ -27578,13 +27724,11 @@ package android.net { public final class IpSecManager { method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException; method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException; - method public void applyTunnelModeTransform(android.net.Network, android.net.IpSecTransform); method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform); method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform); - method public void removeTunnelModeTransform(android.net.Network, android.net.IpSecTransform); - method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; + method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 } @@ -27615,12 +27759,10 @@ package android.net { public static class IpSecTransform.Builder { ctor public IpSecTransform.Builder(android.content.Context); method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; - method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, java.net.InetAddress); method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm); method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm); method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int); method public android.net.IpSecTransform.Builder setNattKeepalive(int); - method public android.net.IpSecTransform.Builder setSpi(int, int); method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex); method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network); } @@ -37320,14 +37462,15 @@ package android.provider { public static final class FontsContract.Columns implements android.provider.BaseColumns { ctor public FontsContract.Columns(); + field public static final java.lang.String ITALIC = "font_italic"; field public static final java.lang.String RESULT_CODE = "result_code"; field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1 field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2 field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3 field public static final int RESULT_CODE_OK = 0; // 0x0 - field public static final java.lang.String STYLE = "font_style"; field public static final java.lang.String TTC_INDEX = "font_ttc_index"; field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings"; + field public static final java.lang.String WEIGHT = "font_weight"; } public final deprecated class LiveFolders implements android.provider.BaseColumns { @@ -39976,7 +40119,7 @@ package android.service.autofill { ctor public FillResponse.Builder(); method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset); method public android.service.autofill.FillResponse build(); - method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender, android.widget.RemoteViews); + method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews); method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle); method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo); } @@ -39992,8 +40135,10 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR; field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2 field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3 + field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5; // 0x5 field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0 field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1 + field public static final int SAVE_DATA_TYPE_USERNAME = 4; // 0x4 } public static final class SaveInfo.Builder { @@ -40219,7 +40364,6 @@ package android.service.notification { method public int getUser(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; - field public static final java.lang.String KEY_CHANNEL_ID = "key_channel_id"; field public static final java.lang.String KEY_PEOPLE = "key_people"; field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria"; } @@ -40274,14 +40418,10 @@ package android.service.notification { ctor public NotificationAssistantService(); method public final void adjustNotification(android.service.notification.Adjustment); method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>); - method public void createNotificationChannel(java.lang.String, android.app.NotificationChannel); - method public void deleteNotificationChannel(java.lang.String, java.lang.String); - method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String); method public final android.os.IBinder onBind(android.content.Intent); method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification); method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String); method public final void unsnoozeNotification(java.lang.String); - method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel); field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; } @@ -41802,6 +41942,7 @@ package android.telecom { method public java.lang.String getCallerDisplayName(); method public int getCallerDisplayNamePresentation(); method public final long getConnectTimeMillis(); + method public long getCreationTimeMillis(); method public android.telecom.DisconnectCause getDisconnectCause(); method public android.os.Bundle getExtras(); method public android.telecom.GatewayInfo getGatewayInfo(); @@ -44675,6 +44816,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 { @@ -44876,7 +45019,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); @@ -49956,7 +50099,7 @@ package android.view { method public void clearFlags(int); method public abstract void closeAllPanels(); method public abstract void closePanel(int); - method public android.view.View findViewById(int); + method public <T extends android.view.View> T findViewById(int); method public boolean getAllowEnterTransitionOverlap(); method public boolean getAllowReturnTransitionOverlap(); method public final android.view.WindowManager.LayoutParams getAttributes(); @@ -52166,7 +52309,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(); @@ -52221,7 +52364,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); @@ -52549,12 +52692,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); @@ -55054,7 +55197,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(); @@ -55167,7 +55310,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-removed.txt b/api/system-removed.txt index 1bafe96c1e6e..1effe9cead69 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -290,6 +290,10 @@ package android.provider { field public static final deprecated java.lang.String TIMESTAMP = "timestamp"; } + public static final class FontsContract.Columns implements android.provider.BaseColumns { + field public static final java.lang.String STYLE = "font_style"; + } + public static final class Settings.Global extends android.provider.Settings.NameValueTable { field public static final deprecated java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync"; } diff --git a/api/test-current.txt b/api/test-current.txt index e9ed54466dfc..d1dc37ccb904 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1609,6 +1609,7 @@ package android { field public static final int alert_light_frame = 17301505; // 0x1080001 field public static final int arrow_down_float = 17301506; // 0x1080002 field public static final int arrow_up_float = 17301507; // 0x1080003 + field public static final int autofilled_highlight = 17301684; // 0x10800b4 field public static final int bottom_bar = 17301658; // 0x108009a field public static final int btn_default = 17301508; // 0x1080004 field public static final int btn_default_small = 17301509; // 0x1080005 @@ -3580,7 +3581,7 @@ package android.app { method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public void enterPictureInPictureMode(); method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs); - method public android.view.View findViewById(int); + method public <T extends android.view.View> T findViewById(int); method public void finish(); method public void finishActivity(int); method public void finishActivityFromChild(android.app.Activity, int); @@ -4370,7 +4371,7 @@ package android.app { method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public boolean dispatchTouchEvent(android.view.MotionEvent); method public boolean dispatchTrackballEvent(android.view.MotionEvent); - method public android.view.View findViewById(int); + method public <T extends android.view.View> T findViewById(int); method public android.app.ActionBar getActionBar(); method public final android.content.Context getContext(); method public android.view.View getCurrentFocus(); @@ -4783,7 +4784,7 @@ package android.app { method public abstract android.app.FragmentManager.BackStackEntry getBackStackEntryAt(int); method public abstract int getBackStackEntryCount(); method public abstract android.app.Fragment getFragment(android.os.Bundle, java.lang.String); - method public abstract java.util.Collection<android.app.Fragment> getFragments(); + method public abstract java.util.List<android.app.Fragment> getFragments(); method public abstract android.app.Fragment getPrimaryNavigationFragment(); method public void invalidateOptionsMenu(); method public abstract boolean isDestroyed(); @@ -5128,6 +5129,7 @@ package android.app { method public java.lang.String getChannel(); method public java.lang.String getGroup(); method public android.graphics.drawable.Icon getLargeIcon(); + method public java.lang.CharSequence getSettingsText(); method public java.lang.String getShortcutId(); method public android.graphics.drawable.Icon getSmallIcon(); method public java.lang.String getSortKey(); @@ -5172,6 +5174,8 @@ package android.app { field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big"; field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession"; field public static final java.lang.String EXTRA_MESSAGES = "android.messages"; + field public static final java.lang.String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID"; + field public static final java.lang.String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG"; field public static final java.lang.String EXTRA_PEOPLE = "android.people"; field public static final java.lang.String EXTRA_PICTURE = "android.picture"; field public static final java.lang.String EXTRA_PROGRESS = "android.progress"; @@ -5357,6 +5361,7 @@ package android.app { method public android.app.Notification.Builder setProgress(int, int, boolean); method public android.app.Notification.Builder setPublicVersion(android.app.Notification); method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]); + method public android.app.Notification.Builder setSettingsText(java.lang.CharSequence); method public android.app.Notification.Builder setShortcutId(java.lang.String); method public android.app.Notification.Builder setShowWhen(boolean); method public android.app.Notification.Builder setSmallIcon(int); @@ -7074,6 +7079,7 @@ package android.appwidget { method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int); method public android.os.Bundle getAppWidgetOptions(int); method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders(); + method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForPackage(java.lang.String, android.os.UserHandle); method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle); method public static android.appwidget.AppWidgetManager getInstance(android.content.Context); method public boolean isRequestPinAppWidgetSupported(); @@ -21027,6 +21033,7 @@ package android.media { method public int getContentType(); method public int getFlags(); method public int getUsage(); + method public static deprecated int getVolumeControlStream(android.media.AudioAttributes); method public int getVolumeControlStream(); method public void writeToParcel(android.os.Parcel, int); field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3 @@ -24669,85 +24676,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 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_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 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 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"; } @@ -24834,22 +24762,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; + 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.BaseProgramColumns { + 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; @@ -24878,28 +24900,122 @@ 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 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 @@ -25560,7 +25676,7 @@ package android.net { method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform); method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform); - method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; + method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 } @@ -25594,7 +25710,6 @@ package android.net { method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm); method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm); method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int); - method public android.net.IpSecTransform.Builder setSpi(int, int); method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex); } @@ -34551,14 +34666,15 @@ package android.provider { public static final class FontsContract.Columns implements android.provider.BaseColumns { ctor public FontsContract.Columns(); + field public static final java.lang.String ITALIC = "font_italic"; field public static final java.lang.String RESULT_CODE = "result_code"; field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1 field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2 field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3 field public static final int RESULT_CODE_OK = 0; // 0x0 - field public static final java.lang.String STYLE = "font_style"; field public static final java.lang.String TTC_INDEX = "font_ttc_index"; field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings"; + field public static final java.lang.String WEIGHT = "font_weight"; } public final deprecated class LiveFolders implements android.provider.BaseColumns { @@ -37099,7 +37215,7 @@ package android.service.autofill { ctor public FillResponse.Builder(); method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset); method public android.service.autofill.FillResponse build(); - method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender, android.widget.RemoteViews); + method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews); method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle); method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo); } @@ -37115,8 +37231,10 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR; field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2 field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3 + field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5; // 0x5 field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0 field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1 + field public static final int SAVE_DATA_TYPE_USERNAME = 4; // 0x4 } public static final class SaveInfo.Builder { @@ -37342,7 +37460,6 @@ package android.service.notification { method public int getUser(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; - field public static final java.lang.String KEY_CHANNEL_ID = "key_channel_id"; field public static final java.lang.String KEY_PEOPLE = "key_people"; field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria"; } @@ -37397,14 +37514,10 @@ package android.service.notification { ctor public NotificationAssistantService(); method public final void adjustNotification(android.service.notification.Adjustment); method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>); - method public void createNotificationChannel(java.lang.String, android.app.NotificationChannel); - method public void deleteNotificationChannel(java.lang.String, java.lang.String); - method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String); method public final android.os.IBinder onBind(android.content.Intent); method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification); method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String); method public final void unsnoozeNotification(java.lang.String); - method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel); field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; } @@ -38844,6 +38957,7 @@ package android.telecom { method public java.lang.String getCallerDisplayName(); method public int getCallerDisplayNamePresentation(); method public final long getConnectTimeMillis(); + method public long getCreationTimeMillis(); method public android.telecom.DisconnectCause getDisconnectCause(); method public android.os.Bundle getExtras(); method public android.telecom.GatewayInfo getGatewayInfo(); @@ -41421,6 +41535,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 { @@ -41622,7 +41738,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); @@ -45924,6 +46040,7 @@ package android.view { method public void setAnimation(android.view.animation.Animation); method public void setAutofillHints(java.lang.String...); method public void setAutofillMode(int); + method public void setAutofilled(boolean); method public void setBackground(android.graphics.drawable.Drawable); method public void setBackgroundColor(int); method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable); @@ -46875,7 +46992,7 @@ package android.view { method public void clearFlags(int); method public abstract void closeAllPanels(); method public abstract void closePanel(int); - method public android.view.View findViewById(int); + method public <T extends android.view.View> T findViewById(int); method public boolean getAllowEnterTransitionOverlap(); method public boolean getAllowReturnTransitionOverlap(); method public final android.view.WindowManager.LayoutParams getAttributes(); @@ -49002,7 +49119,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(); @@ -49051,7 +49168,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); @@ -49342,12 +49459,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); @@ -51615,7 +51732,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(); @@ -51728,7 +51845,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-removed.txt b/api/test-removed.txt index 42b2ae61e5cf..d20c08cf7a8f 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -296,6 +296,10 @@ package android.provider { field public static final deprecated java.lang.String TIMESTAMP = "timestamp"; } + public static final class FontsContract.Columns implements android.provider.BaseColumns { + field public static final java.lang.String STYLE = "font_style"; + } + public static final class Settings.Global extends android.provider.Settings.NameValueTable { field public static final deprecated java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync"; } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 2435ffadd879..7394490fc8d4 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -370,7 +370,8 @@ bool BootAnimation::threadLoop() bool BootAnimation::android() { - ALOGD("BootAnimationShownTiming start time: %" PRId64 "ms", elapsedRealtime()); + ALOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", + elapsedRealtime()); initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png"); initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png"); @@ -896,7 +897,8 @@ bool BootAnimation::playAnimation(const Animation& animation) const int animationX = (mWidth - animation.width) / 2; const int animationY = (mHeight - animation.height) / 2; - ALOGD("BootAnimationShownTiming start time: %" PRId64 "ms", elapsedRealtime()); + ALOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", + elapsedRealtime()); for (size_t i=0 ; i<pcount ; i++) { const Animation::Part& part(animation.parts[i]); const size_t fcount = part.frames.size(); diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java index db17b28b8182..ce114fd02475 100644 --- a/cmds/bu/src/com/android/commands/bu/Backup.java +++ b/cmds/bu/src/com/android/commands/bu/Backup.java @@ -101,8 +101,10 @@ public final class Backup { doCompress = true; } else if ("-nocompress".equals(arg)) { doCompress = false; - } else if ("-includekeyvalue".equals(arg)) { + } else if ("-keyvalue".equals(arg)) { doKeyValue = true; + } else if ("-nokeyvalue".equals(arg)) { + doKeyValue = false; } else { Log.w(TAG, "Unknown backup flag " + arg); continue; diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java index 35011b5f3ec3..844258515561 100644 --- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java +++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java @@ -40,8 +40,8 @@ import com.android.internal.R; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -98,11 +98,10 @@ public class ChooseTypeAndAccountActivity extends Activity "alwaysPromptForAccount"; /** - * If set then this string willb e used as the description rather than + * If set then this string will be used as the description rather than * the default. */ - public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE = - "descriptionTextOverride"; + public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE = "descriptionTextOverride"; public static final int REQUEST_NULL = 0; public static final int REQUEST_CHOOSE_TYPE = 1; @@ -112,7 +111,8 @@ public class ChooseTypeAndAccountActivity extends Activity private static final String KEY_INSTANCE_STATE_EXISTING_ACCOUNTS = "existingAccounts"; private static final String KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME = "selectedAccountName"; private static final String KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT = "selectedAddAccount"; - private static final String KEY_INSTANCE_STATE_ACCOUNT_LIST = "accountAndVisibilityList"; + private static final String KEY_INSTANCE_STATE_ACCOUNTS_LIST = "accountsList"; + private static final String KEY_INSTANCE_STATE_VISIBILITY_LIST = "visibilityList"; private static final int SELECTED_ITEM_NONE = -1; @@ -122,7 +122,7 @@ public class ChooseTypeAndAccountActivity extends Activity private boolean mSelectedAddNewAccount = false; private String mDescriptionOverride; - private Map<Account, Integer> mAccounts; + private LinkedHashMap<Account, Integer> mAccounts; // TODO Redesign flow to show NOT_VISIBLE accounts // and display a warning if they are selected. // Currently NOT_VISBILE accounts are not shown at all. @@ -164,6 +164,10 @@ public class ChooseTypeAndAccountActivity extends Activity // save some items we use frequently final Intent intent = getIntent(); + mSetOfAllowableAccounts = getAllowableAccountSet(intent); + mSetOfRelevantAccountTypes = getReleventAccountTypes(intent); + mDescriptionOverride = intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE); + if (savedInstanceState != null) { mPendingRequest = savedInstanceState.getInt(KEY_INSTANCE_STATE_PENDING_REQUEST); mExistingAccounts = @@ -174,8 +178,15 @@ public class ChooseTypeAndAccountActivity extends Activity savedInstanceState.getString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME); mSelectedAddNewAccount = savedInstanceState.getBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false); - mAccounts = (Map<Account, Integer>) savedInstanceState - .getSerializable(KEY_INSTANCE_STATE_ACCOUNT_LIST); + // restore mAccounts + Parcelable[] accounts = + savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_ACCOUNTS_LIST); + ArrayList<Integer> visibility = + savedInstanceState.getIntegerArrayList(KEY_INSTANCE_STATE_VISIBILITY_LIST); + mAccounts = new LinkedHashMap<>(); + for (int i = 0; i < accounts.length; i++) { + mAccounts.put((Account) accounts[i], visibility.get(i)); + } } else { mPendingRequest = REQUEST_NULL; mExistingAccounts = null; @@ -185,20 +196,21 @@ public class ChooseTypeAndAccountActivity extends Activity if (selectedAccount != null) { mSelectedAccountName = selectedAccount.name; } + mAccounts = getAcceptableAccountChoices(AccountManager.get(this)); } if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "selected account name is " + mSelectedAccountName); } + mPossiblyVisibleAccounts = new ArrayList<>(mAccounts.size()); + for (Map.Entry<Account, Integer> entry : mAccounts.entrySet()) { + if (AccountManager.VISIBILITY_NOT_VISIBLE != entry.getValue()) { + mPossiblyVisibleAccounts.add(entry.getKey()); + } + } - mSetOfAllowableAccounts = getAllowableAccountSet(intent); - mSetOfRelevantAccountTypes = getReleventAccountTypes(intent); - mDescriptionOverride = intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE); - - mAccounts = getAcceptableAccountChoices(AccountManager.get(this)); - if (mAccounts.isEmpty() - && mDisallowAddAccounts) { + if (mPossiblyVisibleAccounts.isEmpty() && mDisallowAddAccounts) { requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.app_not_authorized); mDontShowPicker = true; @@ -216,7 +228,7 @@ public class ChooseTypeAndAccountActivity extends Activity if (mPendingRequest == REQUEST_NULL) { // If there are no relevant accounts and only one relevant account type go directly to // add account. Otherwise let the user choose. - if (mAccounts.isEmpty()) { + if (mPossiblyVisibleAccounts.isEmpty()) { setNonLabelThemeAndCallSuperCreate(savedInstanceState); if (mSetOfRelevantAccountTypes.size() == 1) { runAddAccountForAuthenticator(mSetOfRelevantAccountTypes.iterator().next()); @@ -226,12 +238,6 @@ public class ChooseTypeAndAccountActivity extends Activity } } - mPossiblyVisibleAccounts = new ArrayList<>(mAccounts.size()); - for (Map.Entry<Account, Integer> entry : mAccounts.entrySet()) { - if (AccountManager.VISIBILITY_NOT_VISIBLE != entry.getValue()) { - mPossiblyVisibleAccounts.add(entry.getKey()); - } - } String[] listItems = getListOfDisplayableOptions(mPossiblyVisibleAccounts); mSelectedItemIndex = getItemIndexToSelect(mPossiblyVisibleAccounts, mSelectedAccountName, mSelectedAddNewAccount); @@ -270,10 +276,16 @@ public class ChooseTypeAndAccountActivity extends Activity mPossiblyVisibleAccounts.get(mSelectedItemIndex).name); } } - // should be HashMap by default. - HashMap<Account, Integer> accountsHashMap = (mAccounts instanceof HashMap) - ? (HashMap) mAccounts : new HashMap<Account, Integer>(mAccounts); - outState.putSerializable(KEY_INSTANCE_STATE_ACCOUNT_LIST, accountsHashMap); + // save mAccounts + Parcelable[] accounts = new Parcelable[mAccounts.size()]; + ArrayList<Integer> visibility = new ArrayList<>(mAccounts.size()); + int i = 0; + for (Map.Entry<Account, Integer> e : mAccounts.entrySet()) { + accounts[i++] = e.getKey(); + visibility.add(e.getValue()); + } + outState.putParcelableArray(KEY_INSTANCE_STATE_ACCOUNTS_LIST, accounts); + outState.putIntegerArrayList(KEY_INSTANCE_STATE_VISIBILITY_LIST, visibility); } public void onCancelButtonClicked(View view) { @@ -308,7 +320,7 @@ public class ChooseTypeAndAccountActivity extends Activity if (resultCode == RESULT_CANCELED) { // if canceling out of addAccount and the original state caused us to skip this, // finish this activity - if (mAccounts.isEmpty()) { + if (mPossiblyVisibleAccounts.isEmpty()) { setResult(Activity.RESULT_CANCELED); finish(); } @@ -428,18 +440,20 @@ public class ChooseTypeAndAccountActivity extends Activity private void setResultAndFinish(final String accountName, final String accountType) { // Mark account as visible since user chose it. Account account = new Account(accountName, accountType); - Integer oldVisibility = mAccounts.get(account); - // oldVisibility is null if new account was added - if (oldVisibility == null) { - Map<Account, Integer> accountsAndVisibility = AccountManager.get(this) - .getAccountsAndVisibilityForPackage(mCallingPackage, null /* type */); - oldVisibility = accountsAndVisibility.get(account); - } + Integer oldVisibility = + AccountManager.get(this).getAccountVisibility(account, mCallingPackage); if (oldVisibility != null && oldVisibility == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE) { AccountManager.get(this).setAccountVisibility(account, mCallingPackage, AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); } + + if (oldVisibility != null && oldVisibility == AccountManager.VISIBILITY_NOT_VISIBLE) { + // Added account is not visible to caller. + setResult(Activity.RESULT_CANCELED); + finish(); + return; + } Bundle bundle = new Bundle(); bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName); bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType); @@ -448,6 +462,7 @@ public class ChooseTypeAndAccountActivity extends Activity Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: selected account " + accountName + ", " + accountType); } + finish(); } @@ -509,22 +524,24 @@ public class ChooseTypeAndAccountActivity extends Activity * that don't match the allowable types, if provided, or that don't match the allowable * accounts, if provided. */ - private Map<Account, Integer> getAcceptableAccountChoices(AccountManager accountManager) { - Map<Account, Integer> accountsAndVisibility = - accountManager.getAccountsAndVisibilityForPackage(mCallingPackage, null /* type */); - - Map<Account, Integer> accountsToPopulate = - new HashMap<Account, Integer>(accountsAndVisibility.size()); - for (Map.Entry<Account, Integer> entry : accountsAndVisibility.entrySet()) { + private LinkedHashMap<Account, Integer> getAcceptableAccountChoices(AccountManager accountManager) { + Map<Account, Integer> accountsAndVisibilityForCaller = + accountManager.getAccountsAndVisibilityForPackage(mCallingPackage, null); + Account[] allAccounts = accountManager.getAccounts(); + LinkedHashMap<Account, Integer> accountsToPopulate = + new LinkedHashMap<>(accountsAndVisibilityForCaller.size()); + for (Account account : allAccounts) { if (mSetOfAllowableAccounts != null - && !mSetOfAllowableAccounts.contains(entry.getKey())) { + && !mSetOfAllowableAccounts.contains(account)) { continue; } if (mSetOfRelevantAccountTypes != null - && !mSetOfRelevantAccountTypes.contains(entry.getKey().type)) { + && !mSetOfRelevantAccountTypes.contains(account.type)) { continue; } - accountsToPopulate.put(entry.getKey(), entry.getValue()); + if (accountsAndVisibilityForCaller.get(account) != null) { + accountsToPopulate.put(account, accountsAndVisibilityForCaller.get(account)); + } } return accountsToPopulate; } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d432160ccfbf..2376a95526aa 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -724,6 +724,7 @@ public class Activity extends ContextThemeWrapper public static final int FINISH_TASK_WITH_ACTIVITY = 2; static final String FRAGMENTS_TAG = "android:fragments"; + static final String AUTOFILL_RESET_NEEDED_TAG = "android:autofillResetNeeded"; private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState"; private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds"; @@ -1057,6 +1058,12 @@ public class Activity extends ContextThemeWrapper * @see #onSaveInstanceState */ protected void onRestoreInstanceState(Bundle savedInstanceState) { + mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED_TAG, false); + + if (mAutoFillResetNeeded) { + getSystemService(AutofillManager.class).onRestoreInstanceState(savedInstanceState); + } + if (mWindow != null) { Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG); if (windowState != null) { @@ -1502,6 +1509,10 @@ public class Activity extends ContextThemeWrapper if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p); } + if (mAutoFillResetNeeded) { + outState.putBoolean(AUTOFILL_RESET_NEEDED_TAG, mAutoFillResetNeeded); + getSystemService(AutofillManager.class).onSaveInstanceState(outState); + } getApplication().dispatchActivitySaveInstanceState(this, outState); } @@ -2443,13 +2454,20 @@ public class Activity extends ContextThemeWrapper } /** - * Finds a view that was identified by the id attribute from the XML that - * was processed in {@link #onCreate}. + * Finds a view that was identified by the {@code android:id} XML attribute + * that was processed in {@link #onCreate}. + * <p> + * <strong>Note:</strong> In most cases -- depending on compiler support -- + * the resulting view is automatically cast to the target class type. If + * the target class type is unconstrained, an explicit cast may be + * necessary. * - * @return The view if found or null otherwise. + * @param id the ID to search for + * @return a view with given ID if found, or {@code null} otherwise + * @see View#findViewById(int) */ @Nullable - public View findViewById(@IdRes int id) { + public <T extends View> T findViewById(@IdRes int id) { return getWindow().findViewById(id); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d1d462c2fbe8..7299d6bd0ed7 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -38,6 +38,7 @@ import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.res.AssetManager; @@ -869,16 +870,20 @@ public final class ActivityThread { sendMessage(H.UNBIND_SERVICE, s); } - public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId, - int flags ,Intent args) { - ServiceArgsData s = new ServiceArgsData(); - s.token = token; - s.taskRemoved = taskRemoved; - s.startId = startId; - s.flags = flags; - s.args = args; + public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) { + List<ServiceStartArgs> list = args.getList(); + + for (int i = 0; i < list.size(); i++) { + ServiceStartArgs ssa = list.get(i); + ServiceArgsData s = new ServiceArgsData(); + s.token = token; + s.taskRemoved = ssa.taskRemoved; + s.startId = ssa.startId; + s.flags = ssa.flags; + s.args = ssa.args; - sendMessage(H.SERVICE_ARGS, s); + sendMessage(H.SERVICE_ARGS, s); + } } public final void scheduleStopService(IBinder token) { diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 72ccf72d2d05..943c572eba9d 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -498,14 +498,22 @@ public class Dialog implements DialogInterface, Window.Callback, } /** - * Finds a child view with the given identifier. Returns null if the - * specified child view does not exist or the dialog has not yet been fully - * created (for example, via {@link #show()} or {@link #create()}). + * Finds the first descendant view with the given ID or {@code null} if the + * ID is invalid (< 0), there is no matching view in the hierarchy, or the + * dialog has not yet been fully created (for example, via {@link #show()} + * or {@link #create()}). + * <p> + * <strong>Note:</strong> In most cases -- depending on compiler support -- + * the resulting view is automatically cast to the target class type. If + * the target class type is unconstrained, an explicit cast may be + * necessary. * - * @param id the identifier of the view to find - * @return The view with the given id or null. + * @param id the ID to search for + * @return a view with given ID if found, or {@code null} otherwise + * @see View#findViewById(int) */ - public @Nullable View findViewById(@IdRes int id) { + @Nullable + public <T extends View> T findViewById(@IdRes int id) { return mWindow.findViewById(id); } diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 462f66f38def..b89c16539b4c 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -18,6 +18,7 @@ package android.app; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SystemApi; import android.annotation.SdkConstant.SdkConstantType; import android.content.ContentResolver; import android.content.ContentUris; @@ -321,6 +322,11 @@ public class DownloadManager { */ public static final String EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS = "extra_click_download_ids"; + /** {@hide} */ + @SystemApi + public static final String ACTION_DOWNLOAD_COMPLETED = + "android.intent.action.DOWNLOAD_COMPLETED"; + /** * columns to request from DownloadProvider. * @hide diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 3102a93790b2..a3c123f74cc4 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -2608,6 +2608,12 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene } } + void noteStateNotSaved() { + if (mChildFragmentManager != null) { + mChildFragmentManager.noteStateNotSaved(); + } + } + @Deprecated void performMultiWindowModeChanged(boolean isInMultiWindowMode) { onMultiWindowModeChanged(isInMultiWindowMode); diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 279b9003f8c0..75d62956671e 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -314,14 +314,17 @@ public abstract class FragmentManager { public abstract Fragment getFragment(Bundle bundle, String key); /** - * Get a collection of all fragments that are currently added to the FragmentManager. + * Get a list of all fragments that are currently added to the FragmentManager. * This may include those that are hidden as well as those that are shown. * This will not include any fragments only in the back stack, or fragments that * are detached or removed. + * <p> + * The order of the fragments in the list is the order in which they were + * added or attached. * - * @return A collection of all fragments that are added to the FragmentManager. + * @return A list of all fragments that are added to the FragmentManager. */ - public abstract Collection<Fragment> getFragments(); + public abstract List<Fragment> getFragments(); /** * Save the current instance state of the given Fragment. This can be @@ -695,6 +698,9 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate // This is dangerous, but we want to keep from breaking old applications. boolean mAllowOldReentrantBehavior; + // Saved FragmentManagerNonConfig during saveAllState() and cleared in noteStateNotSaved() + FragmentManagerNonConfig mSavedNonConfig; + Runnable mExecCommit = new Runnable() { @Override public void run() { @@ -907,12 +913,12 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } @Override - public Collection<Fragment> getFragments() { + public List<Fragment> getFragments() { if (mAdded == null) { return Collections.EMPTY_LIST; } synchronized (mAdded) { - return (Collection<Fragment>) mAdded.clone(); + return (List<Fragment>) mAdded.clone(); } } @@ -1128,8 +1134,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. @@ -2513,6 +2524,35 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } FragmentManagerNonConfig retainNonConfig() { + setRetaining(mSavedNonConfig); + return mSavedNonConfig; + } + + /** + * Recurse the FragmentManagerNonConfig fragments and set the mRetaining to true. This + * was previously done while saving the non-config state, but that has been moved to + * {@link #saveNonConfig()} called from {@link #saveAllState()}. If mRetaining is set too + * early, the fragment won't be destroyed when the FragmentManager is destroyed. + */ + private static void setRetaining(FragmentManagerNonConfig nonConfig) { + if (nonConfig == null) { + return; + } + List<Fragment> fragments = nonConfig.getFragments(); + if (fragments != null) { + for (Fragment fragment : fragments) { + fragment.mRetaining = true; + } + } + List<FragmentManagerNonConfig> children = nonConfig.getChildNonConfigs(); + if (children != null) { + for (FragmentManagerNonConfig child : children) { + setRetaining(child); + } + } + } + + void saveNonConfig() { ArrayList<Fragment> fragments = null; ArrayList<FragmentManagerNonConfig> childFragments = null; if (mActive != null) { @@ -2524,13 +2564,13 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate fragments = new ArrayList<>(); } fragments.add(f); - f.mRetaining = true; f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f); } boolean addedChild = false; if (f.mChildFragmentManager != null) { - FragmentManagerNonConfig child = f.mChildFragmentManager.retainNonConfig(); + f.mChildFragmentManager.saveNonConfig(); + FragmentManagerNonConfig child = f.mChildFragmentManager.mSavedNonConfig; if (child != null) { if (childFragments == null) { childFragments = new ArrayList<>(); @@ -2549,9 +2589,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } } if (fragments == null && childFragments == null) { - return null; + mSavedNonConfig = null; + } else { + mSavedNonConfig = new FragmentManagerNonConfig(fragments, childFragments); } - return new FragmentManagerNonConfig(fragments, childFragments); } void saveFragmentViewState(Fragment f) { @@ -2612,6 +2653,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate execPendingActions(); mStateSaved = true; + mSavedNonConfig = null; if (mActive == null || mActive.size() <= 0) { return null; @@ -2712,6 +2754,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate if (mPrimaryNav != null) { fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex; } + saveNonConfig(); return fms; } @@ -2887,9 +2930,17 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } public void noteStateNotSaved() { + mSavedNonConfig = null; mStateSaved = false; + final int addedCount = mAdded == null ? 0 : mAdded.size(); + for (int i = 0; i < addedCount; i++) { + Fragment fragment = mAdded.get(i); + if (fragment != null) { + fragment.noteStateNotSaved(); + } + } } - + public void dispatchCreate() { mStateSaved = false; dispatchMoveToState(Fragment.CREATED); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index f4d26fd7d9d8..079bbcdd4951 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -635,6 +635,11 @@ interface IActivityManager { */ int getLastResumedActivityUserId(); + /** + * Add a bare uid to the background restrictions whitelist. Only the system uid may call this. + */ + void backgroundWhitelistUid(int uid); + // WARNING: when these transactions are updated, check if they are any callers on the native // side. If so, make sure they are using the correct transaction ids and arguments. // If a transaction which will also be used on the native side is being inserted, add it diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 6c43fe3beca1..1b3c00b61539 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -25,6 +25,7 @@ import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.res.CompatibilityInfo; @@ -86,8 +87,7 @@ oneway interface IApplicationThread { in Bundle coreSettings, in String buildSerial); void scheduleExit(); void scheduleConfigurationChanged(in Configuration config); - void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId, - int flags, in Intent args); + void scheduleServiceArgs(IBinder token, in ParceledListSlice args); void updateTimeZone(); void processInBackground(); void scheduleBindService(IBinder token, diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 43cad5b2f729..61dacefd3f51 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -104,10 +104,6 @@ interface INotificationManager void applyEnqueuedAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment); void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment); void applyAdjustmentsFromAssistant(in INotificationListener token, in List<Adjustment> adjustments); - void createNotificationChannelFromAssistant(in INotificationListener token, String pkg, in NotificationChannel channel); - void updateNotificationChannelFromAssistant(in INotificationListener token, String pkg, in NotificationChannel channel); - void deleteNotificationChannelFromAssistant(in INotificationListener token, String pkg, String channelId); - ParceledListSlice getNotificationChannelsFromAssistant(in INotificationListener token, String pkg); void unsnoozeNotificationFromAssistant(in INotificationListener token, String key); ComponentName getEffectsSuppressor(); diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java index 2bdfa99fee47..88399e5d6695 100644 --- a/core/java/android/app/InstantAppResolverService.java +++ b/core/java/android/app/InstantAppResolverService.java @@ -21,6 +21,7 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.InstantAppResolveInfo; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -28,9 +29,12 @@ import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; import com.android.internal.os.SomeArgs; +import java.util.Arrays; import java.util.List; /** @@ -39,6 +43,9 @@ import java.util.List; */ @SystemApi public abstract class InstantAppResolverService extends Service { + private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE; + private static final String TAG = "PackageManager"; + /** @hide */ public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO"; /** @hide */ @@ -132,11 +139,19 @@ public abstract class InstantAppResolverService extends Service { @Deprecated void _onGetInstantAppResolveInfo(int[] digestPrefix, String token, InstantAppResolutionCallback callback) { + if (DEBUG_EPHEMERAL) { + Slog.d(TAG, "Instant resolver; getInstantAppResolveInfo;" + + " prefix: " + Arrays.toString(digestPrefix)); + } onGetInstantAppResolveInfo(digestPrefix, token, callback); } @Deprecated void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName, InstantAppResolutionCallback callback) { + if (DEBUG_EPHEMERAL) { + Slog.d(TAG, "Instant resolver; getInstantAppIntentFilter;" + + " prefix: " + Arrays.toString(digestPrefix)); + } onGetInstantAppIntentFilter(digestPrefix, token, callback); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 3d661353ffde..6d7486b5311e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -103,8 +103,7 @@ public class Notification implements Parcelable /** * An activity that provides a user interface for adjusting notification preferences for its - * containing application. Optional but recommended for apps that post - * {@link android.app.Notification Notifications}. + * containing application. */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES @@ -113,11 +112,25 @@ public class Notification implements Parcelable /** * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down - * what in app notifications settings should be shown. + * what settings should be shown in the target app. */ public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID"; /** + * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will + * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)} + * that can be used to narrow down what settings should be shown in the target app. + */ + public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG"; + + /** + * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will + * contain the id provided to {@link NotificationManager#notify(String, int, Notification)} + * that can be used to narrow down what settings should be shown in the target app. + */ + public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID"; + + /** * Use all default values (where applicable). */ public static final int DEFAULT_ALL = ~0; @@ -1082,6 +1095,7 @@ public class Notification implements Parcelable private long mTimeout; private String mShortcutId; + private CharSequence mSettingsText; /** * If this notification is being shown as a badge, always show as a number. @@ -1851,6 +1865,10 @@ public class Notification implements Parcelable } mBadgeIcon = parcel.readInt(); + + if (parcel.readInt() != 0) { + mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + } } @Override @@ -1960,6 +1978,9 @@ public class Notification implements Parcelable that.mChannelId = this.mChannelId; that.mTimeout = this.mTimeout; + that.mShortcutId = this.mShortcutId; + that.mBadgeIcon = this.mBadgeIcon; + that.mSettingsText = this.mSettingsText; if (!heavy) { that.lightenPayload(); // will clean out extras @@ -2229,6 +2250,13 @@ public class Notification implements Parcelable } parcel.writeInt(mBadgeIcon); + + if (mSettingsText != null) { + parcel.writeInt(1); + TextUtils.writeToParcel(mSettingsText, parcel, flags); + } else { + parcel.writeInt(0); + } } /** @@ -2458,6 +2486,14 @@ public class Notification implements Parcelable return mShortcutId; } + + /** + * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}. + */ + public CharSequence getSettingsText() { + return mSettingsText; + } + /** * The small icon representing this notification in the status bar and content view. * @@ -2887,6 +2923,24 @@ public class Notification implements Parcelable } /** + * Provides text that will appear as a link to your application's settings. + * + * <p>This text does not appear within notification {@link Style templates} but may + * appear when the user uses an affordance to learn more about the notification. + * Additionally, this text will not appear unless you provide a valid link target by + * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. + * + * <p>This text is meant to be concise description about what the user can customize + * when they click on this link. The recommended maximum length is 40 characters. + * @param text + * @return + */ + public Builder setSettingsText(CharSequence text) { + mN.mSettingsText = safeCharSequence(text); + return this; + } + + /** * Set the remote input history. * * This should be set to the most recent inputs that have been sent @@ -3793,9 +3847,9 @@ public class Notification implements Parcelable // Ambient view does not have these bindHeaderText(contentView); bindHeaderChronometerAndTime(contentView); - bindExpandButton(contentView); bindProfileBadge(contentView); } + bindExpandButton(contentView); } private void bindExpandButton(RemoteViews contentView) { diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 489a0f0975ae..d620a81e985b 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -945,7 +945,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.equals(assetPath)) { + if (impl != null && Objects.equals(key.mResDir, assetPath)) { if (!ArrayUtils.contains(key.mLibDirs, libAsset)) { final int newLibAssetCount = 1 + (key.mLibDirs != null ? key.mLibDirs.length : 0); diff --git a/core/java/android/view/autofill/AutoFillId.aidl b/core/java/android/app/ServiceStartArgs.aidl index fc57ce7acfc1..fd2cf0f93dc6 100644 --- a/core/java/android/view/autofill/AutoFillId.aidl +++ b/core/java/android/app/ServiceStartArgs.aidl @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2017, The Android Open Source Project +/* + * 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 + * 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, @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.view.autofill; +package android.app; -// @deprecated TODO(b/35956626): remove once clients use AutofillId -parcelable AutoFillId;
\ No newline at end of file +/** @hide */ +parcelable ServiceStartArgs; diff --git a/core/java/android/app/ServiceStartArgs.java b/core/java/android/app/ServiceStartArgs.java new file mode 100644 index 000000000000..f030cbaaecaf --- /dev/null +++ b/core/java/android/app/ServiceStartArgs.java @@ -0,0 +1,82 @@ +/* + * 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.app; + +import android.content.Intent; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Describes a Service.onStartCommand() request from the system. + * @hide + */ +public class ServiceStartArgs implements Parcelable { + final public boolean taskRemoved; + final public int startId; + final public int flags; + final public Intent args; + + public ServiceStartArgs(boolean _taskRemoved, int _startId, int _flags, Intent _args) { + taskRemoved = _taskRemoved; + startId = _startId; + flags = _flags; + args = _args; + } + + public String toString() { + return "ServiceStartArgs{taskRemoved=" + taskRemoved + ", startId=" + startId + + ", flags=0x" + Integer.toHexString(flags) + ", args=" + args + "}"; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(taskRemoved ? 1 : 0); + out.writeInt(startId); + out.writeInt(flags); + if (args != null) { + out.writeInt(1); + args.writeToParcel(out, 0); + } else { + out.writeInt(0); + } + } + + public static final Parcelable.Creator<ServiceStartArgs> CREATOR + = new Parcelable.Creator<ServiceStartArgs>() { + public ServiceStartArgs createFromParcel(Parcel in) { + return new ServiceStartArgs(in); + } + + public ServiceStartArgs[] newArray(int size) { + return new ServiceStartArgs[size]; + } + }; + + public ServiceStartArgs(Parcel in) { + taskRemoved = in.readInt() != 0; + startId = in.readInt(); + flags = in.readInt(); + if (in.readInt() != 0) { + args = Intent.CREATOR.createFromParcel(in); + } else { + args = null; + } + } +} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 6b05bdff5d1e..4572578ea608 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -74,7 +74,9 @@ import android.net.ConnectivityThread; import android.net.EthernetManager; import android.net.IConnectivityManager; import android.net.IEthernetManager; +import android.net.IIpSecService; import android.net.INetworkPolicyManager; +import android.net.IpSecManager; import android.net.NetworkPolicyManager; import android.net.NetworkScoreManager; import android.net.nsd.INsdManager; @@ -261,6 +263,15 @@ final class SystemServiceRegistry { return new ConnectivityManager(context, service); }}); + registerService(Context.IPSEC_SERVICE, IpSecManager.class, + new StaticServiceFetcher<IpSecManager>() { + @Override + public IpSecManager createService() { + IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE); + IIpSecService service = IIpSecService.Stub.asInterface(b); + return new IpSecManager(service); + }}); + registerService(Context.COUNTRY_DETECTOR, CountryDetector.class, new StaticServiceFetcher<CountryDetector>() { @Override diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 2f0a630bcd04..82ad8252c5a9 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -697,8 +697,8 @@ public class DevicePolicyManager { "android.app.extra.PROVISIONING_ORGANIZATION_NAME"; /** - * A String extra holding a url to the website of the device's provider. The website can be - * opened in a browser during provisioning. + * A String extra holding a url to the website of the device provider so the user can open it + * during provisioning. If the url is not HTTPS, an error will be shown. * * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} * @@ -2768,9 +2768,11 @@ public class DevicePolicyManager { * or clears the lockscreen password. * <p> * <em>This token is highly sensitive and should be treated at the same level as user - * credentials. In particular, NEVER store this token on device in plaintext, especially in - * Device-Encrypted storage if the token will be used to reset password on FBE devices before - * user unlocks. + * credentials. In particular, NEVER store this token on device in plaintext. Do not store + * the plaintext token in device-encrypted storage if it will be needed to reset password on + * file-based encryption devices before user unlocks. Consider carefully how any password token + * will be stored on your server and who will need access to them. Tokens may be the subject of + * legal access requests. * </em> * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 7d2db5c9f384..545aef5224b7 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -27,9 +27,6 @@ import android.view.ViewStructure.HtmlInfo; import android.view.ViewStructure.HtmlInfo.Builder; import android.view.WindowManager; import android.view.WindowManagerGlobal; -import android.view.autofill.AutoFillId; -import android.view.autofill.AutoFillType; -import android.view.autofill.AutoFillValue; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; @@ -602,6 +599,10 @@ public class AssistStructure implements Parcelable { boolean mSanitized; HtmlInfo mHtmlInfo; + // POJO used to override some autofill-related values when the node is parcelized. + // Not written to parcel. + AutofillOverlay mAutofillOverlay; + int mX; int mY; int mScrollX; @@ -759,6 +760,7 @@ public class AssistStructure implements Parcelable { boolean writeSensitive = true; int flags = mFlags & ~FLAGS_ALL_CONTROL; + if (mId != View.NO_ID) { flags |= FLAGS_HAS_ID; } @@ -813,6 +815,13 @@ public class AssistStructure implements Parcelable { // Remove 'checked' from sanitized autofill request. writtenFlags = flags & ~FLAGS_CHECKED; } + if (mAutofillOverlay != null) { + if (mAutofillOverlay.focused) { + writtenFlags |= ViewNode.FLAGS_FOCUSED; + } else { + writtenFlags &= ~ViewNode.FLAGS_FOCUSED; + } + } out.writeInt(writtenFlags); if ((flags&FLAGS_HAS_ID) != 0) { @@ -832,7 +841,14 @@ public class AssistStructure implements Parcelable { out.writeParcelable(mAutofillId, 0); out.writeInt(mAutofillType); out.writeStringArray(mAutofillHints); - final AutofillValue sanitizedValue = writeSensitive ? mAutofillValue : null; + final AutofillValue sanitizedValue; + if (mAutofillOverlay != null && mAutofillOverlay.value != null) { + sanitizedValue = mAutofillOverlay.value; + } else if (writeSensitive) { + sanitizedValue = mAutofillValue; + } else { + sanitizedValue = null; + } out.writeParcelable(sanitizedValue, 0); out.writeStringArray(mAutofillOptions); if (mHtmlInfo instanceof Parcelable) { @@ -920,15 +936,6 @@ public class AssistStructure implements Parcelable { } /** - * @hide - * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype - */ - @Deprecated - public AutoFillId getAutoFillId() { - return AutoFillId.forDaRealId(mAutofillId); - } - - /** * Gets the id that can be used to autofill the view contents. * * <p>It's only set when the {@link AssistStructure} is used for autofilling purposes, not @@ -939,26 +946,6 @@ public class AssistStructure implements Parcelable { } /** - * @hide - * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype() - */ - @Deprecated - public AutoFillType getAutoFillType() { - switch (getAutofillType()) { - case View.AUTOFILL_TYPE_TEXT: - return AutoFillType.forText(); - case View.AUTOFILL_TYPE_TOGGLE: - return AutoFillType.forToggle(); - case View.AUTOFILL_TYPE_LIST: - return AutoFillType.forList(); - case View.AUTOFILL_TYPE_DATE: - return AutoFillType.forDate(); - default: - return null; - } - } - - /** * Gets the the type of value that can be used to autofill the view contents. * * <p>It's only set when the {@link AssistStructure} is used for autofilling purposes, not @@ -982,15 +969,6 @@ public class AssistStructure implements Parcelable { } /** - * @hide - * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype - */ - @Deprecated - public AutoFillValue getAutoFillValue() { - return AutoFillValue.forDaRealValue(mAutofillValue); - } - - /** * Gets the the value of this view. * * <p>It's only set when the {@link AssistStructure} is used for autofilling purposes, not @@ -1000,6 +978,11 @@ public class AssistStructure implements Parcelable { return mAutofillValue; } + /** @hide **/ + public void setAutofillOverlay(AutofillOverlay overlay) { + mAutofillOverlay = overlay; + } + /** * Gets the options that can be used to autofill this structure. * @@ -1381,6 +1364,16 @@ public class AssistStructure implements Parcelable { } } + /** + * POJO used to override some autofill-related values when the node is parcelized. + * + * @hide + */ + static public class AutofillOverlay { + public boolean focused; + public AutofillValue value; + } + static class ViewNodeBuilder extends ViewStructure { final AssistStructure mAssist; final ViewNode mNode; diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 74a39e85e029..9f35e85b0665 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -710,9 +710,9 @@ public class AppWidgetManager { * user may have a corporate profile. In this case the parent user profile has a * child profile, the corporate one. * - * @param profile The profile for which to get providers. Passing null is equivaled - * to passing only the current user handle. - * @return The intalled providers. + * @param profile The profile for which to get providers. Passing null is equivalent + * to querying for only the calling user. + * @return The installed providers. * * @see android.os.Process#myUserHandle() * @see android.os.UserManager#getUserProfiles() @@ -722,7 +722,31 @@ public class AppWidgetManager { return Collections.emptyList(); } return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN, - profile); + profile, null); + } + + /** + * Gets the AppWidget providers for the given package and user profile. User + * profile can only be the current user or a profile of the current user. For + * example, the current user may have a corporate profile. In this case the + * parent user profile has a child profile, the corporate one. + * + * @param packageName The package for which to get providers. If null, this method is + * equivalent to {@link #getInstalledProvidersForProfile(UserHandle)}. + * @param profile The profile for which to get providers. Passing null is equivalent + * to querying for only the calling user. + * @return The installed providers. + * + * @see android.os.Process#myUserHandle() + * @see android.os.UserManager#getUserProfiles() + */ + public List<AppWidgetProviderInfo> getInstalledProvidersForPackage(@Nullable String packageName, + @Nullable UserHandle profile) { + if (mService == null) { + return Collections.emptyList(); + } + return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN, + profile, packageName); } /** @@ -733,7 +757,7 @@ public class AppWidgetManager { return Collections.emptyList(); } return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN, - null); + null, null); } /** @@ -752,7 +776,7 @@ public class AppWidgetManager { if (mService == null) { return Collections.emptyList(); } - return getInstalledProvidersForProfile(categoryFilter, null); + return getInstalledProvidersForProfile(categoryFilter, null, null); } /** @@ -766,6 +790,7 @@ public class AppWidgetManager { * @param profile A profile of the current user which to be queried. The user * is itself also a profile. If null, the providers only for the current user * are returned. + * @param packageName If specified, will only return providers from the given package. * @return The intalled providers. * * @see android.os.Process#myUserHandle() @@ -774,7 +799,7 @@ public class AppWidgetManager { * @hide */ public List<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter, - UserHandle profile) { + @Nullable UserHandle profile, @Nullable String packageName) { if (mService == null) { return Collections.emptyList(); } @@ -785,7 +810,7 @@ public class AppWidgetManager { try { ParceledListSlice<AppWidgetProviderInfo> providers = mService.getInstalledProvidersForProfile( - categoryFilter, profile.getIdentifier()); + categoryFilter, profile.getIdentifier(), packageName); if (providers == null) { return Collections.emptyList(); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index da887af52b2a..5415eb565b90 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 @@ -3479,6 +3506,261 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED"; /** + * Broadcast Action: indicate that the phone service state has changed. + * The intent will have the following extra values:</p> + * <p> + * @see #EXTRA_VOICE_REG_STATE + * @see #EXTRA_DATA_REG_STATE + * @see #EXTRA_VOICE_ROAMING_TYPE + * @see #EXTRA_DATA_ROAMING_TYPE + * @see #EXTRA_OPERATOR_ALPHA_LONG + * @see #EXTRA_OPERATOR_ALPHA_SHORT + * @see #EXTRA_OPERATOR_NUMERIC + * @see #EXTRA_DATA_OPERATOR_ALPHA_LONG + * @see #EXTRA_DATA_OPERATOR_ALPHA_SHORT + * @see #EXTRA_DATA_OPERATOR_NUMERIC + * @see #EXTRA_MANUAL + * @see #EXTRA_VOICE_RADIO_TECH + * @see #EXTRA_DATA_RADIO_TECH + * @see #EXTRA_CSS_INDICATOR + * @see #EXTRA_NETWORK_ID + * @see #EXTRA_SYSTEM_ID + * @see #EXTRA_CDMA_ROAMING_INDICATOR + * @see #EXTRA_CDMA_DEFAULT_ROAMING_INDICATOR + * @see #EXTRA_EMERGENCY_ONLY + * @see #EXTRA_IS_DATA_ROAMING_FROM_REGISTRATION + * @see #EXTRA_IS_USING_CARRIER_AGGREGATION + * @see #EXTRA_LTE_EARFCN_RSRP_BOOST + * + * <p class="note"> + * Requires the READ_PHONE_STATE permission. + * + * <p class="note">This is a protected intent that can only be sent by the system. + * @hide + */ + @Deprecated + @SystemApi + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE"; + + /** + * An int extra used with {@link #ACTION_SERVICE_STATE} which indicates voice registration + * state. + * @see android.telephony.ServiceState#STATE_EMERGENCY_ONLY + * @see android.telephony.ServiceState#STATE_IN_SERVICE + * @see android.telephony.ServiceState#STATE_OUT_OF_SERVICE + * @see android.telephony.ServiceState#STATE_POWER_OFF + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_VOICE_REG_STATE = "voiceRegState"; + + /** + * An int extra used with {@link #ACTION_SERVICE_STATE} which indicates data registration state. + * @see android.telephony.ServiceState#STATE_EMERGENCY_ONLY + * @see android.telephony.ServiceState#STATE_IN_SERVICE + * @see android.telephony.ServiceState#STATE_OUT_OF_SERVICE + * @see android.telephony.ServiceState#STATE_POWER_OFF + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_DATA_REG_STATE = "dataRegState"; + + /** + * An integer extra used with {@link #ACTION_SERVICE_STATE} which indicates the voice roaming + * type. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_VOICE_ROAMING_TYPE = "voiceRoamingType"; + + /** + * An integer extra used with {@link #ACTION_SERVICE_STATE} which indicates the data roaming + * type. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_DATA_ROAMING_TYPE = "dataRoamingType"; + + /** + * A string extra used with {@link #ACTION_SERVICE_STATE} which represents the current + * registered voice operator name in long alphanumeric format. + * {@code null} if the operator name is not known or unregistered. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_OPERATOR_ALPHA_LONG = "operator-alpha-long"; + + /** + * A string extra used with {@link #ACTION_SERVICE_STATE} which represents the current + * registered voice operator name in short alphanumeric format. + * {@code null} if the operator name is not known or unregistered. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_OPERATOR_ALPHA_SHORT = "operator-alpha-short"; + + /** + * A string extra used with {@link #ACTION_SERVICE_STATE} containing the MCC + * (Mobile Country Code, 3 digits) and MNC (Mobile Network code, 2-3 digits) for the mobile + * network. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_OPERATOR_NUMERIC = "operator-numeric"; + + /** + * A string extra used with {@link #ACTION_SERVICE_STATE} which represents the current + * registered data operator name in long alphanumeric format. + * {@code null} if the operator name is not known or unregistered. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_DATA_OPERATOR_ALPHA_LONG = "data-operator-alpha-long"; + + /** + * A string extra used with {@link #ACTION_SERVICE_STATE} which represents the current + * registered data operator name in short alphanumeric format. + * {@code null} if the operator name is not known or unregistered. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_DATA_OPERATOR_ALPHA_SHORT = "data-operator-alpha-short"; + + /** + * A string extra used with {@link #ACTION_SERVICE_STATE} containing the MCC + * (Mobile Country Code, 3 digits) and MNC (Mobile Network code, 2-3 digits) for the + * data operator. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_DATA_OPERATOR_NUMERIC = "data-operator-numeric"; + + /** + * A boolean extra used with {@link #ACTION_SERVICE_STATE} which indicates whether the current + * network selection mode is manual. + * Will be {@code true} if manual mode, {@code false} if automatic mode. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_MANUAL = "manual"; + + /** + * An integer extra used with {@link #ACTION_SERVICE_STATE} which represents the current voice + * radio technology. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_VOICE_RADIO_TECH = "radioTechnology"; + + /** + * An integer extra used with {@link #ACTION_SERVICE_STATE} which represents the current data + * radio technology. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_DATA_RADIO_TECH = "dataRadioTechnology"; + + /** + * A boolean extra used with {@link #ACTION_SERVICE_STATE} which represents concurrent service + * support on CDMA network. + * Will be {@code true} if support, {@code false} otherwise. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_CSS_INDICATOR = "cssIndicator"; + + /** + * An integer extra used with {@link #ACTION_SERVICE_STATE} which represents the CDMA network + * id. {@code Integer.MAX_VALUE} if unknown. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_NETWORK_ID = "networkId"; + + /** + * An integer extra used with {@link #ACTION_SERVICE_STATE} which represents the CDMA system id. + * {@code Integer.MAX_VALUE} if unknown. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_SYSTEM_ID = "systemId"; + + /** + * An integer extra used with {@link #ACTION_SERVICE_STATE} represents the TSB-58 roaming + * indicator if registered on a CDMA or EVDO system or {@code -1} if not. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_CDMA_ROAMING_INDICATOR = "cdmaRoamingIndicator"; + + /** + * An integer extra used with {@link #ACTION_SERVICE_STATE} represents the default roaming + * indicator from the PRL if registered on a CDMA or EVDO system {@code -1} if not. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_CDMA_DEFAULT_ROAMING_INDICATOR = "cdmaDefaultRoamingIndicator"; + + /** + * A boolean extra used with {@link #ACTION_SERVICE_STATE} which indicates if under emergency + * only mode. + * {@code true} if in emergency only mode, {@code false} otherwise. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_EMERGENCY_ONLY = "emergencyOnly"; + + /** + * A boolean extra used with {@link #ACTION_SERVICE_STATE} which indicates whether data network + * registration state is roaming. + * {@code true} if registration indicates roaming, {@code false} otherwise + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_IS_DATA_ROAMING_FROM_REGISTRATION = + "isDataRoamingFromRegistration"; + + /** + * A boolean extra used with {@link #ACTION_SERVICE_STATE} which indicates if carrier + * aggregation is in use. + * {@code true} if carrier aggregation is in use, {@code false} otherwise. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_IS_USING_CARRIER_AGGREGATION = "isUsingCarrierAggregation"; + + /** + * An integer extra used with {@link #ACTION_SERVICE_STATE} representing the offset which + * is reduced from the rsrp threshold while calculating signal strength level. + * @hide + */ + @Deprecated + @SystemApi + public static final String EXTRA_LTE_EARFCN_RSRP_BOOST = "LteEarfcnRsrpBoost"; + + /** * The name of the extra used to define the text to be processed, as a * CharSequence. Note that this may be a styled CharSequence, so you must use * {@link Bundle#getCharSequence(String) Bundle.getCharSequence()} to retrieve it. diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java index c4e4e06be749..aaa5f19c3fca 100644 --- a/core/java/android/content/pm/BaseParceledListSlice.java +++ b/core/java/android/content/pm/BaseParceledListSlice.java @@ -50,6 +50,8 @@ abstract class BaseParceledListSlice<T> implements Parcelable { private final List<T> mList; + private int mInlineCountLimit = Integer.MAX_VALUE; + public BaseParceledListSlice(List<T> list) { mList = list; } @@ -135,6 +137,14 @@ abstract class BaseParceledListSlice<T> implements Parcelable { } /** + * Set a limit on the maximum number of entries in the array that will be included + * inline in the initial parcelling of this object. + */ + public void setInlineCountLimit(int maxCount) { + mInlineCountLimit = maxCount; + } + + /** * Write this to another Parcel. Note that this discards the internal Parcel * and should not be used anymore. This is so we can pass this to a Binder * where we won't have a chance to call recycle on this. @@ -149,7 +159,7 @@ abstract class BaseParceledListSlice<T> implements Parcelable { final Class<?> listElementClass = mList.get(0).getClass(); writeParcelableCreator(mList.get(0), dest); int i = 0; - while (i < N && dest.dataSize() < MAX_IPC_SIZE) { + while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) { dest.writeInt(1); final T parcelable = mList.get(i); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index a493f33cfcb1..50e3e6863d4f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1688,6 +1688,10 @@ public abstract class PackageManager { @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir"; + /** {@hide} */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_CTS = "android.software.cts"; + /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports one or more methods of @@ -6243,7 +6247,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/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 47735683548b..940447ca5f08 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -6173,6 +6173,7 @@ public class PackageParser { cpuAbiOverride = dest.readString(); use32bitAbi = (dest.readInt() == 1); restrictUpdateHash = dest.createByteArray(); + visibleToInstantApps = dest.readInt() == 1; } private static void internStringArrayList(List<String> list) { @@ -6287,6 +6288,7 @@ public class PackageParser { dest.writeString(cpuAbiOverride); dest.writeInt(use32bitAbi ? 1 : 0); dest.writeByteArray(restrictUpdateHash); + dest.writeInt(visibleToInstantApps ? 1 : 0); } diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java index 572a287f53d8..ea1d01bb7738 100644 --- a/core/java/android/hardware/SensorAdditionalInfo.java +++ b/core/java/android/hardware/SensorAdditionalInfo.java @@ -131,6 +131,64 @@ public class SensorAdditionalInfo { */ public static final int TYPE_SAMPLING = 0x10004; + /** + * Local geo-magnetic Field. + * + * Additional into to sensor hardware. Local geomagnetic field information based on + * device geo location. This type is primarily for for magnetic field calibration and rotation + * vector sensor fusion. + * + * float[3]: strength (uT), declination and inclination angle (rad). + * @hide + */ + public static final int TYPE_LOCAL_GEOMAGNETIC_FIELD = 0x30000; + + /** + * Local gravity acceleration strength. + * + * Additional info to sensor hardware for accelerometer calibration. + * + * float: gravitational acceleration norm in m/s^2. + * @hide + */ + public static final int TYPE_LOCAL_GRAVITY = 0x30001; + + /** + * Device dock state. + * + * Additional info to sensor hardware indicating dock states of device. + * + * int32_t: dock state following definition of {@link android.content.Intent#EXTRA_DOCK_STATE}. + * Undefined values are ignored. + * @hide + */ + public static final int TYPE_DOCK_STATE = 0x30002; + + /** + * High performance mode. + * + * Additional info to sensor hardware. Device is able to use up more power and take more + * resources to improve throughput and latency in high performance mode. One possible use case + * is virtual reality, when sensor latency need to be carefully controlled. + * + * int32_t: 1 or 0, denoting device is in or out of high performance mode, respectively. + * Other values are ignored. + * @hide + */ + public static final int TYPE_HIGH_PERFORMANCE_MODE = 0x30003; + + /** + * Magnetic field calibration hint. + * + * Additional info to sensor hardware. Device is notified when manually triggered magnetic field + * calibration procedure is started or stopped. The calibration procedure is assumed timed out + * after 1 minute from start, even if an explicit stop is not received. + * + * int32_t: 1 for calibration start, 0 for stop, other values are ignored. + * @hide + */ + public static final int TYPE_MAGNETIC_FIELD_CALIBRATION = 0x30004; + SensorAdditionalInfo( Sensor aSensor, int aType, int aSerial, int [] aIntValues, float [] aFloatValues) { sensor = aSensor; @@ -139,4 +197,18 @@ public class SensorAdditionalInfo { intValues = aIntValues; floatValues = aFloatValues; } + + /** @hide */ + public static SensorAdditionalInfo createLocalGeomagneticField( + float strength, float declination, float inclination) { + if (strength < 10 || strength > 100 // much beyond extreme values on earth + || declination < 0 || declination > Math.PI + || inclination < -Math.PI / 2 || inclination > Math.PI / 2) { + throw new IllegalArgumentException("Geomagnetic field info out of range"); + } + + return new SensorAdditionalInfo( + null, TYPE_LOCAL_GEOMAGNETIC_FIELD, 0, + null, new float[] { strength, declination, inclination}); + } } diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index a6930b035853..1dc64784c837 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -1927,4 +1927,12 @@ public abstract class SensorManager { } return delay; } + + /** @hide */ + public boolean setOperationParameter(SensorAdditionalInfo parameter) { + return setOperationParameterImpl(parameter); + } + + /** @hide */ + protected abstract boolean setOperationParameterImpl(SensorAdditionalInfo parameter); } diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 70298475043d..067717905518 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -67,6 +67,9 @@ public class SystemSensorManager extends SensorManager { private static native int nativeConfigDirectChannel( long nativeInstance, int channelHandle, int sensorHandle, int rate); + private static native int nativeSetOperationParameter( + long nativeInstance, int type, float[] floatValues, int[] intValues); + private static final Object sLock = new Object(); @GuardedBy("sLock") private static boolean sNativeClassInited = false; @@ -928,4 +931,9 @@ public class SystemSensorManager extends SensorManager { } } + + protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) { + return nativeSetOperationParameter( + mNativeInstance, parameter.type, parameter.floatValues, parameter.intValues) == 0; + } } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 2aa6af6f9e3c..46ad3f0eccc9 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -67,6 +67,15 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * * @hide */ + public Key(String name, Class<T> type, long vendorId) { + mKey = new CameraMetadataNative.Key<T>(name, type, vendorId); + } + + /** + * Visible for testing and vendor extensions only. + * + * @hide + */ public Key(String name, Class<T> type) { mKey = new CameraMetadataNative.Key<T>(name, type); } @@ -99,6 +108,15 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri } /** + * Return vendor tag id. + * + * @hide + */ + public long getVendorId() { + return mKey.getVendorId(); + } + + /** * {@inheritDoc} */ @Override @@ -159,6 +177,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri */ public CameraCharacteristics(CameraMetadataNative properties) { mProperties = CameraMetadataNative.move(properties); + setNativeInstance(mProperties); } /** @@ -227,7 +246,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri } mKeys = Collections.unmodifiableList( - getKeysStatic(getClass(), getKeyClass(), this, filterTags)); + getKeys(getClass(), getKeyClass(), this, filterTags)); return mKeys; } @@ -320,7 +339,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri "metadataClass must be a subclass of CameraMetadata"); } - List<TKey> staticKeyList = CameraCharacteristics.<TKey>getKeysStatic( + List<TKey> staticKeyList = getKeys( metadataClass, keyClass, /*instance*/null, filterTags); return Collections.unmodifiableList(staticKeyList); } diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index e2896271424b..8c8c49fa6373 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -52,6 +52,7 @@ public abstract class CameraMetadata<TKey> { private static final String TAG = "CameraMetadataAb"; private static final boolean DEBUG = false; + private CameraMetadataNative mNativeInstance = null; /** * Set a camera metadata field to a value. The field definitions can be @@ -89,6 +90,13 @@ public abstract class CameraMetadata<TKey> { /** * @hide */ + protected void setNativeInstance(CameraMetadataNative nativeInstance) { + mNativeInstance = nativeInstance; + } + + /** + * @hide + */ protected abstract Class<TKey> getKeyClass(); /** @@ -108,7 +116,7 @@ public abstract class CameraMetadata<TKey> { public List<TKey> getKeys() { Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass(); return Collections.unmodifiableList( - getKeysStatic(thisClass, getKeyClass(), this, /*filterTags*/null)); + getKeys(thisClass, getKeyClass(), this, /*filterTags*/null)); } /** @@ -126,7 +134,7 @@ public abstract class CameraMetadata<TKey> { * </p> */ /*package*/ @SuppressWarnings("unchecked") - static <TKey> ArrayList<TKey> getKeysStatic( + <TKey> ArrayList<TKey> getKeys( Class<?> type, Class<TKey> keyClass, CameraMetadata<TKey> instance, int[] filterTags) { @@ -173,23 +181,31 @@ public abstract class CameraMetadata<TKey> { } } - ArrayList<TKey> vendorKeys = CameraMetadataNative.getAllVendorKeys(keyClass); + if (null == mNativeInstance) { + return keyList; + } + + ArrayList<TKey> vendorKeys = mNativeInstance.getAllVendorKeys(keyClass); if (vendorKeys != null) { for (TKey k : vendorKeys) { String keyName; + long vendorId; if (k instanceof CaptureRequest.Key<?>) { keyName = ((CaptureRequest.Key<?>) k).getName(); + vendorId = ((CaptureRequest.Key<?>) k).getVendorId(); } else if (k instanceof CaptureResult.Key<?>) { keyName = ((CaptureResult.Key<?>) k).getName(); + vendorId = ((CaptureResult.Key<?>) k).getVendorId(); } else if (k instanceof CameraCharacteristics.Key<?>) { keyName = ((CameraCharacteristics.Key<?>) k).getName(); + vendorId = ((CameraCharacteristics.Key<?>) k).getVendorId(); } else { continue; } if (filterTags == null || Arrays.binarySearch(filterTags, - CameraMetadataNative.getTag(keyName)) >= 0) { + CameraMetadataNative.getTag(keyName, vendorId)) >= 0) { keyList.add(k); } } diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 12b46c1bf693..1cf8f0314980 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -101,6 +101,15 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * * @hide */ + public Key(String name, Class<T> type, long vendorId) { + mKey = new CameraMetadataNative.Key<T>(name, type, vendorId); + } + + /** + * Visible for testing and vendor extensions only. + * + * @hide + */ public Key(String name, Class<T> type) { mKey = new CameraMetadataNative.Key<T>(name, type); } @@ -133,6 +142,15 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> } /** + * Return vendor tag id. + * + * @hide + */ + public long getVendorId() { + return mKey.getVendorId(); + } + + /** * {@inheritDoc} */ @Override @@ -199,6 +217,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> */ private CaptureRequest() { mSettings = new CameraMetadataNative(); + setNativeInstance(mSettings); mSurfaceSet = new HashSet<Surface>(); mIsReprocess = false; mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE; @@ -212,6 +231,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> @SuppressWarnings("unchecked") private CaptureRequest(CaptureRequest source) { mSettings = new CameraMetadataNative(source.mSettings); + setNativeInstance(mSettings); mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone(); mIsReprocess = source.mIsReprocess; mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList; @@ -242,6 +262,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> private CaptureRequest(CameraMetadataNative settings, boolean isReprocess, int reprocessableSessionId) { mSettings = CameraMetadataNative.move(settings); + setNativeInstance(mSettings); mSurfaceSet = new HashSet<Surface>(); mIsReprocess = isReprocess; if (isReprocess) { @@ -441,6 +462,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> */ private void readFromParcel(Parcel in) { mSettings.readFromParcel(in); + setNativeInstance(mSettings); mSurfaceSet.clear(); diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 3f8b57a3498f..419e3e28d36d 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -78,6 +78,15 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * * @hide */ + public Key(String name, Class<T> type, long vendorId) { + mKey = new CameraMetadataNative.Key<T>(name, type, vendorId); + } + + /** + * Visible for testing and vendor extensions only. + * + * @hide + */ public Key(String name, Class<T> type) { mKey = new CameraMetadataNative.Key<T>(name, type); } @@ -110,6 +119,15 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { } /** + * Return vendor tag id. + * + * @hide + */ + public long getVendorId() { + return mKey.getVendorId(); + } + + /** * {@inheritDoc} */ @Override @@ -186,6 +204,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { if (mResults.isEmpty()) { throw new AssertionError("Results must not be empty"); } + setNativeInstance(mResults); mRequest = parent; mSequenceId = extras.getRequestId(); mFrameNumber = extras.getFrameNumber(); @@ -215,6 +234,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { throw new AssertionError("Results must not be empty"); } + setNativeInstance(mResults); mRequest = null; mSequenceId = sequenceId; mFrameNumber = -1; diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 4d92ab1ccbb9..ebe2fa17a7c2 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -79,10 +79,28 @@ public class CameraMetadataNative implements Parcelable { public static class Key<T> { private boolean mHasTag; private int mTag; + private long mVendorId = Long.MAX_VALUE; private final Class<T> mType; private final TypeReference<T> mTypeReference; private final String mName; private final int mHash; + + /** + * @hide + */ + public Key(String name, Class<T> type, long vendorId) { + if (name == null) { + throw new NullPointerException("Key needs a valid name"); + } else if (type == null) { + throw new NullPointerException("Type needs to be non-null"); + } + mName = name; + mType = type; + mVendorId = vendorId; + mTypeReference = TypeReference.createSpecializedTypeReference(type); + mHash = mName.hashCode() ^ mTypeReference.hashCode(); + } + /** * Visible for testing only. * @@ -194,7 +212,7 @@ public class CameraMetadataNative implements Parcelable { */ public final int getTag() { if (!mHasTag) { - mTag = CameraMetadataNative.getTag(mName); + mTag = CameraMetadataNative.getTag(mName, mVendorId); mHasTag = true; } return mTag; @@ -212,6 +230,15 @@ public class CameraMetadataNative implements Parcelable { } /** + * Get the vendor tag provider id. + * + * @hide + */ + public final long getVendorId() { + return mVendorId; + } + + /** * Get the type reference backing the type {@code T} for this key. * * <p>The distinction is only important if {@code T} is a generic, e.g. @@ -463,13 +490,14 @@ public class CameraMetadataNative implements Parcelable { } private <T> T getBase(Key<T> key) { - int tag = key.getTag(); + int tag = nativeGetTagFromKeyLocal(key.getName()); byte[] values = readValues(tag); if (values == null) { return null; } - Marshaler<T> marshaler = getMarshalerForKey(key); + int nativeType = nativeGetTypeFromTagLocal(tag); + Marshaler<T> marshaler = getMarshalerForKey(key, nativeType); ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); return marshaler.unmarshal(buffer); } @@ -947,15 +975,15 @@ public class CameraMetadataNative implements Parcelable { } private <T> void setBase(Key<T> key, T value) { - int tag = key.getTag(); - + int tag = nativeGetTagFromKeyLocal(key.getName()); if (value == null) { // Erase the entry writeValues(tag, /*src*/null); return; } // else update the entry to a new value - Marshaler<T> marshaler = getMarshalerForKey(key); + int nativeType = nativeGetTypeFromTagLocal(tag); + Marshaler<T> marshaler = getMarshalerForKey(key, nativeType); int size = marshaler.calculateMarshalSize(value); // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. @@ -1092,10 +1120,14 @@ public class CameraMetadataNative implements Parcelable { private native synchronized void nativeWriteValues(int tag, byte[] src); private native synchronized void nativeDump() throws IOException; // dump to ALOGD - private static native ArrayList nativeGetAllVendorKeys(Class keyClass); - private static native int nativeGetTagFromKey(String keyName) + private native synchronized ArrayList nativeGetAllVendorKeys(Class keyClass); + private native synchronized int nativeGetTagFromKeyLocal(String keyName) throws IllegalArgumentException; - private static native int nativeGetTypeFromTag(int tag) + private native synchronized int nativeGetTypeFromTagLocal(int tag) + throws IllegalArgumentException; + private static native int nativeGetTagFromKey(String keyName, long vendorId) + throws IllegalArgumentException; + private static native int nativeGetTypeFromTag(int tag, long vendorId) throws IllegalArgumentException; /** @@ -1133,7 +1165,7 @@ public class CameraMetadataNative implements Parcelable { * * @hide */ - public static <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) { + public <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) { if (keyClass == null) { throw new NullPointerException(); } @@ -1149,19 +1181,32 @@ public class CameraMetadataNative implements Parcelable { * @hide */ public static int getTag(String key) { - return nativeGetTagFromKey(key); + return nativeGetTagFromKey(key, Long.MAX_VALUE); + } + + /** + * Convert a key string into the equivalent native tag. + * + * @throws IllegalArgumentException if the key was not recognized + * @throws NullPointerException if the key was null + * + * @hide + */ + public static int getTag(String key, long vendorId) { + return nativeGetTagFromKey(key, vendorId); } /** * Get the underlying native type for a tag. * * @param tag An integer tag, see e.g. {@link #getTag} + * @param vendorId A vendor tag provider id * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE} * * @hide */ - public static int getNativeType(int tag) { - return nativeGetTypeFromTag(tag); + public static int getNativeType(int tag, long vendorId) { + return nativeGetTypeFromTag(tag, vendorId); } /** @@ -1226,9 +1271,9 @@ public class CameraMetadataNative implements Parcelable { * @throws UnsupportedOperationException * if the native/managed type combination for {@code key} is not supported */ - private static <T> Marshaler<T> getMarshalerForKey(Key<T> key) { + private static <T> Marshaler<T> getMarshalerForKey(Key<T> key, int nativeType) { return MarshalRegistry.getMarshaler(key.getTypeReference(), - getNativeType(key.getTag())); + nativeType); } @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java b/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java new file mode 100644 index 000000000000..1f92f6d9ebf1 --- /dev/null +++ b/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java @@ -0,0 +1,67 @@ +/* + * 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.hardware.camera2.params; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * A class for describing the vendor tag cache declared by a camera HAL module. + * Generally only used by the native side of + * android.hardware.camera2.impl.CameraMetadataNative + * + * @hide + */ +public final class VendorTagDescriptorCache implements Parcelable { + + private VendorTagDescriptorCache(Parcel source) { + } + + public static final Parcelable.Creator<VendorTagDescriptorCache> CREATOR = + new Parcelable.Creator<VendorTagDescriptorCache>() { + @Override + public VendorTagDescriptorCache createFromParcel(Parcel source) { + try { + VendorTagDescriptorCache vendorDescriptorCache = new VendorTagDescriptorCache(source); + return vendorDescriptorCache; + } catch (Exception e) { + Log.e(TAG, "Exception creating VendorTagDescriptorCache from parcel", e); + return null; + } + } + + @Override + public VendorTagDescriptorCache[] newArray(int size) { + return new VendorTagDescriptorCache[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (dest == null) { + throw new IllegalArgumentException("dest must not be null"); + } + } + + private static final String TAG = "VendorTagDescriptorCache"; +} diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl new file mode 100644 index 000000000000..0aa3ce66eb29 --- /dev/null +++ b/core/java/android/net/IIpSecService.aidl @@ -0,0 +1,46 @@ +/* +** Copyright 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.net; + +import android.net.Network; +import android.net.IpSecConfig; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; + +/** + * @hide + */ +interface IIpSecService +{ + Bundle reserveSecurityParameterIndex( + int direction, in String remoteAddress, int requestedSpi, in IBinder binder); + + void releaseSecurityParameterIndex(int resourceId); + + Bundle openUdpEncapsulationSocket(int port, in IBinder binder); + + void closeUdpEncapsulationSocket(in ParcelFileDescriptor socket); + + Bundle createTransportModeTransform(in IpSecConfig c, in IBinder binder); + + void deleteTransportModeTransform(int transformId); + + void applyTransportModeTransform(in ParcelFileDescriptor socket, int transformId); + + void removeTransportModeTransform(in ParcelFileDescriptor socket, int transformId); +} diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java index da5cb37961ab..7fea4a25cab7 100644 --- a/core/java/android/net/IpSecAlgorithm.java +++ b/core/java/android/net/IpSecAlgorithm.java @@ -164,6 +164,8 @@ public final class IpSecAlgorithm implements Parcelable { private static boolean isTruncationLengthValid(String algo, int truncLenBits) { switch (algo) { + case ALGO_CRYPT_AES_CBC: + return (truncLenBits == 128 || truncLenBits == 192 || truncLenBits == 256); case ALGO_AUTH_HMAC_MD5: return (truncLenBits >= 96 && truncLenBits <= 128); case ALGO_AUTH_HMAC_SHA1: diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java index b58bf421a86a..13dc19f68577 100644 --- a/core/java/android/net/IpSecConfig.java +++ b/core/java/android/net/IpSecConfig.java @@ -23,7 +23,7 @@ import java.net.UnknownHostException; /** @hide */ public final class IpSecConfig implements Parcelable { - private static final String TAG = IpSecConfig.class.getSimpleName(); + private static final String TAG = "IpSecConfig"; //MODE_TRANSPORT or MODE_TUNNEL int mode; @@ -43,13 +43,13 @@ public final class IpSecConfig implements Parcelable { int spi; // Encryption Algorithm - IpSecAlgorithm encryptionAlgo; + IpSecAlgorithm encryption; // Authentication Algorithm - IpSecAlgorithm authenticationAlgo; + IpSecAlgorithm authentication; } - Flow[] flow = new Flow[2]; + Flow[] flow = new Flow[] {new Flow(), new Flow()}; // For tunnel mode IPv4 UDP Encapsulation // IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE @@ -57,17 +57,15 @@ public final class IpSecConfig implements Parcelable { int encapLocalPort; int encapRemotePort; - // An optional protocol to match with the selector - int selectorProto; - - // A bitmask of FEATURE_* indicating which of the fields - // of this class are valid. - long features; - // An interval, in seconds between the NattKeepalive packets int nattKeepaliveInterval; - public InetAddress getLocalIp() { + // Transport or Tunnel + public int getMode() { + return mode; + } + + public InetAddress getLocalAddress() { return localAddress; } @@ -75,19 +73,19 @@ public final class IpSecConfig implements Parcelable { return flow[direction].spi; } - public InetAddress getRemoteIp() { + public InetAddress getRemoteAddress() { return remoteAddress; } - public IpSecAlgorithm getEncryptionAlgo(int direction) { - return flow[direction].encryptionAlgo; + public IpSecAlgorithm getEncryption(int direction) { + return flow[direction].encryption; } - public IpSecAlgorithm getAuthenticationAlgo(int direction) { - return flow[direction].authenticationAlgo; + public IpSecAlgorithm getAuthentication(int direction) { + return flow[direction].authentication; } - Network getNetwork() { + public Network getNetwork() { return network; } @@ -103,18 +101,10 @@ public final class IpSecConfig implements Parcelable { return encapRemotePort; } - public int getSelectorProto() { - return selectorProto; - } - - int getNattKeepaliveInterval() { + public int getNattKeepaliveInterval() { return nattKeepaliveInterval; } - public boolean hasProperty(int featureBits) { - return (features & featureBits) == featureBits; - } - // Parcelable Methods @Override @@ -124,31 +114,25 @@ public final class IpSecConfig implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeLong(features); // TODO: Use a byte array or other better method for storing IPs that can also include scope out.writeString((localAddress != null) ? localAddress.getHostAddress() : null); // TODO: Use a byte array or other better method for storing IPs that can also include scope out.writeString((remoteAddress != null) ? remoteAddress.getHostAddress() : null); out.writeParcelable(network, flags); out.writeInt(flow[IpSecTransform.DIRECTION_IN].spi); - out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryptionAlgo, flags); - out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authenticationAlgo, flags); + out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryption, flags); + out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authentication, flags); out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spi); - out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo, flags); - out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo, flags); + out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryption, flags); + out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authentication, flags); out.writeInt(encapType); out.writeInt(encapLocalPort); out.writeInt(encapRemotePort); - out.writeInt(selectorProto); } // Package Private: Used by the IpSecTransform.Builder; // there should be no public constructor for this object - IpSecConfig() { - flow[IpSecTransform.DIRECTION_IN].spi = 0; - flow[IpSecTransform.DIRECTION_OUT].spi = 0; - nattKeepaliveInterval = 0; //FIXME constant - } + IpSecConfig() {} private static InetAddress readInetAddressFromParcel(Parcel in) { String addrString = in.readString(); @@ -164,24 +148,22 @@ public final class IpSecConfig implements Parcelable { } private IpSecConfig(Parcel in) { - features = in.readLong(); localAddress = readInetAddressFromParcel(in); remoteAddress = readInetAddressFromParcel(in); network = (Network) in.readParcelable(Network.class.getClassLoader()); flow[IpSecTransform.DIRECTION_IN].spi = in.readInt(); - flow[IpSecTransform.DIRECTION_IN].encryptionAlgo = + flow[IpSecTransform.DIRECTION_IN].encryption = (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); - flow[IpSecTransform.DIRECTION_IN].authenticationAlgo = + flow[IpSecTransform.DIRECTION_IN].authentication = (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); flow[IpSecTransform.DIRECTION_OUT].spi = in.readInt(); - flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo = + flow[IpSecTransform.DIRECTION_OUT].encryption = (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); - flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo = + flow[IpSecTransform.DIRECTION_OUT].authentication = (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); encapType = in.readInt(); encapLocalPort = in.readInt(); encapRemotePort = in.readInt(); - selectorProto = in.readInt(); } public static final Parcelable.Creator<IpSecConfig> CREATOR = diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 2c544e9b9bfe..6852beb06529 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -17,10 +17,11 @@ package android.net; import static com.android.internal.util.Preconditions.checkNotNull; -import android.annotation.SystemApi; -import android.content.Context; -import android.os.INetworkManagementService; +import android.annotation.NonNull; +import android.os.Binder; +import android.os.Bundle; import android.os.ParcelFileDescriptor; +import android.os.RemoteException; import android.util.AndroidException; import dalvik.system.CloseGuard; import java.io.FileDescriptor; @@ -41,6 +42,29 @@ public final class IpSecManager { private static final String TAG = "IpSecManager"; /** + * The Security Parameter Index, SPI, 0 indicates an unknown or invalid index. + * + * <p>No IPsec packet may contain an SPI of 0. + */ + public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; + + /** @hide */ + public interface Status { + public static final int OK = 0; + public static final int RESOURCE_UNAVAILABLE = 1; + public static final int SPI_UNAVAILABLE = 2; + } + + /** @hide */ + public static final String KEY_STATUS = "status"; + /** @hide */ + public static final String KEY_RESOURCE_ID = "resourceId"; + /** @hide */ + public static final String KEY_SPI = "spi"; + /** @hide */ + public static final int INVALID_RESOURCE_ID = 0; + + /** * Indicates that the combination of remote InetAddress and SPI was non-unique for a given * request. If encountered, selection of a new SPI is required before a transform may be * created. Note, this should happen very rarely if the SPI is chosen to be sufficiently random @@ -79,42 +103,30 @@ public final class IpSecManager { } } - private final Context mContext; - private final INetworkManagementService mService; + private final IIpSecService mService; public static final class SecurityParameterIndex implements AutoCloseable { - private final Context mContext; - private final InetAddress mDestinationAddress; + private final IIpSecService mService; + private final InetAddress mRemoteAddress; private final CloseGuard mCloseGuard = CloseGuard.get(); - private int mSpi; + private int mSpi = INVALID_SECURITY_PARAMETER_INDEX; + private int mResourceId; /** Return the underlying SPI held by this object */ public int getSpi() { return mSpi; } - private SecurityParameterIndex(Context context, InetAddress destinationAddress, int spi) - throws ResourceUnavailableException, SpiUnavailableException { - mContext = context; - mDestinationAddress = destinationAddress; - mSpi = spi; - mCloseGuard.open("open"); - } - /** * Release an SPI that was previously reserved. * - * <p>Release an SPI for use by other users in the system. This will fail if the SPI is - * currently in use by an IpSecTransform. - * - * @param destinationAddress SPIs must be unique for each combination of SPI and destination - * address. Thus, the destinationAddress to which the SPI will communicate must be - * supplied. - * @param spi the previously reserved SPI to be freed. + * <p>Release an SPI for use by other users in the system. If a SecurityParameterIndex is + * applied to an IpSecTransform, it will become unusable for future transforms but should + * still be closed to ensure system resources are released. */ @Override public void close() { - mSpi = INVALID_SECURITY_PARAMETER_INDEX; // TODO: Invalid SPI + mSpi = INVALID_SECURITY_PARAMETER_INDEX; mCloseGuard.close(); } @@ -126,23 +138,61 @@ public final class IpSecManager { close(); } - } - /** - * The Security Parameter Index, SPI, 0 indicates an unknown or invalid index. - * - * <p>No IPsec packet may contain an SPI of 0. - */ - public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; + private SecurityParameterIndex( + @NonNull IIpSecService service, int direction, InetAddress remoteAddress, int spi) + throws ResourceUnavailableException, SpiUnavailableException { + mService = service; + mRemoteAddress = remoteAddress; + try { + Bundle result = + mService.reserveSecurityParameterIndex( + direction, remoteAddress.getHostAddress(), spi, new Binder()); + + if (result == null) { + throw new NullPointerException("Received null response from IpSecService"); + } + + int status = result.getInt(KEY_STATUS); + switch (status) { + case Status.OK: + break; + case Status.RESOURCE_UNAVAILABLE: + throw new ResourceUnavailableException( + "No more SPIs may be allocated by this requester."); + case Status.SPI_UNAVAILABLE: + throw new SpiUnavailableException("Requested SPI is unavailable", spi); + default: + throw new RuntimeException( + "Unknown status returned by IpSecService: " + status); + } + mSpi = result.getInt(KEY_SPI); + mResourceId = result.getInt(KEY_RESOURCE_ID); + + if (mSpi == INVALID_SECURITY_PARAMETER_INDEX) { + throw new RuntimeException("Invalid SPI returned by IpSecService: " + status); + } + + if (mResourceId == INVALID_RESOURCE_ID) { + throw new RuntimeException( + "Invalid Resource ID returned by IpSecService: " + status); + } + + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mCloseGuard.open("open"); + } + } /** - * Reserve an SPI for traffic bound towards the specified destination address. + * Reserve an SPI for traffic bound towards the specified remote address. * * <p>If successful, this SPI is guaranteed available until released by a call to {@link * SecurityParameterIndex#close()}. * - * @param destinationAddress SPIs must be unique for each combination of SPI and destination - * address. + * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT} + * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress. * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. * @return the reserved SecurityParameterIndex * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated @@ -150,9 +200,9 @@ public final class IpSecManager { * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved */ public SecurityParameterIndex reserveSecurityParameterIndex( - InetAddress destinationAddress, int requestedSpi) + int direction, InetAddress remoteAddress, int requestedSpi) throws SpiUnavailableException, ResourceUnavailableException { - return new SecurityParameterIndex(mContext, destinationAddress, requestedSpi); + return new SecurityParameterIndex(mService, direction, remoteAddress, requestedSpi); } /** @@ -190,7 +240,13 @@ public final class IpSecManager { } /* Call down to activate a transform */ - private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {} + private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) { + try { + mService.applyTransportModeTransform(pfd, transform.getResourceId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } /** * Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to @@ -203,7 +259,6 @@ public final class IpSecManager { * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform. * @hide */ - @SystemApi public void applyTunnelModeTransform(Network net, IpSecTransform transform) {} /** @@ -235,7 +290,13 @@ public final class IpSecManager { } /* Call down to activate a transform */ - private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {} + private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) { + try { + mService.removeTransportModeTransform(pfd, transform.getResourceId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } /** * Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of @@ -248,7 +309,6 @@ public final class IpSecManager { * network * @hide */ - @SystemApi public void removeTunnelModeTransform(Network net, IpSecTransform transform) {} /** @@ -260,19 +320,19 @@ public final class IpSecManager { */ public static final class UdpEncapsulationSocket implements AutoCloseable { private final FileDescriptor mFd; - private final Context mContext; + private final IIpSecService mService; private final CloseGuard mCloseGuard = CloseGuard.get(); - private UdpEncapsulationSocket(Context context, int port) + private UdpEncapsulationSocket(@NonNull IIpSecService service, int port) throws ResourceUnavailableException { - mContext = context; + mService = service; mCloseGuard.open("constructor"); // TODO: go down to the kernel and get a socket on the specified mFd = new FileDescriptor(); } - private UdpEncapsulationSocket(Context context) throws ResourceUnavailableException { - mContext = context; + private UdpEncapsulationSocket(IIpSecService service) throws ResourceUnavailableException { + mService = service; mCloseGuard.open("constructor"); // TODO: go get a random socket on a random port mFd = new FileDescriptor(); @@ -339,7 +399,7 @@ public final class IpSecManager { public UdpEncapsulationSocket openUdpEncapsulationSocket(int port) throws IOException, ResourceUnavailableException { // Temporary code - return new UdpEncapsulationSocket(mContext, port); + return new UdpEncapsulationSocket(mService, port); } /** @@ -363,7 +423,7 @@ public final class IpSecManager { public UdpEncapsulationSocket openUdpEncapsulationSocket() throws IOException, ResourceUnavailableException { // Temporary code - return new UdpEncapsulationSocket(mContext); + return new UdpEncapsulationSocket(mService); } /** @@ -372,8 +432,7 @@ public final class IpSecManager { * @param context the application context for this manager * @hide */ - public IpSecManager(Context context, INetworkManagementService service) { - mContext = checkNotNull(context, "missing context"); + public IpSecManager(IIpSecService service) { mService = checkNotNull(service, "missing service"); } } diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index d6dd28bec390..801e98c7b138 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -15,11 +15,21 @@ */ package android.net; +import static android.net.IpSecManager.INVALID_RESOURCE_ID; +import static android.net.IpSecManager.KEY_RESOURCE_ID; +import static android.net.IpSecManager.KEY_STATUS; + import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.Context; -import android.system.ErrnoException; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; +import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; import java.io.IOException; import java.lang.annotation.Retention; @@ -86,39 +96,64 @@ public final class IpSecTransform implements AutoCloseable { @Retention(RetentionPolicy.SOURCE) public @interface EncapType {} - /** - * Sentinel for an invalid transform (means that this transform is inactive). - * - * @hide - */ - public static final int INVALID_TRANSFORM_ID = -1; - private IpSecTransform(Context context, IpSecConfig config) { mContext = context; mConfig = config; - mTransformId = INVALID_TRANSFORM_ID; + mResourceId = INVALID_RESOURCE_ID; + } + + private IIpSecService getIpSecService() { + IBinder b = ServiceManager.getService(android.content.Context.IPSEC_SERVICE); + if (b == null) { + throw new RemoteException("Failed to connect to IpSecService") + .rethrowAsRuntimeException(); + } + + return IIpSecService.Stub.asInterface(b); + } + + private void checkResultStatusAndThrow(int status) + throws IOException, IpSecManager.ResourceUnavailableException, + IpSecManager.SpiUnavailableException { + switch (status) { + case IpSecManager.Status.OK: + return; + // TODO: Pass Error string back from bundle so that errors can be more specific + case IpSecManager.Status.RESOURCE_UNAVAILABLE: + throw new IpSecManager.ResourceUnavailableException( + "Failed to allocate a new IpSecTransform"); + case IpSecManager.Status.SPI_UNAVAILABLE: + Log.wtf(TAG, "Attempting to use an SPI that was somehow not reserved"); + // Fall through + default: + throw new IllegalStateException( + "Failed to Create a Transform with status code " + status); + } } private IpSecTransform activate() throws IOException, IpSecManager.ResourceUnavailableException, IpSecManager.SpiUnavailableException { - int transformId; synchronized (this) { - //try { - transformId = INVALID_TRANSFORM_ID; - //} catch (RemoteException e) { - // throw e.rethrowFromSystemServer(); - //} - - if (transformId < 0) { - throw new ErrnoException("addTransform", -transformId).rethrowAsIOException(); + try { + IIpSecService svc = getIpSecService(); + Bundle result = svc.createTransportModeTransform(mConfig, new Binder()); + int status = result.getInt(KEY_STATUS); + checkResultStatusAndThrow(status); + mResourceId = result.getInt(KEY_RESOURCE_ID, INVALID_RESOURCE_ID); + + /* Keepalive will silently fail if not needed by the config; but, if needed and + * it fails to start, we need to bail because a transform will not be reliable + * to use if keepalive is expected to offload and fails. + */ + // FIXME: if keepalive fails, we need to fail spectacularly + startKeepalive(mContext); + Log.d(TAG, "Added Transform with Id " + mResourceId); + mCloseGuard.open("build"); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } - - startKeepalive(mContext); // Will silently fail if not required - mTransformId = transformId; - Log.d(TAG, "Added Transform with Id " + transformId); } - mCloseGuard.open("build"); return this; } @@ -133,21 +168,27 @@ public final class IpSecTransform implements AutoCloseable { * transform is no longer needed. */ public void close() { - Log.d(TAG, "Removing Transform with Id " + mTransformId); + Log.d(TAG, "Removing Transform with Id " + mResourceId); // Always safe to attempt cleanup - if (mTransformId == INVALID_TRANSFORM_ID) { + if (mResourceId == INVALID_RESOURCE_ID) { + mCloseGuard.close(); return; } - //try { - stopKeepalive(); - //} catch (RemoteException e) { - // transform.setTransformId(transformId); - // throw e.rethrowFromSystemServer(); - //} finally { - mTransformId = INVALID_TRANSFORM_ID; - //} - mCloseGuard.close(); + try { + /* Order matters here because the keepalive is best-effort but could fail in some + * horrible way to be removed if the wifi (or cell) subsystem has crashed, and we + * still want to clear out the transform. + */ + IIpSecService svc = getIpSecService(); + svc.deleteTransportModeTransform(mResourceId); + stopKeepalive(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } finally { + mResourceId = INVALID_RESOURCE_ID; + mCloseGuard.close(); + } } @Override @@ -164,7 +205,7 @@ public final class IpSecTransform implements AutoCloseable { } private final IpSecConfig mConfig; - private int mTransformId; + private int mResourceId; private final Context mContext; private final CloseGuard mCloseGuard = CloseGuard.get(); private ConnectivityManager.PacketKeepalive mKeepalive; @@ -200,6 +241,7 @@ public final class IpSecTransform implements AutoCloseable { /* Package */ void startKeepalive(Context c) { + // FIXME: NO_KEEPALIVE needs to be a constant if (mConfig.getNattKeepaliveInterval() == 0) { return; } @@ -208,7 +250,7 @@ public final class IpSecTransform implements AutoCloseable { (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE); if (mKeepalive != null) { - Log.e(TAG, "Keepalive already started for this IpSecTransform."); + Log.wtf(TAG, "Keepalive already started for this IpSecTransform."); return; } @@ -218,10 +260,11 @@ public final class IpSecTransform implements AutoCloseable { mConfig.getNetwork(), mConfig.getNattKeepaliveInterval(), mKeepaliveCallback, - mConfig.getLocalIp(), + mConfig.getLocalAddress(), mConfig.getEncapLocalPort(), - mConfig.getRemoteIp()); + mConfig.getRemoteAddress()); try { + // FIXME: this is still a horrible way to fudge the synchronous callback mKeepaliveSyncLock.wait(2000); } catch (InterruptedException e) { } @@ -232,6 +275,11 @@ public final class IpSecTransform implements AutoCloseable { } /* Package */ + int getResourceId() { + return mResourceId; + } + + /* Package */ void stopKeepalive() { if (mKeepalive == null) { return; @@ -247,16 +295,6 @@ public final class IpSecTransform implements AutoCloseable { } } - /* Package */ - void setTransformId(int transformId) { - mTransformId = transformId; - } - - /* Package */ - int getTransformId() { - return mTransformId; - } - /** * Builder object to facilitate the creation of IpSecTransform objects. * @@ -280,7 +318,7 @@ public final class IpSecTransform implements AutoCloseable { */ public IpSecTransform.Builder setEncryption( @TransformDirection int direction, IpSecAlgorithm algo) { - mConfig.flow[direction].encryptionAlgo = algo; + mConfig.flow[direction].encryption = algo; return this; } @@ -295,7 +333,7 @@ public final class IpSecTransform implements AutoCloseable { */ public IpSecTransform.Builder setAuthentication( @TransformDirection int direction, IpSecAlgorithm algo) { - mConfig.flow[direction].authenticationAlgo = algo; + mConfig.flow[direction].authentication = algo; return this; } @@ -305,32 +343,9 @@ public final class IpSecTransform implements AutoCloseable { * given destination address. * * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as - * possible. Random number generation is a reasonable approach to selecting an SPI. For - * outbound SPIs, they must be reserved by calling {@link - * IpSecManager#reserveSecurityParameterIndex(InetAddress, int)}. Otherwise, Transforms will - * fail to build. - * - * <p>Unless an SPI is set for a given direction, traffic in that direction will be - * sent/received without any IPsec applied. - * - * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT} - * @param spi a unique 32-bit integer to identify transformed traffic - */ - public IpSecTransform.Builder setSpi(@TransformDirection int direction, int spi) { - mConfig.flow[direction].spi = spi; - return this; - } - - /** - * Set the SPI, which uniquely identifies a particular IPsec session from others. Because - * IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a - * given destination address. - * - * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as - * possible. Random number generation is a reasonable approach to selecting an SPI. For - * outbound SPIs, they must be reserved by calling {@link - * IpSecManager#reserveSecurityParameterIndex(InetAddress, int)}. Otherwise, Transforms will - * fail to activate. + * possible. To reserve a value call {@link IpSecManager#reserveSecurityParameterIndex(int, + * InetAddress, int)}. Otherwise, SPI collisions would prevent a transform from being + * activated. IpSecManager#reserveSecurityParameterIndex(int, InetAddres$s, int)}. * * <p>Unless an SPI is set for a given direction, traffic in that direction will be * sent/received without any IPsec applied. @@ -341,6 +356,8 @@ public final class IpSecTransform implements AutoCloseable { */ public IpSecTransform.Builder setSpi( @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) { + // TODO: convert to using the resource Id of the SPI. Then build() can validate + // the owner in the IpSecService mConfig.flow[direction].spi = spi.getSpi(); return this; } @@ -447,7 +464,6 @@ public final class IpSecTransform implements AutoCloseable { * properties is invalid. * @hide */ - @SystemApi public IpSecTransform buildTunnelModeTransform( InetAddress localAddress, InetAddress remoteAddress) { //FIXME: argument validation here @@ -463,7 +479,8 @@ public final class IpSecTransform implements AutoCloseable { * * @param context current Context */ - public Builder(Context context) { + public Builder(@NonNull Context context) { + Preconditions.checkNotNull(context); mContext = context; mConfig = new IpSecConfig(); } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index dd11f68ffe50..4c6d22ae1c51 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -182,7 +182,7 @@ public abstract class BatteryStats implements Parcelable { * New in version 19: * - Wakelock data (wl) gets current and max times. * New in version 20: - * - Sensor, BluetoothScan, WifiScan get background timers and counter. + * - Background timers and counters for: Sensor, BluetoothScan, WifiScan, Jobs. */ static final String CHECKIN_VERSION = "20"; @@ -392,6 +392,16 @@ public abstract class BatteryStats implements Parcelable { } /** + * Returns the secondary Timer held by the Timer, if one exists. This secondary timer may be + * used, for example, for tracking background usage. Secondary timers are never pooled. + * + * Not all Timer subclasses have a secondary timer; those that don't return null. + */ + public Timer getSubTimer() { + return null; + } + + /** * Returns whether the timer is currently running. Some types of timers * (e.g. BatchTimers) don't know whether the event is currently active, * and report false. @@ -3303,16 +3313,17 @@ public abstract class BatteryStats implements Parcelable { final int wifiScanCount = u.getWifiScanCount(which); final int wifiScanCountBg = u.getWifiScanBackgroundCount(which); // Note that 'ActualTime' are unpooled and always since reset (regardless of 'which') - final long wifiScanActualTime = u.getWifiScanActualTime(rawRealtime); - final long wifiScanActualTimeBg = u.getWifiScanBackgroundTime(rawRealtime); + final long wifiScanActualTimeMs = (u.getWifiScanActualTime(rawRealtime) + 500) / 1000; + final long wifiScanActualTimeMsBg = (u.getWifiScanBackgroundTime(rawRealtime) + 500) + / 1000; final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which); if (fullWifiLockOnTime != 0 || wifiScanTime != 0 || wifiScanCount != 0 - || wifiScanCountBg != 0 || wifiScanActualTime != 0 || wifiScanActualTimeBg != 0 - || uidWifiRunningTime != 0) { + || wifiScanCountBg != 0 || wifiScanActualTimeMs != 0 + || wifiScanActualTimeMsBg != 0 || uidWifiRunningTime != 0) { dumpLine(pw, uid, category, WIFI_DATA, fullWifiLockOnTime, wifiScanTime, uidWifiRunningTime, wifiScanCount, /* legacy fields follow, keep at 0 */ 0, 0, 0, - wifiScanCountBg, wifiScanActualTime, wifiScanActualTimeBg); + wifiScanCountBg, wifiScanActualTimeMs, wifiScanActualTimeMsBg); } dumpControllerActivityLine(pw, uid, category, WIFI_CONTROLLER_DATA, @@ -3393,9 +3404,13 @@ public abstract class BatteryStats implements Parcelable { // Convert from microseconds to milliseconds with rounding final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; final int count = timer.getCountLocked(which); + final Timer bgTimer = timer.getSubTimer(); + final long bgTime = bgTimer != null ? + (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1; + final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1; if (totalTime != 0) { dumpLine(pw, uid, category, JOB_DATA, "\"" + jobs.keyAt(ij) + "\"", - totalTime, count); + totalTime, count, bgTime, bgCount); } } @@ -4616,6 +4631,10 @@ public abstract class BatteryStats implements Parcelable { // Convert from microseconds to milliseconds with rounding final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; final int count = timer.getCountLocked(which); + final Timer bgTimer = timer.getSubTimer(); + final long bgTime = bgTimer != null ? + (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1; + final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1; sb.setLength(0); sb.append(prefix); sb.append(" Job "); @@ -4626,6 +4645,13 @@ public abstract class BatteryStats implements Parcelable { sb.append("realtime ("); sb.append(count); sb.append(" times)"); + if (bgTime > 0) { + sb.append(", "); + formatTimeMs(sb, bgTime); + sb.append("background ("); + sb.append(bgCount); + sb.append(" times)"); + } } else { sb.append("(not used)"); } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index e5d73e04bda6..b5af766fd159 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -59,19 +59,14 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.os.AppFuseMount; import com.android.internal.os.FuseAppLoop; -import com.android.internal.os.FuseAppLoop.UnmountedException; import com.android.internal.os.FuseUnavailableMountException; import com.android.internal.os.RoSystemProperties; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; -import java.io.BufferedReader; import java.io.File; import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStreamReader; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -84,7 +79,6 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; -import libcore.io.IoUtils; /** * StorageManager is the interface to the systems storage service. The storage @@ -186,15 +180,6 @@ public class StorageManager { private static volatile IStorageManager sStorageManager = null; - // TODO: the location of the primary storage block varies from device to device, so we need to - // try the most likely candidates - a long-term solution would be a device-specific vold - // function that returns the calculated size. - private static final String[] INTERNAL_STORAGE_SIZE_PATHS = { - "/sys/block/mmcblk0/size", - "/sys/block/sda/size" - }; - private static final int INTERNAL_STORAGE_SECTOR_SIZE = 512; - private final Context mContext; private final ContentResolver mResolver; @@ -1011,38 +996,13 @@ public class StorageManager { /** {@hide} */ public static Pair<String, Long> getPrimaryStoragePathAndSize() { - for (String path : INTERNAL_STORAGE_SIZE_PATHS) { - final long numberBlocks = readLong(path); - if (numberBlocks > 0) { - return new Pair<>(path, - FileUtils.roundStorageSize(numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE)); - } - } - return null; + return Pair.create(null, + FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace())); } - /** {@hide} */ public long getPrimaryStorageSize() { - final Pair<String, Long> pair = getPrimaryStoragePathAndSize(); - return pair == null ? 0 : pair.second.longValue(); - } - - private static long readLong(String path) { - try (final FileInputStream fis = new FileInputStream(path); - final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) { - return Long.parseLong(reader.readLine()); - } catch (FileNotFoundException e) { - // This is expected since we are trying to parse multiple paths. - Slog.i(TAG, "readLong(): Path doesn't exist: " + path + ": " + e); - return 0; - } catch (NumberFormatException e) { - Slog.e(TAG, "readLong(): Could not parse " + path + ": " + e); - return 0; - } catch (Exception e) { - Slog.e(TAG, "readLong(): Unknown exception while opening " + path + ": " + e); - return 0; - } + return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()); } /** @removed */ diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index a280e594525e..9d83bd7e90a6 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -114,7 +114,7 @@ public final class Downloads { * download's content: uri is specified in the intent's data. */ public static final String ACTION_DOWNLOAD_COMPLETED = - "android.intent.action.DOWNLOAD_COMPLETED"; + DownloadManager.ACTION_DOWNLOAD_COMPLETED; /** * Broadcast Action: this is sent by the download manager to the app @@ -127,7 +127,7 @@ public final class Downloads { * successfully. */ public static final String ACTION_NOTIFICATION_CLICKED = - "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"; + DownloadManager.ACTION_NOTIFICATION_CLICKED; /** * The name of the column containing the URI of the data being downloaded. diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java index fd9d4db9842e..4deb4ab69059 100644 --- a/core/java/android/provider/FontsContract.java +++ b/core/java/android/provider/FontsContract.java @@ -72,15 +72,28 @@ public class FontsContract { */ public static final String VARIATION_SETTINGS = "font_variation_settings"; /** - * Constant used to request data from a font provider. The cursor returned from the query - * should have this column populated with the int style for the resulting font. This should - * be one of {@link android.graphics.Typeface#NORMAL}, - * {@link android.graphics.Typeface#BOLD}, {@link android.graphics.Typeface#ITALIC} or - * {@link android.graphics.Typeface#BOLD_ITALIC} + * DO NOT USE THIS COLUMN. + * This column is kept for preventing demo apps. + * TODO: Remove once nobody uses this column. + * @hide + * @removed */ public static final String STYLE = "font_style"; /** * Constant used to request data from a font provider. The cursor returned from the query + * should have this column populated with the int weight for the resulting font. This value + * should be between 100 and 900. The most common values are 400 for regular weight and 700 + * for bold weight. + */ + public static final String WEIGHT = "font_weight"; + /** + * Constant used to request data from a font provider. The cursor returned from the query + * should have this column populated with the int italic for the resulting font. This should + * be 0 for regular style and 1 for italic. + */ + public static final String ITALIC = "font_italic"; + /** + * Constant used to request data from a font provider. The cursor returned from the query * should have this column populated to indicate the result status of the * query. This will be checked before any other data in the cursor. Possible values are * {@link #RESULT_CODE_OK}, {@link #RESULT_CODE_FONT_NOT_FOUND}, @@ -274,7 +287,7 @@ public class FontsContract { .build(); try (Cursor cursor = mContext.getContentResolver().query(uri, new String[] { Columns._ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.STYLE, - Columns.RESULT_CODE }, + Columns.WEIGHT, Columns.ITALIC, Columns.RESULT_CODE }, "query = ?", new String[] { request.getQuery() }, null);) { // TODO: Should we restrict the amount of fonts that can be returned? // TODO: Write documentation explaining that all results should be from the same family. @@ -285,6 +298,8 @@ public class FontsContract { final int idColumnIndex = cursor.getColumnIndexOrThrow(Columns._ID); final int ttcIndexColumnIndex = cursor.getColumnIndex(Columns.TTC_INDEX); final int vsColumnIndex = cursor.getColumnIndex(Columns.VARIATION_SETTINGS); + final int weightColumnIndex = cursor.getColumnIndex(Columns.WEIGHT); + final int italicColumnIndex = cursor.getColumnIndex(Columns.ITALIC); final int styleColumnIndex = cursor.getColumnIndex(Columns.STYLE); while (cursor.moveToNext()) { resultCode = resultCodeColumnIndex != -1 @@ -313,9 +328,22 @@ public class FontsContract { ? cursor.getInt(ttcIndexColumnIndex) : 0; final String variationSettings = vsColumnIndex != -1 ? cursor.getString(vsColumnIndex) : null; - final int style = styleColumnIndex != -1 - ? cursor.getInt(styleColumnIndex) : Typeface.NORMAL; - result.add(new FontResult(pfd, ttcIndex, variationSettings, style)); + // TODO: Stop using STYLE column and enforce WEIGHT/ITALIC column. + int weight; + boolean italic; + if (weightColumnIndex != -1 && italicColumnIndex != -1) { + weight = cursor.getInt(weightColumnIndex); + italic = cursor.getInt(italicColumnIndex) == 1; + } else if (styleColumnIndex != -1) { + final int style = cursor.getInt(styleColumnIndex); + weight = (style & Typeface.BOLD) != 0 ? 700 : 400; + italic = (style & Typeface.ITALIC) != 0; + } else { + weight = 400; + italic = false; + } + result.add( + new FontResult(pfd, ttcIndex, variationSettings, weight, italic)); } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException raised when interacting with content " + "provider " + authority, e); diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java deleted file mode 100644 index c26f679cbda8..000000000000 --- a/core/java/android/service/autofill/AutoFillService.java +++ /dev/null @@ -1,24 +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.service.autofill; - -/** - * @hide - * @deprecated TODO(b/35956626): remove once clients use AutofillService - */ -@Deprecated -public abstract class AutoFillService extends AutofillService { -} diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java index 6f17d0e5ec77..ef14095f62b1 100644 --- a/core/java/android/service/autofill/AutofillService.java +++ b/core/java/android/service/autofill/AutofillService.java @@ -71,7 +71,7 @@ public abstract class AutofillService extends Service { * Name under which a AutoFillService component publishes information about itself. * This meta-data should reference an XML resource containing a * <code><{@link - * android.R.styleable#AutoFillService autofill-service}></code> tag. + * android.R.styleable#AutofillService autofill-service}></code> tag. * This is a a sample XML file configuring an AutoFillService: * <pre> <autofill-service * android:settingsActivity="foo.bar.SettingsActivity" @@ -204,11 +204,12 @@ public abstract class AutofillService extends Service { * to notify the result of the request. * * @param structure {@link Activity}'s view structure. - * @param data bundle containing data passed by the service on previous calls to fill. - * This bundle allows your service to keep state between fill and save requests - * as well as when filling different sections of the UI as the system will try to - * aggressively unbind from the service to conserve resources. See {@link - * FillResponse} Javadoc for examples of multiple-sections requests. + * @param data bundle containing data passed by the service in a last call to + * {@link FillResponse.Builder#setExtras(Bundle)}, if any. This bundle allows your + * service to keep state between fill and save requests as well as when filling different + * sections of the UI as the system will try to aggressively unbind from the service to + * conserve resources. + * See {@link FillResponse} for examples of multiple-sections requests. * @param flags either {@code 0} or {@link AutofillManager#FLAG_MANUAL_REQUEST}. * @param cancellationSignal signal for observing cancellation requests. The system will use * this to notify you that the fill result is no longer needed and you should stop @@ -242,11 +243,12 @@ public abstract class AutofillService extends Service { * to notify the result of the request. * * @param structure {@link Activity}'s view structure. - * @param data bundle containing data passed by the service on previous calls to fill. - * This bundle allows your service to keep state between fill and save requests - * as well as when filling different sections of the UI as the system will try to - * aggressively unbind from the service to conserve resources. See {@link - * FillResponse} Javadoc for examples of multiple-sections requests. + * @param data bundle containing data passed by the service in a last call to + * {@link FillResponse.Builder#setExtras(Bundle)}, if any. This bundle allows your + * service to keep state between fill and save requests as well as when filling different + * sections of the UI as the system will try to aggressively unbind from the service to + * conserve resources. + * See {@link FillResponse} for examples of multiple-sections requests. * @param callback object used to notify the result of the request. */ public abstract void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle data, diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java index f6d40dbf3414..0f4824e47fa9 100644 --- a/core/java/android/service/autofill/AutofillServiceInfo.java +++ b/core/java/android/service/autofill/AutofillServiceInfo.java @@ -36,7 +36,6 @@ import com.android.internal.R; import java.io.IOException; -// TODO(b/33197203 , b/33802548): add CTS tests /** * {@link ServiceInfo} and meta-data about an {@link AutofillService}. * @@ -75,15 +74,8 @@ public final class AutofillServiceInfo { mServiceInfo = si; final TypedArray metaDataArray = getMetaDataArray(pm, si); if (metaDataArray != null) { - // TODO(b/35956626): inline newSettingsActivity once clients migrate - final String newSettingsActivity = - metaDataArray.getString(R.styleable.AutofillService_settingsActivity); - if (newSettingsActivity != null) { - mSettingsActivity = newSettingsActivity; - } else { - mSettingsActivity = - metaDataArray.getString(R.styleable.AutoFillService_settingsActivity); - } + mSettingsActivity = metaDataArray + .getString(R.styleable.AutofillService_settingsActivity); metaDataArray.recycle(); } else { mSettingsActivity = null; @@ -96,16 +88,11 @@ public final class AutofillServiceInfo { @Nullable private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si) { // Check for permissions. - // TODO(b/35956626): remove check for BIND_AUTO_FILL once clients migrate - if (!Manifest.permission.BIND_AUTOFILL.equals(si.permission) - && !Manifest.permission.BIND_AUTO_FILL.equals(si.permission)) { + if (!Manifest.permission.BIND_AUTOFILL.equals(si.permission)) { Log.e(TAG, "Service does not require permission " + Manifest.permission.BIND_AUTOFILL); return null; } - // TODO(b/35956626): remove once clients migrate - final boolean oldStyle = !Manifest.permission.BIND_AUTOFILL.equals(si.permission); - // Get the AutoFill metadata, if declared. XmlResourceParser parser = si.loadXmlMetaData(pm, AutofillService.SERVICE_META_DATA); if (parser == null) { @@ -141,8 +128,7 @@ public final class AutofillServiceInfo { return null; } - return oldStyle ? res.obtainAttributes(attrs, R.styleable.AutoFillService) - : res.obtainAttributes(attrs, R.styleable.AutofillService); + return res.obtainAttributes(attrs, R.styleable.AutofillService); } finally { parser.close(); } diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index e27fa06a9df7..e77bd0d753ac 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -23,8 +23,6 @@ import android.annotation.Nullable; import android.content.IntentSender; import android.os.Parcel; import android.os.Parcelable; -import android.view.autofill.AutoFillId; -import android.view.autofill.AutoFillValue; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import android.widget.RemoteViews; @@ -78,11 +76,6 @@ public final class Dataset implements Parcelable { } /** @hide */ - public @Nullable RemoteViews getPresentation() { - return mPresentation; - } - - /** @hide */ public @Nullable IntentSender getAuthentication() { return mAuthentication; } @@ -180,15 +173,6 @@ public final class Dataset implements Parcelable { } /** - * @hide - * @deprecated TODO(b/35956626): remove once clients use other setValue() - */ - @Deprecated - public @NonNull Builder setValue(@NonNull AutoFillId id, @NonNull AutoFillValue value) { - return setValue(id.getDaRealId(), value.getDaRealValue()); - } - - /** * Sets the value of a field. * * @param id id returned by {@link diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index c43019dc297d..3117f9842b64 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.service.autofill; import static android.view.autofill.Helper.DEBUG; @@ -23,6 +24,7 @@ import android.content.IntentSender; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.widget.RemoteViews; @@ -112,7 +114,7 @@ import java.util.ArrayList; * * <p>The service could require user authentication at the {@link FillResponse} or the * {@link Dataset} level, prior to autofilling an activity - see - * {@link FillResponse.Builder#setAuthentication(IntentSender, RemoteViews)} and + * {@link FillResponse.Builder#setAuthentication(AutofillId[], IntentSender, RemoteViews)} and * {@link Dataset.Builder#setAuthentication(IntentSender)}. * * <p>It is recommended that you encrypt only the sensitive data but leave the labels unencrypted @@ -126,7 +128,7 @@ import java.util.ArrayList; * possible options) which will start your auth flow and after successfully authenticating * the user will be presented with the Home and Work options to pick one. Hence, you have * flexibility how to implement your auth while storing labels non-encrypted and data - * encrypted provides a better user experience.</p> + * encrypted provides a better user experience. */ public final class FillResponse implements Parcelable { @@ -135,6 +137,7 @@ public final class FillResponse implements Parcelable { private final Bundle mExtras; private final RemoteViews mPresentation; private final IntentSender mAuthentication; + private AutofillId[] mAuthenticationIds; private FillResponse(@NonNull Builder builder) { mDatasets = builder.mDatasets; @@ -142,6 +145,7 @@ public final class FillResponse implements Parcelable { mExtras = builder.mExtras; mPresentation = builder.mPresentation; mAuthentication = builder.mAuthentication; + mAuthenticationIds = builder.mAuthenticationIds; } /** @hide */ @@ -169,6 +173,11 @@ public final class FillResponse implements Parcelable { return mAuthentication; } + /** @hide */ + public @Nullable AutofillId[] getAuthenticationIds() { + return mAuthenticationIds; + } + /** * Builder for {@link FillResponse} objects. You must to provide at least * one dataset or set an authentication intent with a presentation view. @@ -179,6 +188,7 @@ public final class FillResponse implements Parcelable { private Bundle mExtras; private RemoteViews mPresentation; private IntentSender mAuthentication; + private AutofillId[] mAuthenticationIds; private boolean mDestroyed; /** @@ -193,7 +203,7 @@ public final class FillResponse implements Parcelable { * be encrypted. The provided {@link android.app.PendingIntent intent} must be an * activity which implements your authentication flow. Also if you provide an auth * intent you also need to specify the presentation view to be shown in the fill UI - * for the user to trigger your authentication flow.</p> + * for the user to trigger your authentication flow. * * <p>When a user triggers autofill, the system launches the provided intent * whose extras will have the {@link AutofillManager#EXTRA_ASSIST_STRUCTURE screen @@ -205,40 +215,54 @@ public final class FillResponse implements Parcelable { * user's data was locked and marked that the response needs an authentication then * in the response returned if authentication succeeds you need to provide all * available data sets some of which may need to be further authenticated, for - * example a credit card whose CVV needs to be entered.</p> + * example a credit card whose CVV needs to be entered. * * <p>If you provide an authentication intent you must also provide a presentation * which is used to visualize visualize the response for triggering the authentication - * flow.</p> + * flow. * * <p></><strong>Note:</strong> Do not make the provided pending intent * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the - * platform needs to fill in the authentication arguments.</p> + * platform needs to fill in the authentication arguments. * * @param authentication Intent to an activity with your authentication flow. * @param presentation The presentation to visualize the response. - * @return This builder. + * @param ids id of Views that when focused will display the authentication UI affordance. * + * @return This builder. * @see android.app.PendingIntent#getIntentSender() */ - public @NonNull Builder setAuthentication(@Nullable IntentSender authentication, - @Nullable RemoteViews presentation) { + public @NonNull Builder setAuthentication(@NonNull AutofillId[] ids, + @Nullable IntentSender authentication, @Nullable RemoteViews presentation) { throwIfDestroyed(); + // TODO(b/33197203): assert ids is not null nor empty once old version is removed if (authentication == null ^ presentation == null) { throw new IllegalArgumentException("authentication and presentation" + " must be both non-null or null"); } mAuthentication = authentication; mPresentation = presentation; + mAuthenticationIds = ids; return this; } /** + * TODO(b/33197203): will be removed once clients use the version that takes ids + * @hide + * @deprecated + */ + @Deprecated + public @NonNull Builder setAuthentication(@Nullable IntentSender authentication, + @Nullable RemoteViews presentation) { + return setAuthentication(null, authentication, presentation); + } + + /** * Adds a new {@link Dataset} to this response. * * @return This builder. */ - public@NonNull Builder addDataset(@Nullable Dataset dataset) { + public @NonNull Builder addDataset(@Nullable Dataset dataset) { throwIfDestroyed(); if (dataset == null) { return this; @@ -273,6 +297,9 @@ public final class FillResponse implements Parcelable { * android.os.CancellationSignal, FillCallback)} and {@link AutofillService#onSaveRequest( * android.app.assist.AssistStructure, Bundle, SaveCallback)}. * + * <p>If this method is called on multiple {@link FillResponse} objects for the same + * activity, just the latest bundle is passed back to the service. + * * @param extras The response extras. * @return This builder. */ @@ -282,6 +309,7 @@ public final class FillResponse implements Parcelable { return this; } + /** * Builds a new {@link FillResponse} instance. You must provide at least * one dataset or some savable ids or an authentication with a presentation @@ -308,7 +336,7 @@ public final class FillResponse implements Parcelable { } ///////////////////////////////////// - // Object "contract" methods. // + // Object "contract" methods. // ///////////////////////////////////// @Override public String toString() { @@ -320,11 +348,13 @@ public final class FillResponse implements Parcelable { .append(", hasExtras=").append(mExtras != null) .append(", hasPresentation=").append(mPresentation != null) .append(", hasAuthentication=").append(mAuthentication != null) + .append(", authenticationSize=").append(mAuthenticationIds != null + ? mAuthenticationIds.length : "N/A") .toString(); } ///////////////////////////////////// - // Parcelable "contract" methods. // + // Parcelable "contract" methods. // ///////////////////////////////////// @Override @@ -337,6 +367,7 @@ public final class FillResponse implements Parcelable { parcel.writeTypedArrayList(mDatasets, flags); parcel.writeParcelable(mSaveInfo, flags); parcel.writeParcelable(mExtras, flags); + parcel.writeParcelableArray(mAuthenticationIds, flags); parcel.writeParcelable(mAuthentication, flags); parcel.writeParcelable(mPresentation, flags); } @@ -356,8 +387,8 @@ public final class FillResponse implements Parcelable { } builder.setSaveInfo(parcel.readParcelable(null)); builder.setExtras(parcel.readParcelable(null)); - builder.setAuthentication(parcel.readParcelable(null), - parcel.readParcelable(null)); + builder.setAuthentication(parcel.readParcelableArray(null, AutofillId.class), + parcel.readParcelable(null), parcel.readParcelable(null)); return builder.build(); } diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java index 6213d27bfc70..f75b7afe4c46 100644 --- a/core/java/android/service/autofill/SaveInfo.java +++ b/core/java/android/service/autofill/SaveInfo.java @@ -25,7 +25,6 @@ import android.content.IntentSender; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.view.autofill.AutoFillId; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; @@ -130,6 +129,16 @@ public final class SaveInfo implements Parcelable { */ public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; + /** + * Type used when the {@link FillResponse} represents just an username, without a password. + */ + public static final int SAVE_DATA_TYPE_USERNAME = 4; + + /** + * Type used when the {@link FillResponse} represents just an email address, without a password. + */ + public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5; + private final @SaveDataType int mType; private final CharSequence mNegativeActionTitle; private final IntentSender mNegativeActionListener; @@ -223,6 +232,8 @@ public final class SaveInfo implements Parcelable { case SAVE_DATA_TYPE_PASSWORD: case SAVE_DATA_TYPE_ADDRESS: case SAVE_DATA_TYPE_CREDIT_CARD: + case SAVE_DATA_TYPE_USERNAME: + case SAVE_DATA_TYPE_EMAIL_ADDRESS: mType = type; break; default: @@ -269,26 +280,6 @@ public final class SaveInfo implements Parcelable { return this; } - - /** - * @hide - */ - // TODO(b/33197203): temporary fix to runtime crash - public @NonNull Builder addSavableIds(@Nullable AutoFillId... ids) { - throwIfDestroyed(); - - if (ids == null || ids.length == 0) { - return this; - } - if (mRequiredIds == null) { - mRequiredIds = new AutofillId[ids.length]; - } - for (int i = 0; i < ids.length; i++) { - mRequiredIds[i] = ids[i].getDaRealId(); - } - return this; - } - /** * Sets an optional description to be shown in the UI when the user is asked to save. * diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java index e39d53fddaad..137cf577ab7c 100644 --- a/core/java/android/service/notification/Adjustment.java +++ b/core/java/android/service/notification/Adjustment.java @@ -36,10 +36,6 @@ public final class Adjustment implements Parcelable { private final int mUser; /** - * Data type: {@code String}. See {@link NotificationChannel#getId()}. - */ - public static final String KEY_CHANNEL_ID = "key_channel_id"; - /** * Data type: ArrayList of {@code String}, where each is a representation of a * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}. * See {@link android.app.Notification.Builder#addPerson(String)}. diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index 46609df6d3f2..6ec9d69583e3 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -138,69 +138,6 @@ public abstract class NotificationAssistantService extends NotificationListenerS } } - /** - * Creates a notification channel that notifications can be posted to for a given package. - * - * @param pkg The package to create a channel for. - * @param channel the channel to attempt to create. - */ - public void createNotificationChannel(@NonNull String pkg, - @NonNull NotificationChannel channel) { - if (!isBound()) return; - try { - getNotificationInterface().createNotificationChannelFromAssistant( - mWrapper, pkg, channel); - } catch (RemoteException e) { - Log.v(TAG, "Unable to contact notification manager", e); - throw e.rethrowFromSystemServer(); - } - } - - /** - * Updates a notification channel for a given package. - * - * @param pkg The package to the channel belongs to. - * @param channel the channel to attempt to update. - */ - public void updateNotificationChannel(@NonNull String pkg, - @NonNull NotificationChannel channel) { - if (!isBound()) return; - try { - getNotificationInterface().updateNotificationChannelFromAssistant( - mWrapper, pkg, channel); - } catch (RemoteException e) { - Log.v(TAG, "Unable to contact notification manager", e); - throw e.rethrowFromSystemServer(); - } - } - - /** - * Returns all notification channels belonging to the given package. - */ - public List<NotificationChannel> getNotificationChannels(@NonNull String pkg) { - if (!isBound()) return null; - try { - return getNotificationInterface().getNotificationChannelsFromAssistant( - mWrapper, pkg).getList(); - } catch (RemoteException e) { - Log.v(TAG, "Unable to contact notification manager", e); - throw e.rethrowFromSystemServer(); - } - } - - /** - * Deletes the given notification channel. - */ - public void deleteNotificationChannel(@NonNull String pkg, @NonNull String channelId) { - if (!isBound()) return; - try { - getNotificationInterface().deleteNotificationChannelFromAssistant( - mWrapper, pkg, channelId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper { @Override 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/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/text/method/DateKeyListener.java b/core/java/android/text/method/DateKeyListener.java index e14cd2cd725d..0accbf6c74e5 100644 --- a/core/java/android/text/method/DateKeyListener.java +++ b/core/java/android/text/method/DateKeyListener.java @@ -22,6 +22,7 @@ import android.text.InputType; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import java.util.HashMap; import java.util.LinkedHashSet; @@ -37,8 +38,11 @@ import java.util.Locale; public class DateKeyListener extends NumberKeyListener { public int getInputType() { - return InputType.TYPE_CLASS_DATETIME - | InputType.TYPE_DATETIME_VARIATION_DATE; + if (mNeedsAdvancedInput) { + return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + } else { + return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE; + } } @Override @@ -65,7 +69,13 @@ public class DateKeyListener extends NumberKeyListener final boolean success = NumberKeyListener.addDigits(chars, locale) && NumberKeyListener.addFormatCharsFromSkeletons( chars, locale, SKELETONS, SYMBOLS_TO_IGNORE); - mCharacters = success ? NumberKeyListener.collectionToArray(chars) : CHARACTERS; + if (success) { + mCharacters = NumberKeyListener.collectionToArray(chars); + mNeedsAdvancedInput = !ArrayUtils.containsAll(CHARACTERS, mCharacters); + } else { + mCharacters = CHARACTERS; + mNeedsAdvancedInput = false; + } } /** @@ -110,6 +120,7 @@ public class DateKeyListener extends NumberKeyListener }; private final char[] mCharacters; + private final boolean mNeedsAdvancedInput; private static final Object sLock = new Object(); @GuardedBy("sLock") diff --git a/core/java/android/text/method/DateTimeKeyListener.java b/core/java/android/text/method/DateTimeKeyListener.java index 62e3adea9b7a..551db5560128 100644 --- a/core/java/android/text/method/DateTimeKeyListener.java +++ b/core/java/android/text/method/DateTimeKeyListener.java @@ -22,6 +22,7 @@ import android.text.InputType; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import java.util.HashMap; import java.util.LinkedHashSet; @@ -37,10 +38,13 @@ import java.util.Locale; public class DateTimeKeyListener extends NumberKeyListener { public int getInputType() { - return InputType.TYPE_CLASS_DATETIME - | InputType.TYPE_DATETIME_VARIATION_NORMAL; + if (mNeedsAdvancedInput) { + return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + } else { + return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_NORMAL; + } } - + @Override @NonNull protected char[] getAcceptedChars() @@ -70,7 +74,13 @@ public class DateTimeKeyListener extends NumberKeyListener chars, locale, SKELETON_12HOUR, SYMBOLS_TO_IGNORE) && NumberKeyListener.addFormatCharsFromSkeleton( chars, locale, SKELETON_24HOUR, SYMBOLS_TO_IGNORE); - mCharacters = success ? NumberKeyListener.collectionToArray(chars) : CHARACTERS; + if (success) { + mCharacters = NumberKeyListener.collectionToArray(chars); + mNeedsAdvancedInput = !ArrayUtils.containsAll(CHARACTERS, mCharacters); + } else { + mCharacters = CHARACTERS; + mNeedsAdvancedInput = false; + } } /** @@ -114,6 +124,7 @@ public class DateTimeKeyListener extends NumberKeyListener }; private final char[] mCharacters; + private final boolean mNeedsAdvancedInput; private static final Object sLock = new Object(); @GuardedBy("sLock") diff --git a/core/java/android/text/method/DigitsKeyListener.java b/core/java/android/text/method/DigitsKeyListener.java index 26c69ab01da0..d9f2dcf2e896 100644 --- a/core/java/android/text/method/DigitsKeyListener.java +++ b/core/java/android/text/method/DigitsKeyListener.java @@ -27,6 +27,7 @@ import android.text.Spanned; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import java.util.HashMap; import java.util.LinkedHashSet; @@ -42,8 +43,12 @@ import java.util.Locale; public class DigitsKeyListener extends NumberKeyListener { private char[] mAccepted; + private boolean mNeedsAdvancedInput; private final boolean mSign; private final boolean mDecimal; + private final boolean mStringMode; + @Nullable + private final Locale mLocale; private static final String DEFAULT_DECIMAL_POINT_CHARS = "."; private static final String DEFAULT_SIGN_CHARS = "-+"; @@ -112,11 +117,17 @@ public class DigitsKeyListener extends NumberKeyListener this(locale, false, false); } - private void setToCompat(boolean sign, boolean decimal) { + private void setToCompat() { mDecimalPointChars = DEFAULT_DECIMAL_POINT_CHARS; mSignChars = DEFAULT_SIGN_CHARS; - final int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0); + final int kind = (mSign ? SIGN : 0) | (mDecimal ? DECIMAL : 0); mAccepted = COMPATIBILITY_CHARACTERS[kind]; + mNeedsAdvancedInput = false; + } + + private void calculateNeedForAdvancedInput() { + final int kind = (mSign ? SIGN : 0) | (mDecimal ? DECIMAL : 0); + mNeedsAdvancedInput = !ArrayUtils.containsAll(COMPATIBILITY_CHARACTERS[kind], mAccepted); } // Takes a sign string and strips off its bidi controls, if any. @@ -144,14 +155,16 @@ public class DigitsKeyListener extends NumberKeyListener public DigitsKeyListener(@Nullable Locale locale, boolean sign, boolean decimal) { mSign = sign; mDecimal = decimal; + mStringMode = false; + mLocale = locale; if (locale == null) { - setToCompat(sign, decimal); + setToCompat(); return; } LinkedHashSet<Character> chars = new LinkedHashSet<>(); final boolean success = NumberKeyListener.addDigits(chars, locale); if (!success) { - setToCompat(sign, decimal); + setToCompat(); return; } if (sign || decimal) { @@ -161,7 +174,7 @@ public class DigitsKeyListener extends NumberKeyListener final String plusString = stripBidiControls(symbols.getPlusSignString()); if (minusString.length() > 1 || plusString.length() > 1) { // non-BMP and multi-character signs are not supported. - setToCompat(sign, decimal); + setToCompat(); return; } final char minus = minusString.charAt(0); @@ -181,7 +194,7 @@ public class DigitsKeyListener extends NumberKeyListener final String separatorString = symbols.getDecimalSeparatorString(); if (separatorString.length() > 1) { // non-BMP and multi-character decimal separators are not supported. - setToCompat(sign, decimal); + setToCompat(); return; } final Character separatorChar = Character.valueOf(separatorString.charAt(0)); @@ -190,13 +203,19 @@ public class DigitsKeyListener extends NumberKeyListener } } mAccepted = NumberKeyListener.collectionToArray(chars); + calculateNeedForAdvancedInput(); } private DigitsKeyListener(@NonNull final String accepted) { mSign = false; mDecimal = false; + mStringMode = true; + mLocale = null; mAccepted = new char[accepted.length()]; accepted.getChars(0, accepted.length(), mAccepted, 0); + // Theoretically we may need advanced input, but for backward compatibility, we don't change + // the input type. + mNeedsAdvancedInput = false; } /** @@ -280,13 +299,38 @@ public class DigitsKeyListener extends NumberKeyListener return result; } - public int getInputType() { - int contentType = InputType.TYPE_CLASS_NUMBER; - if (mSign) { - contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED; + /** + * Returns a DigitsKeyListener based on an the settings of a existing DigitsKeyListener, with + * the locale modified. + * + * @hide + */ + @NonNull + public static DigitsKeyListener getInstance( + @Nullable Locale locale, + @NonNull DigitsKeyListener listener) { + if (listener.mStringMode) { + return listener; // string-mode DigitsKeyListeners have no locale. + } else { + return getInstance(locale, listener.mSign, listener.mDecimal); } - if (mDecimal) { - contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL; + } + + /** + * Returns the input type for the listener. + */ + public int getInputType() { + int contentType; + if (mNeedsAdvancedInput) { + contentType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + } else { + contentType = InputType.TYPE_CLASS_NUMBER; + if (mSign) { + contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED; + } + if (mDecimal) { + contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL; + } } return contentType; } diff --git a/core/java/android/text/method/TimeKeyListener.java b/core/java/android/text/method/TimeKeyListener.java index c9f9f9fc5dde..5b1db11777dd 100644 --- a/core/java/android/text/method/TimeKeyListener.java +++ b/core/java/android/text/method/TimeKeyListener.java @@ -22,6 +22,7 @@ import android.text.InputType; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import java.util.HashMap; import java.util.LinkedHashSet; @@ -37,8 +38,11 @@ import java.util.Locale; public class TimeKeyListener extends NumberKeyListener { public int getInputType() { - return InputType.TYPE_CLASS_DATETIME - | InputType.TYPE_DATETIME_VARIATION_TIME; + if (mNeedsAdvancedInput) { + return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + } else { + return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_TIME; + } } @Override @@ -70,7 +74,13 @@ public class TimeKeyListener extends NumberKeyListener chars, locale, SKELETON_12HOUR, SYMBOLS_TO_IGNORE) && NumberKeyListener.addFormatCharsFromSkeleton( chars, locale, SKELETON_24HOUR, SYMBOLS_TO_IGNORE); - mCharacters = success ? NumberKeyListener.collectionToArray(chars) : CHARACTERS; + if (success) { + mCharacters = NumberKeyListener.collectionToArray(chars); + mNeedsAdvancedInput = !ArrayUtils.containsAll(CHARACTERS, mCharacters); + } else { + mCharacters = CHARACTERS; + mNeedsAdvancedInput = false; + } } /** @@ -114,6 +124,7 @@ public class TimeKeyListener extends NumberKeyListener }; private final char[] mCharacters; + private final boolean mNeedsAdvancedInput; private static final Object sLock = new Object(); @GuardedBy("sLock") diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index d25e5f04fad2..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>(); @@ -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/KeyEvent.java b/core/java/android/view/KeyEvent.java index 5d8f336ef193..829b2b74ac9f 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -494,7 +494,8 @@ public class KeyEvent extends InputEvent implements Parcelable { * On TV remotes, switches to viewing live TV. */ public static final int KEYCODE_TV = 170; /** Key code constant: Window key. - * On TV remotes, toggles picture-in-picture mode or other windowing functions. */ + * On TV remotes, toggles picture-in-picture mode or other windowing functions. + * On Android Wear devices, triggers a display offset. */ public static final int KEYCODE_WINDOW = 171; /** Key code constant: Guide key. * On TV remotes, shows a programming guide. */ diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b12a7676dd7f..a376c83ac17c 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 @@ -2747,7 +2750,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG3_SCROLL_INDICATOR_END * 1 PFLAG3_ASSIST_BLOCKED * 1 PFLAG3_CLUSTER - * x * NO LONGER NEEDED, SHOULD BE REUSED * + * 1 PFLAG3_IS_AUTOFILLED * 1 PFLAG3_FINGER_DOWN * 1 PFLAG3_FOCUSED_BY_DEFAULT * 11 PFLAG3_AUTO_FILL_MODE_MASK @@ -2958,6 +2961,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int PFLAG3_CLUSTER = 0x8000; /** + * Flag indicating that the view is autofilled + * + * @see #isAutofilled() + * @see #setAutofilled(boolean) + */ + private static final int PFLAG3_IS_AUTOFILLED = 0x10000; + + /** * Indicates that the user is currently touching the screen. * Currently used for the tooltip positioning only. */ @@ -4076,7 +4087,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; @@ -7435,10 +7448,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <ol> * <li>Calling the proper getter method on {@link AutofillValue} to fetch the actual value. * <li>Passing the actual value to the equivalent setter in the view. - * <ol> + * </ol> * * <p>For example, a text-field view would call: - * * <pre class="prettyprint"> * CharSequence text = value.getTextValue(); * if (text != null) { @@ -7446,6 +7458,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * } * </pre> * + * <p>If the value is updated asyncronously the next call to + * {@link AutofillManager#notifyValueChanged(View)} must happen <u>after</u> the value was + * changed to the autofilled value. If not, the view will not be considered autofilled. + * * @param value value to be autofilled. */ public void autofill(@SuppressWarnings("unused") AutofillValue value) { @@ -7456,6 +7472,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * <p>See {@link #autofill(AutofillValue)} and * {@link #onProvideAutofillVirtualStructure(ViewStructure, int)} for more info. + * <p>To indicate that a virtual view was autofilled + * <code>@android:drawable/autofilled_highlight</code> should be drawn over it until the data + * changes. * * @param values map of values to be autofilled, keyed by virtual child id. */ @@ -7486,6 +7505,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide + */ + public boolean isAutofilled() { + return (mPrivateFlags3 & PFLAG3_IS_AUTOFILLED) != 0; + } + + /** * Gets the {@link View}'s current autofill value. * * <p>By default returns {@code null}, but views should override it (and @@ -9126,6 +9152,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide + */ + @TestApi + public void setAutofilled(boolean isAutofilled) { + boolean wasChanged = isAutofilled != isAutofilled(); + + if (wasChanged) { + if (isAutofilled) { + mPrivateFlags3 |= PFLAG3_IS_AUTOFILLED; + } else { + mPrivateFlags3 &= ~PFLAG3_IS_AUTOFILLED; + } + + invalidate(); + } + } + + /** * Set whether this view should have sound effects enabled for events such as * clicking and touching. * @@ -9968,6 +10012,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(); @@ -17089,9 +17156,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @CallSuper protected Parcelable onSaveInstanceState() { mPrivateFlags |= PFLAG_SAVE_STATE_CALLED; - if (mStartActivityRequestWho != null) { + if (mStartActivityRequestWho != null || isAutofilled()) { BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE); + + if (mStartActivityRequestWho != null) { + state.mSavedData |= BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED; + } + + if (isAutofilled()) { + state.mSavedData |= BaseSavedState.IS_AUTOFILLED; + } + state.mStartActivityRequestWhoSaved = mStartActivityRequestWho; + state.mIsAutofilled = isAutofilled(); return state; } return BaseSavedState.EMPTY_STATE; @@ -17161,7 +17238,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, + "other views do not use the same id."); } if (state != null && state instanceof BaseSavedState) { - mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved; + BaseSavedState baseState = (BaseSavedState) state; + + if ((baseState.mSavedData & BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED) != 0) { + mStartActivityRequestWho = baseState.mStartActivityRequestWhoSaved; + } + if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) { + setAutofilled(baseState.mIsAutofilled); + } } } @@ -17542,6 +17626,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { dispatchDraw(canvas); + drawAutofilledHighlight(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } @@ -17842,6 +17927,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); + drawAutofilledHighlight(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } @@ -17918,6 +18004,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { dispatchDraw(canvas); + drawAutofilledHighlight(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } @@ -18602,6 +18689,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Step 4, draw the children dispatchDraw(canvas); + drawAutofilledHighlight(canvas); + // Overlay is part of the content and draws beneath Foreground if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); @@ -18755,6 +18844,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, canvas.restoreToCount(saveCount); + drawAutofilledHighlight(canvas); + // Overlay is part of the content and draws beneath Foreground if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); @@ -20113,6 +20204,41 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Get the drawable to be overlayed when a view is autofilled + * + * @return The drawable + * + * @throws IllegalStateException if the drawable could not be found. + */ + @NonNull private Drawable getAutofilledDrawable() { + // Lazily load the isAutofilled drawable. + if (mAttachInfo.mAutofilledDrawable == null) { + mAttachInfo.mAutofilledDrawable = mContext.getDrawable(R.drawable.autofilled_highlight); + + if (mAttachInfo.mAutofilledDrawable == null) { + throw new IllegalStateException( + "Could not find android:drawable/autofilled_highlight"); + } + } + + return mAttachInfo.mAutofilledDrawable; + } + + /** + * Draw {@link View#isAutofilled()} highlight over view if the view is autofilled. + * + * @param canvas The canvas to draw on + */ + private void drawAutofilledHighlight(@NonNull Canvas canvas) { + if (isAutofilled()) { + Drawable autofilledHighlight = getAutofilledDrawable(); + + autofilledHighlight.setBounds(0, 0, getWidth(), getHeight()); + autofilledHighlight.draw(canvas); + } + } + + /** * Draw any foreground content for this view. * * <p>Foreground content may consist of scroll bars, a {@link #setForeground foreground} @@ -20780,11 +20906,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Look for a child view with the given id. If this view has the given - * id, return this view. + * Finds the first descendant view with the given ID, the view itself if + * the ID matches {@link #getId()}, or {@code null} if the ID is invalid + * (< 0) or there is no matching view in the hierarchy. + * <p> + * <strong>Note:</strong> In most cases -- depending on compiler support -- + * the resulting view is automatically cast to the target class type. If + * the target class type is unconstrained, an explicit cast may be + * necessary. * - * @param id The id to search for. - * @return The view that has the given id in the hierarchy or null + * @param id the ID to search for + * @return a view with given ID if found, or {@code null} otherwise + * @see View#findViewById(int) */ @Nullable public final <T extends View> T findViewById(@IdRes int id) { @@ -24270,7 +24403,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * state in {@link android.view.View#onSaveInstanceState()}. */ public static class BaseSavedState extends AbsSavedState { + static final int START_ACTIVITY_REQUESTED_WHO_SAVED = 0b1; + static final int IS_AUTOFILLED = 0b10; + + // Flags that describe what data in this state is valid + int mSavedData; String mStartActivityRequestWhoSaved; + boolean mIsAutofilled; /** * Constructor used when reading from a parcel. Reads the state of the superclass. @@ -24290,7 +24429,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public BaseSavedState(Parcel source, ClassLoader loader) { super(source, loader); + mSavedData = source.readInt(); mStartActivityRequestWhoSaved = source.readString(); + mIsAutofilled = source.readBoolean(); } /** @@ -24305,7 +24446,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); + + out.writeInt(mSavedData); out.writeString(mStartActivityRequestWhoSaved); + out.writeBoolean(mIsAutofilled); } public static final Parcelable.Creator<BaseSavedState> CREATOR @@ -24706,6 +24850,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Drawable mAccessibilityFocusDrawable; /** + * The drawable for highlighting autofilled views. + * + * @see #isAutofilled() + */ + Drawable mAutofilledDrawable; + + /** * Show where the margins, bounds and layout bounds are for each view. */ boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false); 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/view/Window.java b/core/java/android/view/Window.java index 0053caa78278..6dd8ecfa12e3 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1308,15 +1308,22 @@ public abstract class Window { } /** - * Finds a view that was identified by the id attribute from the XML that - * was processed in {@link android.app.Activity#onCreate}. This will - * implicitly call {@link #getDecorView} for you, with all of the - * associated side-effects. + * Finds a view that was identified by the {@code android:id} XML attribute + * that was processed in {@link android.app.Activity#onCreate}. This will + * implicitly call {@link #getDecorView} with all of the associated + * side-effects. + * <p> + * <strong>Note:</strong> In most cases -- depending on compiler support -- + * the resulting view is automatically cast to the target class type. If + * the target class type is unconstrained, an explicit cast may be + * necessary. * - * @return The view if found or null otherwise. + * @param id the ID to search for + * @return a view with given ID if found, or {@code null} otherwise + * @see View#findViewById(int) */ @Nullable - public View findViewById(@IdRes int id) { + public <T extends View> T findViewById(@IdRes int id) { return getDecorView().findViewById(id); } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 35276ccd5172..5e6ace785dc3 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -41,6 +41,7 @@ import android.util.Log; import android.view.IWindow; import android.view.View; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IntPair; import java.util.ArrayList; @@ -126,6 +127,8 @@ public final class AccessibilityManager { final Handler mHandler; + final Handler.Callback mCallback; + boolean mIsEnabled; int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; @@ -217,12 +220,12 @@ public final class AccessibilityManager { // is now off an exception will be thrown. We want to have the exception // enforcement to guard against apps that fire unnecessary accessibility // events when accessibility is off. - mHandler.obtainMessage(MyHandler.MSG_SET_STATE, state, 0).sendToTarget(); + mHandler.obtainMessage(MyCallback.MSG_SET_STATE, state, 0).sendToTarget(); } @Override public void notifyServicesStateChanged() { - mHandler.obtainMessage(MyHandler.MSG_NOTIFY_SERVICES_STATE_CHANGED).sendToTarget(); + mHandler.obtainMessage(MyCallback.MSG_NOTIFY_SERVICES_STATE_CHANGED).sendToTarget(); } @Override @@ -271,7 +274,8 @@ public final class AccessibilityManager { public AccessibilityManager(Context context, IAccessibilityManager service, int userId) { // Constructor can't be chained because we can't create an instance of an inner class // before calling another constructor. - mHandler = new MyHandler(context.getMainLooper()); + mCallback = new MyCallback(); + mHandler = new Handler(context.getMainLooper(), mCallback); mUserId = userId; synchronized (mLock) { tryConnectToServiceLocked(service); @@ -288,6 +292,7 @@ public final class AccessibilityManager { * @hide */ public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) { + mCallback = new MyCallback(); mHandler = handler; mUserId = userId; synchronized (mLock) { @@ -303,6 +308,14 @@ public final class AccessibilityManager { } /** + * @hide + */ + @VisibleForTesting + public Handler.Callback getCallback() { + return mCallback; + } + + /** * Returns if the accessibility in the system is enabled. * * @return True if accessibility is enabled, false otherwise. @@ -711,15 +724,15 @@ public final class AccessibilityManager { mIsHighTextContrastEnabled = highTextContrastEnabled; if (wasEnabled != enabled) { - mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED); + mHandler.sendEmptyMessage(MyCallback.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED); } if (wasTouchExplorationEnabled != touchExplorationEnabled) { - mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_EXPLORATION_STATE_CHANGED); + mHandler.sendEmptyMessage(MyCallback.MSG_NOTIFY_EXPLORATION_STATE_CHANGED); } if (wasHighTextContrastEnabled != highTextContrastEnabled) { - mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED); + mHandler.sendEmptyMessage(MyCallback.MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED); } } @@ -960,19 +973,15 @@ public final class AccessibilityManager { } } - private final class MyHandler extends Handler { + private final class MyCallback implements Handler.Callback { public static final int MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED = 1; public static final int MSG_NOTIFY_EXPLORATION_STATE_CHANGED = 2; public static final int MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED = 3; public static final int MSG_SET_STATE = 4; public static final int MSG_NOTIFY_SERVICES_STATE_CHANGED = 5; - public MyHandler(Looper looper) { - super(looper, null, false); - } - @Override - public void handleMessage(Message message) { + public boolean handleMessage(Message message) { switch (message.what) { case MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED: { handleNotifyAccessibilityStateChanged(); @@ -998,6 +1007,7 @@ public final class AccessibilityManager { } } break; } + return true; } } } diff --git a/core/java/android/view/autofill/AutoFillId.java b/core/java/android/view/autofill/AutoFillId.java deleted file mode 100644 index 081fb0289d76..000000000000 --- a/core/java/android/view/autofill/AutoFillId.java +++ /dev/null @@ -1,85 +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.view.autofill; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * @hide - * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype - */ -@Deprecated -public final class AutoFillId implements Parcelable { - - private final AutofillId mRealId; - - /** @hide */ - public AutoFillId(AutofillId daRealId) { - this.mRealId = daRealId; - } - - @Override - public int hashCode() { - return mRealId.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - final AutoFillId other = (AutoFillId) obj; - return mRealId.equals(other.mRealId); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeParcelable(mRealId, 0); - } - - private AutoFillId(Parcel parcel) { - mRealId = parcel.readParcelable(null); - } - - /** @hide */ - public AutofillId getDaRealId() { - return mRealId; - } - - /** @hide */ - public static AutoFillId forDaRealId(AutofillId id) { - return id == null ? null : new AutoFillId(id); - } - - public static final Parcelable.Creator<AutoFillId> CREATOR = - new Parcelable.Creator<AutoFillId>() { - @Override - public AutoFillId createFromParcel(Parcel source) { - return new AutoFillId(source); - } - - @Override - public AutoFillId[] newArray(int size) { - return new AutoFillId[size]; - } - }; -} diff --git a/core/java/android/view/autofill/AutoFillType.java b/core/java/android/view/autofill/AutoFillType.java deleted file mode 100644 index c508ba4eb892..000000000000 --- a/core/java/android/view/autofill/AutoFillType.java +++ /dev/null @@ -1,188 +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.view.autofill; - -import static android.view.autofill.Helper.DEBUG; - -import android.os.Parcel; -import android.os.Parcelable; -import android.view.View; - -/** - * Defines the type of a object that can be used to autofill a {@link View} so the - * {@link android.service.autofill.AutofillService} can use the proper {@link AutofillValue} to - * fill it. - * - * @hide - * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype - */ -@Deprecated -public final class AutoFillType implements Parcelable { - - // Cached instance for types that don't have subtype; it uses the "lazy initialization holder - // class idiom" (Effective Java, Item 71) to avoid memory utilization when autofill is not - // enabled. - private static class DefaultTypesHolder { - static final AutoFillType TEXT = new AutoFillType(TYPE_TEXT); - static final AutoFillType TOGGLE = new AutoFillType(TYPE_TOGGLE); - static final AutoFillType LIST = new AutoFillType(TYPE_LIST); - static final AutoFillType DATE = new AutoFillType(TYPE_DATE); - } - - private static final int TYPE_TEXT = 1; - private static final int TYPE_TOGGLE = 2; - private static final int TYPE_LIST = 3; - private static final int TYPE_DATE = 4; - - private final int mType; - - private AutoFillType(int type) { - mType = type; - } - - /** - * Checks if this is a type for a text field, which is filled by a {@link CharSequence}. - */ - public boolean isText() { - return mType == TYPE_TEXT; - } - - /** - * Checks if this is a a type for a togglable field, which is filled by a {@code boolean}. - */ - public boolean isToggle() { - return mType == TYPE_TOGGLE; - } - - /** - * Checks if this is a type for a selection list field, which is filled by a {@code integer} - * representing the element index inside the list (starting at {@code 0}. - */ - public boolean isList() { - return mType == TYPE_LIST; - } - - /** - * Checks if this is a type for a date and time, which is represented by a long representing - * the number of milliseconds since the standard base time known as "the epoch", namely - * January 1, 1970, 00:00:00 GMT (see {@link java.util.Date#getTime()}. - */ - public boolean isDate() { - return mType == TYPE_DATE; - } - - ///////////////////////////////////// - // Object "contract" methods. // - ///////////////////////////////////// - - @Override - public String toString() { - if (!DEBUG) return super.toString(); - - return "AutoFillType [type=" + mType + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + mType; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - final AutoFillType other = (AutoFillType) obj; - if (mType != other.mType) return false; - return true; - } - - ///////////////////////////////////// - // Parcelable "contract" methods. // - ///////////////////////////////////// - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeInt(mType); - } - - private AutoFillType(Parcel parcel) { - mType = parcel.readInt(); - } - - public static final Parcelable.Creator<AutoFillType> CREATOR = - new Parcelable.Creator<AutoFillType>() { - @Override - public AutoFillType createFromParcel(Parcel source) { - return new AutoFillType(source); - } - - @Override - public AutoFillType[] newArray(int size) { - return new AutoFillType[size]; - } - }; - - //////////////////// - // Factory methods // - //////////////////// - - /** - * Creates a text field type, which is filled by a {@link CharSequence}. - * - * <p>See {@link #isText()} for more info. - */ - public static AutoFillType forText() { - return DefaultTypesHolder.TEXT; - } - - /** - * Creates a type that can be toggled which is filled by a {@code boolean}. - * - * <p>See {@link #isToggle()} for more info. - */ - public static AutoFillType forToggle() { - return DefaultTypesHolder.TOGGLE; - } - - /** - * Creates a selection list, which is filled by a {@code integer} representing the element index - * inside the list (starting at {@code 0}. - * - * <p>See {@link #isList()} for more info. - */ - public static AutoFillType forList() { - return DefaultTypesHolder.LIST; - } - - /** - * Creates a type that represents a date. - * - * <p>See {@link #isDate()} for more info. - */ - public static AutoFillType forDate() { - return DefaultTypesHolder.DATE; - } -} diff --git a/core/java/android/view/autofill/AutoFillValue.java b/core/java/android/view/autofill/AutoFillValue.java deleted file mode 100644 index 4774d8f0a71a..000000000000 --- a/core/java/android/view/autofill/AutoFillValue.java +++ /dev/null @@ -1,181 +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.view.autofill; - -import static android.view.autofill.Helper.DEBUG; - -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.view.View; - -/** - * @hide - * @deprecated TODO(b/35956626): remove once clients use AutofillValue - */ -@Deprecated -public final class AutoFillValue implements Parcelable { - private final AutofillValue mRealValue; - - private AutoFillValue(AutofillValue daRealValue) { - this.mRealValue = daRealValue; - } - - /** - * Gets the value to autofill a text field. - * - * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info. - */ - public CharSequence getTextValue() { - return mRealValue.getTextValue(); - } - - /** - * Gets the value to autofill a toggable field. - * - * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info. - */ - public boolean getToggleValue() { - return mRealValue.getToggleValue(); - } - - /** - * Gets the value to autofill a selection list field. - * - * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info. - */ - public int getListValue() { - return mRealValue.getListValue(); - } - - /** - * Gets the value to autofill a date field. - * - * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info. - */ - public long getDateValue() { - return mRealValue.getDateValue(); - } - - ///////////////////////////////////// - // Object "contract" methods. // - ///////////////////////////////////// - - @Override - public int hashCode() { - return mRealValue.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - final AutoFillValue other = (AutoFillValue) obj; - return mRealValue.equals(other.mRealValue); - } - - @Override - public String toString() { - if (!DEBUG) return super.toString(); - - return mRealValue.toString(); - } - - ///////////////////////////////////// - // Parcelable "contract" methods. // - ///////////////////////////////////// - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeParcelable(mRealValue, 0); - } - - private AutoFillValue(Parcel parcel) { - mRealValue = parcel.readParcelable(null); - } - - public static final Parcelable.Creator<AutoFillValue> CREATOR = - new Parcelable.Creator<AutoFillValue>() { - @Override - public AutoFillValue createFromParcel(Parcel source) { - return new AutoFillValue(source); - } - - @Override - public AutoFillValue[] newArray(int size) { - return new AutoFillValue[size]; - } - }; - - //////////////////// - // Factory methods // - //////////////////// - /** - * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a text field. - * - * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info. - */ - @Nullable - public static AutoFillValue forText(@Nullable CharSequence value) { - return value == null ? null : new AutoFillValue(AutofillValue.forText(value)); - } - - /** - * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a toggable - * field. - * - * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info. - */ - public static AutoFillValue forToggle(boolean value) { - return new AutoFillValue(AutofillValue.forToggle(value)); - } - - /** - * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a selection - * list. - * - * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info. - */ - public static AutoFillValue forList(int value) { - return new AutoFillValue(AutofillValue.forList(value)); - } - - /** - * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a date. - * - * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info. - */ - public static AutoFillValue forDate(long date) { - return new AutoFillValue(AutofillValue.forDate(date)); - } - - /** @hide */ - public static AutoFillValue forDaRealValue(AutofillValue daRealValue) { - return new AutoFillValue(daRealValue); - } - - /** @hide */ - public AutofillValue getDaRealValue() { - return mRealValue; - } -} diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 19980fb45b4a..07fad60e5b6a 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -44,6 +44,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.List; +import java.util.Objects; /** * App entry point to the AutoFill Framework. @@ -91,6 +92,8 @@ public final class AutofillManager { */ public static final String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS"; + static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData"; + // Public flags start from the lowest bit /** * Indicates autofill was explicitly requested by the user. @@ -115,6 +118,9 @@ public final class AutofillManager { private boolean mHasSession; private boolean mEnabled; + /** If a view changes to this mapping the autofill operation was successful */ + @Nullable private ParcelableMap mLastAutofilledData; + /** @hide */ public interface AutofillClient { /** @@ -160,7 +166,31 @@ public final class AutofillManager { } /** - * Checkes whether autofill is enabled for the current user. + * Restore state after activity lifecycle + * + * @param savedInstanceState The state to be restored + * + * {@hide} + */ + public void onRestoreInstanceState(Bundle savedInstanceState) { + mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG); + } + + /** + * Save state before activity lifecycle + * + * @param outState Place to store the state + * + * {@hide} + */ + public void onSaveInstanceState(Bundle outState) { + if (mLastAutofilledData != null) { + outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData); + } + } + + /** + * Checks whether autofill is enabled for the current user. * * <p>Typically used to determine whether the option to explicitly request autofill should * be offered - see {@link #requestAutofill(View)}. @@ -311,12 +341,43 @@ public final class AutofillManager { * @param view view whose value changed. */ public void notifyValueChanged(View view) { + AutofillId id = null; + boolean valueWasRead = false; + AutofillValue value = null; + + // If the session is gone some fields might still be highlighted, hence we have to remove + // the isAutofilled property even if no sessions are active. + if (mLastAutofilledData == null) { + view.setAutofilled(false); + } else { + id = getAutofillId(view); + if (mLastAutofilledData.containsKey(id)) { + value = view.getAutofillValue(); + valueWasRead = true; + + if (Objects.equals(mLastAutofilledData.get(id), value)) { + view.setAutofilled(true); + } else { + view.setAutofilled(false); + mLastAutofilledData.remove(id); + } + } else { + view.setAutofilled(false); + } + } + if (!mEnabled || !mHasSession) { return; } - final AutofillId id = getAutofillId(view); - final AutofillValue value = view.getAutofillValue(); + if (id == null) { + id = getAutofillId(view); + } + + if (!valueWasRead) { + value = view.getAutofillValue(); + } + updateSession(id, null, value, FLAG_VALUE_CHANGED); } @@ -535,6 +596,23 @@ public final class AutofillManager { } } + /** + * Sets a view as autofilled if the current value is the {code targetValue}. + * + * @param view The view that is to be autofilled + * @param targetValue The value we want to fill into view + */ + private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) { + AutofillValue currentValue = view.getAutofillValue(); + if (Objects.equals(currentValue, targetValue)) { + if (mLastAutofilledData == null) { + mLastAutofilledData = new ParcelableMap(1); + } + mLastAutofilledData.put(getAutofillId(view), targetValue); + view.setAutofilled(true); + } + } + private void handleAutofill(IBinder windowToken, List<AutofillId> ids, List<AutofillValue> values) { final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken); @@ -568,7 +646,19 @@ public final class AutofillManager { } valuesByParent.put(id.getVirtualChildId(), value); } else { + // Mark the view as to be autofilled with 'value' + if (mLastAutofilledData == null) { + mLastAutofilledData = new ParcelableMap(itemCount - i); + } + mLastAutofilledData.put(id, value); + view.autofill(value); + + // Set as autofilled if the values match now, e.g. when the value was updated + // synchronously. + // If autofill happens async, the view is set to autofilled in notifyValueChanged. + setAutofilledIfValuesIs(view, value); + numApplied++; } } diff --git a/core/java/android/view/autofill/ParcelableMap.java b/core/java/android/view/autofill/ParcelableMap.java new file mode 100644 index 000000000000..f97b1a0d5f44 --- /dev/null +++ b/core/java/android/view/autofill/ParcelableMap.java @@ -0,0 +1,73 @@ +/* + * 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.view.autofill; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.HashMap; +import java.util.Map; + +/** + * A parcelable HashMap for {@link AutofillId} and {@link AutofillValue} + * + * {@hide} + */ +class ParcelableMap extends HashMap<AutofillId, AutofillValue> implements Parcelable { + ParcelableMap(int size) { + super(size); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(size()); + + for (Map.Entry<AutofillId, AutofillValue> entry : entrySet()) { + dest.writeParcelable(entry.getKey(), 0); + dest.writeParcelable(entry.getValue(), 0); + } + } + + public static final Parcelable.Creator<ParcelableMap> CREATOR = + new Parcelable.Creator<ParcelableMap>() { + @Override + public ParcelableMap createFromParcel(Parcel source) { + int size = source.readInt(); + + ParcelableMap map = new ParcelableMap(size); + + for (int i = 0; i < size; i++) { + AutofillId key = source.readParcelable(null); + AutofillValue value = source.readParcelable(null); + + map.put(key, value); + } + + return map; + } + + @Override + public ParcelableMap[] newArray(int size) { + return new ParcelableMap[size]; + } + }; +} 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/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index da00d9c970b1..0bf246042e00 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -1443,7 +1443,7 @@ public class HorizontalScrollView extends FrameLayout { @Override public void requestChildFocus(View child, View focused) { - if (focused.getRevealOnFocusHint()) { + if (focused != null && focused.getRevealOnFocusHint()) { if (!mIsLayoutDirty) { scrollToChild(focused); } else { diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index ab4cce479005..2e8faeec6f13 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -76,6 +76,7 @@ public class ListPopupWindow implements ShowableListMenu { private boolean mDropDownVerticalOffsetSet; private boolean mIsAnimatedFromAnchor = true; private boolean mOverlapAnchor; + private boolean mOverlapAnchorSet; private int mDropDownGravity = Gravity.NO_GRAVITY; @@ -681,7 +682,9 @@ public class ListPopupWindow implements ShowableListMenu { mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible); mPopup.setTouchInterceptor(mTouchInterceptor); mPopup.setEpicenterBounds(mEpicenterBounds); - mPopup.setOverlapAnchor(mOverlapAnchor); + if (mOverlapAnchorSet) { + mPopup.setOverlapAnchor(mOverlapAnchor); + } mPopup.showAsDropDown(getAnchorView(), mDropDownHorizontalOffset, mDropDownVerticalOffset, mDropDownGravity); mDropDownList.setSelection(ListView.INVALID_POSITION); @@ -1259,6 +1262,7 @@ public class ListPopupWindow implements ShowableListMenu { * @hide */ public void setOverlapAnchor(boolean overlap) { + mOverlapAnchorSet = true; mOverlapAnchor = overlap; } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 0a9e361a20c4..8fc4f5044261 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -1468,7 +1468,7 @@ public class ScrollView extends FrameLayout { @Override public void requestChildFocus(View child, View focused) { - if (focused.getRevealOnFocusHint()) { + if (focused != null && focused.getRevealOnFocusHint()) { if (!mIsLayoutDirty) { scrollToChild(focused); } else { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index b38366393a4d..7d9253b4db0a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -598,6 +598,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private Layout mLayout; private boolean mLocalesChanged = false; + // True if setKeyListener() has been explicitly called + private boolean mListenerChanged = false; + // True if internationalized input should be used for numbers and date and time. + private final boolean mUseInternationalizedInput; + @ViewDebug.ExportedProperty(category = "text") private int mGravity = Gravity.TOP | Gravity.START; private boolean mHorizontallyScrolling; @@ -610,7 +615,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; @@ -821,7 +826,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(); @@ -1356,6 +1361,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final boolean numberPasswordInputType = variation == (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD); + mUseInternationalizedInput = + context.getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O; + if (inputMethod != null) { Class<?> c; @@ -1398,15 +1406,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mEditor.mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE; } else if (numeric != 0) { createEditorIfNeeded(); - mEditor.mKeyListener = DigitsKeyListener.getInstance((numeric & SIGNED) != 0, - (numeric & DECIMAL) != 0); - inputType = EditorInfo.TYPE_CLASS_NUMBER; - if ((numeric & SIGNED) != 0) { - inputType |= EditorInfo.TYPE_NUMBER_FLAG_SIGNED; - } - if ((numeric & DECIMAL) != 0) { - inputType |= EditorInfo.TYPE_NUMBER_FLAG_DECIMAL; - } + mEditor.mKeyListener = DigitsKeyListener.getInstance( + mUseInternationalizedInput ? getTextLocale() : null, + (numeric & SIGNED) != 0, + (numeric & DECIMAL) != 0); + inputType = mEditor.mKeyListener.getInputType(); mEditor.mInputType = inputType; } else if (autotext || autocap != -1) { TextKeyListener.Capitalize cap; @@ -2308,19 +2312,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_autoText */ public void setKeyListener(KeyListener input) { + mListenerChanged = true; setKeyListenerOnly(input); fixFocusableAndClickableSettings(); if (input != null) { createEditorIfNeeded(); - try { - mEditor.mInputType = mEditor.mKeyListener.getInputType(); - } catch (IncompatibleClassChangeError e) { - mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT; - } - // Change inputType, without affecting transformation. - // No need to applySingleLine since mSingleLine is unchanged. - setInputTypeSingleLine(mSingleLine); + setInputTypeFromEditor(); } else { if (mEditor != null) mEditor.mInputType = EditorInfo.TYPE_NULL; } @@ -2329,6 +2327,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (imm != null) imm.restartInput(this); } + private void setInputTypeFromEditor() { + try { + mEditor.mInputType = mEditor.mKeyListener.getInputType(); + } catch (IncompatibleClassChangeError e) { + mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT; + } + // Change inputType, without affecting transformation. + // No need to applySingleLine since mSingleLine is unchanged. + setInputTypeSingleLine(mSingleLine); + } + private void setKeyListenerOnly(KeyListener input) { if (mEditor == null && input == null) return; // null is the default value @@ -3390,6 +3399,29 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mTextPaint.getTextLocales(); } + private void changeListenerLocaleTo(@NonNull Locale locale) { + if (mListenerChanged) { + // If a listener has been explicitly set, don't change it. We may break something. + return; + } + if (mEditor != null) { + KeyListener listener = mEditor.mKeyListener; + if (listener instanceof DigitsKeyListener) { + listener = DigitsKeyListener.getInstance(locale, (DigitsKeyListener) listener); + } else if (listener instanceof DateKeyListener) { + listener = DateKeyListener.getInstance(locale); + } else if (listener instanceof TimeKeyListener) { + listener = TimeKeyListener.getInstance(locale); + } else if (listener instanceof DateTimeKeyListener) { + listener = DateTimeKeyListener.getInstance(locale); + } else { + return; + } + setKeyListenerOnly(listener); + setInputTypeFromEditor(); + } + } + /** * Set the default {@link LocaleList} of the text in this TextView to a one-member list * containing just the given value. @@ -3401,6 +3433,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void setTextLocale(@NonNull Locale locale) { mLocalesChanged = true; mTextPaint.setTextLocale(locale); + changeListenerLocaleTo(locale); if (mLayout != null) { nullLayouts(); requestLayout(); @@ -3422,6 +3455,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void setTextLocales(@NonNull @Size(min = 1) LocaleList locales) { mLocalesChanged = true; mTextPaint.setTextLocales(locales); + changeListenerLocaleTo(locales.get(0)); if (mLayout != null) { nullLayouts(); requestLayout(); @@ -3433,7 +3467,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (!mLocalesChanged) { - mTextPaint.setTextLocales(LocaleList.getDefault()); + final LocaleList locales = LocaleList.getDefault(); + mTextPaint.setTextLocales(locales); + changeListenerLocaleTo(locales.get(0)); if (mLayout != null) { nullLayouts(); requestLayout(); @@ -3738,14 +3774,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(); @@ -3754,12 +3791,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; } /** @@ -5566,26 +5603,35 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener input = TextKeyListener.getInstance(autotext, cap); } else if (cls == EditorInfo.TYPE_CLASS_NUMBER) { input = DigitsKeyListener.getInstance( + mUseInternationalizedInput ? getTextLocale() : null, (type & EditorInfo.TYPE_NUMBER_FLAG_SIGNED) != 0, (type & EditorInfo.TYPE_NUMBER_FLAG_DECIMAL) != 0); + if (mUseInternationalizedInput) { + type = input.getInputType(); // Override type, if necessary for i18n. + } } else if (cls == EditorInfo.TYPE_CLASS_DATETIME) { + final Locale locale = mUseInternationalizedInput ? getTextLocale() : null; switch (type & EditorInfo.TYPE_MASK_VARIATION) { case EditorInfo.TYPE_DATETIME_VARIATION_DATE: - input = DateKeyListener.getInstance(); + input = DateKeyListener.getInstance(locale); break; case EditorInfo.TYPE_DATETIME_VARIATION_TIME: - input = TimeKeyListener.getInstance(); + input = TimeKeyListener.getInstance(locale); break; default: - input = DateTimeKeyListener.getInstance(); + input = DateTimeKeyListener.getInstance(locale); break; } + if (mUseInternationalizedInput) { + type = input.getInputType(); // Override type, if necessary for i18n. + } } else if (cls == EditorInfo.TYPE_CLASS_PHONE) { input = DialerKeyListener.getInstance(); } else { input = TextKeyListener.getInstance(); } setRawInputType(type); + mListenerChanged = false; if (direct) { createEditorIfNeeded(); mEditor.mKeyListener = input; @@ -7683,7 +7729,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) @@ -7725,7 +7771,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) { @@ -7775,7 +7821,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) @@ -8115,7 +8161,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/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl index f987a9f45e9f..caf35b39b324 100644 --- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl @@ -55,8 +55,8 @@ interface IAppWidgetService { in RemoteViews views); void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views); void notifyAppWidgetViewDataChanged(String packageName, in int[] appWidgetIds, int viewId); - ParceledListSlice getInstalledProvidersForProfile(int categoryFilter, - int profileId); + ParceledListSlice getInstalledProvidersForProfile(int categoryFilter, int profileId, + String packageName); AppWidgetProviderInfo getAppWidgetInfo(String callingPackage, int appWidgetId); boolean hasBindAppWidgetPermission(in String packageName, int userId); void setBindAppWidgetPermission(in String packageName, int userId, in boolean permission); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index b2636578d6a2..d19ffad37c95 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -114,7 +114,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 152 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 153 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -2006,107 +2006,92 @@ public class BatteryStatsImpl extends BatteryStats { * State for keeping track of two DurationTimers with different TimeBases, presumably where one * TimeBase is effectively a subset of the other. */ - public static class DualTimer { - // mMainTimer typically tracks the total time. May be pooled (but since it's a durationTimer, - // it also has the unpooled getTotalDurationMsLocked() for STATS_SINCE_CHARGED). - private final DurationTimer mMainTimer; + public static class DualTimer extends DurationTimer { + // This class both is a DurationTimer and also holds a second DurationTimer. + // The main timer (this) typically tracks the total time. It may be pooled (but since it's a + // durationTimer, it also has the unpooled getTotalDurationMsLocked() for + // STATS_SINCE_CHARGED). // mSubTimer typically tracks only part of the total time, such as background time, as // determined by a subTimeBase. It is NOT pooled. private final DurationTimer mSubTimer; /** - * Creates a DualTimer to hold a mMainTimer and a mSubTimer. - * The mMainTimer is based on the given timeBase and timerPool. + * Creates a DualTimer to hold a main timer (this) and a mSubTimer. + * The main timer (this) is based on the given timeBase and timerPool. * The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if - * the mMainTimer is. + * the main timer is. */ public DualTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool, TimeBase timeBase, TimeBase subTimeBase, Parcel in) { - mMainTimer = new DurationTimer(clocks, uid, type, timerPool, timeBase, in); + super(clocks, uid, type, timerPool, timeBase, in); mSubTimer = new DurationTimer(clocks, uid, type, null, subTimeBase, in); } /** - * Creates a DualTimer to hold a mMainTimer and a mSubTimer. - * The mMainTimer is based on the given timeBase and timerPool. + * Creates a DualTimer to hold a main timer (this) and a mSubTimer. + * The main timer (this) is based on the given timeBase and timerPool. * The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if - * the mMainTimer is. + * the main timer is. */ public DualTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool, TimeBase timeBase, TimeBase subTimeBase) { - mMainTimer = new DurationTimer(clocks, uid, type, timerPool, timeBase); + super(clocks, uid, type, timerPool, timeBase); mSubTimer = new DurationTimer(clocks, uid, type, null, subTimeBase); } - /** Get the main timer. */ - public DurationTimer getMainTimer() { - return mMainTimer; - } - /** Get the secondary timer. */ + @Override public DurationTimer getSubTimer() { return mSubTimer; } + @Override public void startRunningLocked(long elapsedRealtimeMs) { - mMainTimer.startRunningLocked(elapsedRealtimeMs); + super.startRunningLocked(elapsedRealtimeMs); mSubTimer.startRunningLocked(elapsedRealtimeMs); } + @Override public void stopRunningLocked(long elapsedRealtimeMs) { - mMainTimer.stopRunningLocked(elapsedRealtimeMs); + super.stopRunningLocked(elapsedRealtimeMs); mSubTimer.stopRunningLocked(elapsedRealtimeMs); } + @Override public void stopAllRunningLocked(long elapsedRealtimeMs) { - mMainTimer.stopAllRunningLocked(elapsedRealtimeMs); + super.stopAllRunningLocked(elapsedRealtimeMs); mSubTimer.stopAllRunningLocked(elapsedRealtimeMs); } - public void setMark(long elapsedRealtimeMs) { - mMainTimer.setMark(elapsedRealtimeMs); - mSubTimer.setMark(elapsedRealtimeMs); - } - + @Override public boolean reset(boolean detachIfReset) { boolean active = false; - active |= !mMainTimer.reset(detachIfReset); + active |= !super.reset(detachIfReset); active |= !mSubTimer.reset(detachIfReset); return !active; } + @Override public void detach() { - mMainTimer.detach(); + super.detach(); mSubTimer.detach(); } - /** - * Writes a possibly null DualTimer to a Parcel. - * - * @param out the Parcel to which to write. - * @param t a DualTimer, or null. - */ - public static void writeDualTimerToParcel(Parcel out, DualTimer t, long elapsedRealtimeUs) { - if (t != null) { - out.writeInt(1); - t.writeToParcel(out, elapsedRealtimeUs); - } else { - out.writeInt(0); - } - } - + @Override public void writeToParcel(Parcel out, long elapsedRealtimeUs) { - mMainTimer.writeToParcel(out, elapsedRealtimeUs); + super.writeToParcel(out, elapsedRealtimeUs); mSubTimer.writeToParcel(out, elapsedRealtimeUs); } + @Override public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) { - mMainTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); + super.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); mSubTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); } + @Override public void readSummaryFromParcelLocked(Parcel in) { - mMainTimer.readSummaryFromParcelLocked(in); + super.readSummaryFromParcelLocked(in); mSubTimer.readSummaryFromParcelLocked(in); } } @@ -5488,7 +5473,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * The statistics we have collected for this uid's jobs. */ - final OverflowArrayMap<StopwatchTimer> mJobStats; + final OverflowArrayMap<DualTimer> mJobStats; /** * The statistics we have collected for this uid's sensor activations. @@ -5533,10 +5518,10 @@ public class BatteryStatsImpl extends BatteryStats { mBsi.mOnBatteryTimeBase); } }; - mJobStats = mBsi.new OverflowArrayMap<StopwatchTimer>(uid) { - @Override public StopwatchTimer instantiateObject() { - return new StopwatchTimer(mBsi.mClocks, Uid.this, JOB, null, - mBsi.mOnBatteryTimeBase); + mJobStats = mBsi.new OverflowArrayMap<DualTimer>(uid) { + @Override public DualTimer instantiateObject() { + return new DualTimer(mBsi.mClocks, Uid.this, JOB, null, + mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase); } }; @@ -5918,7 +5903,7 @@ public class BatteryStatsImpl extends BatteryStats { if (mWifiScanTimer == null) { return 0; } - return mWifiScanTimer.getMainTimer().getTotalTimeLocked(elapsedRealtimeUs, which); + return mWifiScanTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @Override @@ -5926,12 +5911,12 @@ public class BatteryStatsImpl extends BatteryStats { if (mWifiScanTimer == null) { return 0; } - return mWifiScanTimer.getMainTimer().getCountLocked(which); + return mWifiScanTimer.getCountLocked(which); } @Override public int getWifiScanBackgroundCount(int which) { - if (mWifiScanTimer == null) { + if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) { return 0; } return mWifiScanTimer.getSubTimer().getCountLocked(which); @@ -5943,12 +5928,12 @@ public class BatteryStatsImpl extends BatteryStats { return 0; } final long elapsedRealtimeMs = (elapsedRealtimeUs + 500) / 1000; - return mWifiScanTimer.getMainTimer().getTotalDurationMsLocked(elapsedRealtimeMs) * 1000; + return mWifiScanTimer.getTotalDurationMsLocked(elapsedRealtimeMs) * 1000; } @Override public long getWifiScanBackgroundTime(final long elapsedRealtimeUs) { - if (mWifiScanTimer == null) { + if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) { return 0; } final long elapsedRealtimeMs = (elapsedRealtimeUs + 500) / 1000; @@ -6008,10 +5993,7 @@ public class BatteryStatsImpl extends BatteryStats { @Override public Timer getBluetoothScanTimer() { - if (mBluetoothScanTimer == null) { - return null; - } - return mBluetoothScanTimer.getMainTimer(); + return mBluetoothScanTimer; } @Override @@ -6361,9 +6343,9 @@ public class BatteryStatsImpl extends BatteryStats { } } mSyncStats.cleanup(); - final ArrayMap<String, StopwatchTimer> jobStats = mJobStats.getMap(); + final ArrayMap<String, DualTimer> jobStats = mJobStats.getMap(); for (int ij=jobStats.size()-1; ij>=0; ij--) { - StopwatchTimer timer = jobStats.valueAt(ij); + DualTimer timer = jobStats.valueAt(ij); if (timer.reset(false)) { jobStats.removeAt(ij); timer.detach(); @@ -6531,12 +6513,12 @@ public class BatteryStatsImpl extends BatteryStats { Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs); } - final ArrayMap<String, StopwatchTimer> jobStats = mJobStats.getMap(); + final ArrayMap<String, DualTimer> jobStats = mJobStats.getMap(); int NJ = jobStats.size(); out.writeInt(NJ); for (int ij=0; ij<NJ; ij++) { out.writeString(jobStats.keyAt(ij)); - StopwatchTimer timer = jobStats.valueAt(ij); + DualTimer timer = jobStats.valueAt(ij); Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs); } @@ -6756,8 +6738,8 @@ public class BatteryStatsImpl extends BatteryStats { for (int j = 0; j < numJobs; j++) { String jobName = in.readString(); if (in.readInt() != 0) { - mJobStats.add(jobName, new StopwatchTimer(mBsi.mClocks, Uid.this, JOB, null, - timeBase, in)); + mJobStats.add(jobName, new DualTimer(mBsi.mClocks, Uid.this, JOB, null, + mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, in)); } } @@ -7196,15 +7178,12 @@ public class BatteryStatsImpl extends BatteryStats { } void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) { - DualTimer.writeDualTimerToParcel(out, mTimer, elapsedRealtimeUs); + Timer.writeTimerToParcel(out, mTimer, elapsedRealtimeUs); } @Override public Timer getSensorTime() { - if (mTimer == null) { - return null; - } - return mTimer.getMainTimer(); + return mTimer; } @Override @@ -8023,7 +8002,7 @@ public class BatteryStatsImpl extends BatteryStats { } public void readJobSummaryFromParcelLocked(String name, Parcel in) { - StopwatchTimer timer = mJobStats.instantiateObject(); + DualTimer timer = mJobStats.instantiateObject(); timer.readSummaryFromParcelLocked(in); mJobStats.add(name, timer); } @@ -8084,14 +8063,14 @@ public class BatteryStatsImpl extends BatteryStats { } public void noteStartJobLocked(String name, long elapsedRealtimeMs) { - StopwatchTimer t = mJobStats.startObject(name); + DualTimer t = mJobStats.startObject(name); if (t != null) { t.startRunningLocked(elapsedRealtimeMs); } } public void noteStopJobLocked(String name, long elapsedRealtimeMs) { - StopwatchTimer t = mJobStats.stopObject(name); + DualTimer t = mJobStats.stopObject(name); if (t != null) { t.stopRunningLocked(elapsedRealtimeMs); } @@ -9149,7 +9128,7 @@ public class BatteryStatsImpl extends BatteryStats { final Uid uid = mUidStats.valueAt(i); // Sum the total scan power for all apps. - totalScanTimeMs += uid.mWifiScanTimer.getMainTimer().getTimeSinceMarkLocked( + totalScanTimeMs += uid.mWifiScanTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; // Sum the total time holding wifi lock for all apps. @@ -9170,7 +9149,7 @@ public class BatteryStatsImpl extends BatteryStats { for (int i = 0; i < uidStatsSize; i++) { final Uid uid = mUidStats.valueAt(i); - long scanTimeSinceMarkMs = uid.mWifiScanTimer.getMainTimer().getTimeSinceMarkLocked( + long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; if (scanTimeSinceMarkMs > 0) { // Set the new mark so that next time we get new data since this point. @@ -9444,7 +9423,7 @@ public class BatteryStatsImpl extends BatteryStats { continue; } - totalScanTimeMs += u.mBluetoothScanTimer.getMainTimer().getTimeSinceMarkLocked( + totalScanTimeMs += u.mBluetoothScanTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; } @@ -9465,7 +9444,7 @@ public class BatteryStatsImpl extends BatteryStats { continue; } - long scanTimeSinceMarkMs = u.mBluetoothScanTimer.getMainTimer().getTimeSinceMarkLocked( + long scanTimeSinceMarkMs = u.mBluetoothScanTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; if (scanTimeSinceMarkMs > 0) { // Set the new mark so that next time we get new data since this point. @@ -11526,7 +11505,7 @@ public class BatteryStatsImpl extends BatteryStats { syncStats.valueAt(is).writeSummaryFromParcelLocked(out, NOWREAL_SYS); } - final ArrayMap<String, StopwatchTimer> jobStats = u.mJobStats.getMap(); + final ArrayMap<String, DualTimer> jobStats = u.mJobStats.getMap(); int NJ = jobStats.size(); out.writeInt(NJ); for (int ij=0; ij<NJ; ij++) { diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index f4dd5a62112c..2c8e4e0414b6 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -236,6 +236,29 @@ public class ArrayUtils { return false; } + public static boolean contains(@Nullable char[] array, char value) { + if (array == null) return false; + for (char element : array) { + if (element == value) { + return true; + } + } + return false; + } + + /** + * Test if all {@code check} items are contained in {@code array}. + */ + public static <T> boolean containsAll(@Nullable char[] array, char[] check) { + if (check == null) return true; + for (char checkItem : check) { + if (!contains(array, checkItem)) { + return false; + } + } + return true; + } + public static long total(@Nullable long[] array) { long total = 0; if (array != null) { diff --git a/core/jni/Android.mk b/core/jni/Android.mk index da5d04d63457..33fabfc4c2a5 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -208,6 +208,7 @@ LOCAL_C_INCLUDES += \ $(TOP)/system/core/include \ $(TOP)/system/core/libappfuse/include \ $(TOP)/system/media/camera/include \ + $(TOP)/system/media/private/camera/include \ $(TOP)/system/netd/include \ external/giflib \ external/pdfium/public \ 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/Shader.cpp b/core/jni/android/graphics/Shader.cpp index a77ed626d0e8..214d97c213e4 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); @@ -60,11 +68,18 @@ static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, j } sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); - sk_sp<SkShader> shader = image->makeShader( - (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY, matrix); + sk_sp<SkShader> baseShader = image->makeShader( + (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY); + + SkShader* shader; + if (matrix) { + shader = baseShader->makeWithLocalMatrix(*matrix).release(); + } else { + shader = baseShader.release(); + } - ThrowIAE_IfNull(env, shader.get()); - return reinterpret_cast<jlong>(shader.release()); + ThrowIAE_IfNull(env, shader); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -87,9 +102,16 @@ static jlong LinearGradient_create1(JNIEnv* env, jobject o, jlong matrixPtr, #error Need to convert float array to SkScalar array before calling the following function. #endif - SkShader* shader = SkGradientShader::MakeLinear(pts, + sk_sp<SkShader> baseShader(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, NULL)); + + SkShader* shader; + if (matrix) { + shader = baseShader->makeWithLocalMatrix(*matrix).release(); + } else { + shader = baseShader.release(); + } env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT); ThrowIAE_IfNull(env, shader); @@ -108,8 +130,15 @@ static jlong LinearGradient_create2(JNIEnv* env, jobject o, jlong matrixPtr, colors[0] = color0; colors[1] = color1; - SkShader* s = SkGradientShader::MakeLinear(pts, colors, NULL, 2, - (SkShader::TileMode)tileMode, /* flags */ 0, matrix).release(); + sk_sp<SkShader> baseShader(SkGradientShader::MakeLinear(pts, colors, NULL, 2, + static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL)); + + SkShader* s; + if (matrix) { + s = baseShader->makeWithLocalMatrix(*matrix).release(); + } else { + s = baseShader.release(); + } ThrowIAE_IfNull(env, s); return reinterpret_cast<jlong>(s); @@ -133,9 +162,17 @@ static jlong RadialGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloa #error Need to convert float array to SkScalar array before calling the following function. #endif - SkShader* shader = SkGradientShader::MakeRadial(center, radius, + sk_sp<SkShader> baseShader = 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, NULL); + + SkShader* shader; + if (matrix) { + shader = baseShader->makeWithLocalMatrix(*matrix).release(); + } else { + shader = baseShader.release(); + } + env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT); @@ -153,10 +190,17 @@ static jlong RadialGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloa colors[0] = color0; colors[1] = color1; - SkShader* s = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2, - (SkShader::TileMode)tileMode, /* flags */ 0, matrix).release(); - ThrowIAE_IfNull(env, s); - return reinterpret_cast<jlong>(s); + sk_sp<SkShader> baseShader = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2, + static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL); + + SkShader* shader; + if (matrix) { + shader = baseShader->makeWithLocalMatrix(*matrix).release(); + } else { + shader = baseShader.release(); + } + ThrowIAE_IfNull(env, shader); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////// @@ -174,8 +218,17 @@ 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(); + sk_sp<SkShader> baseShader = SkGradientShader::MakeSweep(x, y, + reinterpret_cast<const SkColor*>(colors), pos, count, + sGradientShaderFlags, NULL); + + SkShader* shader; + if (matrix) { + shader = baseShader->makeWithLocalMatrix(*matrix).release(); + } else { + shader = baseShader.release(); + } + env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors), JNI_ABORT); ThrowIAE_IfNull(env, shader); @@ -188,10 +241,18 @@ static jlong SweepGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat SkColor colors[2]; colors[0] = color0; colors[1] = color1; - SkShader* s = SkGradientShader::MakeSweep(x, y, colors, NULL, 2, - /* flags */ 0, matrix).release(); - ThrowIAE_IfNull(env, s); - return reinterpret_cast<jlong>(s); + + sk_sp<SkShader> baseShader = SkGradientShader::MakeSweep(x, y, colors, + NULL, 2, sGradientShaderFlags, NULL); + + SkShader* shader; + if (matrix) { + shader = baseShader->makeWithLocalMatrix(*matrix).release(); + } else { + shader = baseShader.release(); + } + ThrowIAE_IfNull(env, shader); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index dae431057209..520302ee02b9 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -282,6 +282,25 @@ static jint nativeConfigDirectChannel(JNIEnv *_env, jclass _this, jlong sensorMa return mgr->configureDirectChannel(channelHandle, sensorHandle, rate); } +static jint nativeSetOperationParameter(JNIEnv *_env, jclass _this, jlong sensorManager, + jint type, jfloatArray floats, jintArray ints) { + SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager); + Vector<float> floatVector; + Vector<int32_t> int32Vector; + + if (floats != nullptr) { + floatVector.resize(_env->GetArrayLength(floats)); + _env->GetFloatArrayRegion(floats, 0, _env->GetArrayLength(floats), floatVector.editArray()); + } + + if (ints != nullptr) { + int32Vector.resize(_env->GetArrayLength(ints)); + _env->GetIntArrayRegion(ints, 0, _env->GetArrayLength(ints), int32Vector.editArray()); + } + + return mgr->setOperationParameter(type, floatVector, int32Vector); +} + //---------------------------------------------------------------------------- class Receiver : public LooperCallback { @@ -499,6 +518,10 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = { {"nativeConfigDirectChannel", "(JIII)I", (void*)nativeConfigDirectChannel }, + + {"nativeSetOperationParameter", + "(JI[F[I)I", + (void*)nativeSetOperationParameter }, }; static const JNINativeMethod gBaseEventQueueMethods[] = { diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp index 78a5735279ea..c11ce0fe338e 100644 --- a/core/jni/android_hardware_camera2_CameraMetadata.cpp +++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp @@ -36,6 +36,7 @@ #include <android/hardware/ICameraService.h> #include <binder/IServiceManager.h> #include <camera/CameraMetadata.h> +#include <camera_metadata_hidden.h> #include <camera/VendorTagDescriptor.h> #include <nativehelper/ScopedUtfChars.h> #include <nativehelper/ScopedPrimitiveArray.h> @@ -162,8 +163,10 @@ struct Helpers { extern "C" { static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType); -static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName); -static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag); +static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName, jlong vendorId); +static jint CameraMetadata_getTagFromKeyLocal(JNIEnv *env, jobject thiz, jstring keyName); +static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag, jlong vendorId); +static jint CameraMetadata_getTypeFromTagLocal(JNIEnv *env, jobject thiz, jint tag); static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz); // Less safe access to native pointer. Does NOT throw any Java exceptions if NULL. @@ -286,7 +289,9 @@ static jbyteArray CameraMetadata_readValues(JNIEnv *env, jobject thiz, jint tag) CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); if (metadata == NULL) return NULL; - int tagType = get_camera_metadata_tag_type(tag); + const camera_metadata_t *metaBuffer = metadata->getAndLock(); + int tagType = get_local_camera_metadata_tag_type(tag, metaBuffer); + metadata->unlock(metaBuffer); if (tagType == -1) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Tag (%d) did not have a type", tag); @@ -323,7 +328,9 @@ static void CameraMetadata_writeValues(JNIEnv *env, jobject thiz, jint tag, jbyt CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); if (metadata == NULL) return; - int tagType = get_camera_metadata_tag_type(tag); + const camera_metadata_t *metaBuffer = metadata->getAndLock(); + int tagType = get_local_camera_metadata_tag_type(tag, metaBuffer); + metadata->unlock(metaBuffer); if (tagType == -1) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Tag (%d) did not have a type", tag); @@ -528,14 +535,11 @@ static void CameraMetadata_writeToParcel(JNIEnv *env, jobject thiz, jobject parc static const JNINativeMethod gCameraMetadataMethods[] = { // static methods - { "nativeGetAllVendorKeys", - "(Ljava/lang/Class;)Ljava/util/ArrayList;", - (void *)CameraMetadata_getAllVendorKeys}, { "nativeGetTagFromKey", - "(Ljava/lang/String;)I", + "(Ljava/lang/String;J)I", (void *)CameraMetadata_getTagFromKey }, { "nativeGetTypeFromTag", - "(I)I", + "(IJ)I", (void *)CameraMetadata_getTypeFromTag }, { "nativeSetupGlobalVendorTagDescriptor", "()I", @@ -559,6 +563,12 @@ static const JNINativeMethod gCameraMetadataMethods[] = { { "nativeSwap", "(L" CAMERA_METADATA_CLASS_NAME ";)V", (void *)CameraMetadata_swap }, + { "nativeGetTagFromKeyLocal", + "(Ljava/lang/String;)I", + (void *)CameraMetadata_getTagFromKeyLocal }, + { "nativeGetTypeFromTagLocal", + "(I)I", + (void *)CameraMetadata_getTypeFromTagLocal }, { "nativeReadValues", "(I)[B", (void *)CameraMetadata_readValues }, @@ -568,6 +578,9 @@ static const JNINativeMethod gCameraMetadataMethods[] = { { "nativeDump", "()V", (void *)CameraMetadata_dump }, + { "nativeGetAllVendorKeys", + "(Ljava/lang/Class;)Ljava/util/ArrayList;", + (void *)CameraMetadata_getAllVendorKeys}, // Parcelable interface { "nativeReadFromParcel", "(Landroid/os/Parcel;)V", @@ -590,11 +603,11 @@ int register_android_hardware_camera2_CameraMetadata(JNIEnv *env) gMetadataOffsets.mResultKey = MakeGlobalRefOrDie(env, resultKeyClazz); gMetadataOffsets.mCharacteristicsConstr = GetMethodIDOrDie(env, gMetadataOffsets.mCharacteristicsKey, "<init>", - "(Ljava/lang/String;Ljava/lang/Class;)V"); + "(Ljava/lang/String;Ljava/lang/Class;J)V"); gMetadataOffsets.mRequestConstr = GetMethodIDOrDie(env, - gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V"); + gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;J)V"); gMetadataOffsets.mResultConstr = GetMethodIDOrDie(env, - gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V"); + gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;J)V"); // Store global references for primitive array types used by Keys jclass byteClazz = FindClassOrDie(env, "[B"); @@ -630,13 +643,76 @@ int register_android_hardware_camera2_CameraMetadata(JNIEnv *env) extern "C" { -static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) { +static jint CameraMetadata_getTypeFromTagLocal(JNIEnv *env, jobject thiz, jint tag) { + CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz); + metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID; + if (metadata) { + const camera_metadata_t *metaBuffer = metadata->getAndLock(); + vendorId = get_camera_metadata_vendor_id(metaBuffer); + metadata->unlock(metaBuffer); + } + + int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId); + if (tagType == -1) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Tag (%d) did not have a type", tag); + return -1; + } + + return tagType; +} + +static jint CameraMetadata_getTagFromKeyLocal(JNIEnv *env, jobject thiz, jstring keyName) { + ScopedUtfChars keyScoped(env, keyName); + const char *key = keyScoped.c_str(); + if (key == NULL) { + // exception thrown by ScopedUtfChars + return 0; + } + ALOGV("%s (key = '%s')", __FUNCTION__, key); + + uint32_t tag = 0; + sp<VendorTagDescriptor> vTags; + CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz); + if (metadata) { + sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache(); + if (cache.get()) { + const camera_metadata_t *metaBuffer = metadata->getAndLock(); + metadata_vendor_id_t vendorId = get_camera_metadata_vendor_id(metaBuffer); + metadata->unlock(metaBuffer); + cache->getVendorTagDescriptor(vendorId, &vTags); + } + } + status_t res = CameraMetadata::getTagFromName(key, vTags.get(), &tag); + if (res != OK) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Could not find tag for key '%s')", key); + } + return tag; +} + +static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) { + metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID; // Get all vendor tags sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor(); if (vTags.get() == nullptr) { - // No vendor tags. - return NULL; + sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache(); + if (cache.get() == nullptr) { + // No vendor tags. + return nullptr; + } + + CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); + if (metadata == NULL) return NULL; + + const camera_metadata_t *metaBuffer = metadata->getAndLock(); + vendorId = get_camera_metadata_vendor_id(metaBuffer); + cache->getVendorTagDescriptor(vendorId, &vTags); + metadata->unlock(metaBuffer); + if (vTags.get() == nullptr) { + return nullptr; + } } int count = vTags->getTagCount(); @@ -714,7 +790,7 @@ static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass return NULL; } - jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz); + jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz, vendorId); if (env->ExceptionCheck()) { return NULL; } @@ -731,8 +807,8 @@ static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass return arrayList; } -static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) { - +static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName, + jlong vendorId) { ScopedUtfChars keyScoped(env, keyName); const char *key = keyScoped.c_str(); if (key == NULL) { @@ -744,6 +820,13 @@ static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyN uint32_t tag = 0; sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor(); + if (vTags.get() == nullptr) { + sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache(); + if (cache.get() != nullptr) { + cache->getVendorTagDescriptor(vendorId, &vTags); + } + } + status_t res = CameraMetadata::getTagFromName(key, vTags.get(), &tag); if (res != OK) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", @@ -752,8 +835,8 @@ static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyN return tag; } -static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) { - int tagType = get_camera_metadata_tag_type(tag); +static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag, jlong vendorId) { + int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId); if (tagType == -1) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Tag (%d) did not have a type", tag); @@ -787,8 +870,24 @@ static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject t __FUNCTION__, res.toString8().string()); return res.serviceSpecificErrorCode(); } + if (0 < desc->getTagCount()) { + err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc); + } else { + sp<VendorTagDescriptorCache> cache = new VendorTagDescriptorCache(); + binder::Status res = cameraService->getCameraVendorTagCache(/*out*/cache.get()); + if (res.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_DISCONNECTED) { + // No camera module available, not an error on devices with no cameras + VendorTagDescriptorCache::clearGlobalVendorTagCache(); + return OK; + } else if (!res.isOk()) { + VendorTagDescriptorCache::clearGlobalVendorTagCache(); + ALOGE("%s: Failed to setup vendor tag cache: %s", + __FUNCTION__, res.toString8().string()); + return res.serviceSpecificErrorCode(); + } - err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc); + err = VendorTagDescriptorCache::setAsGlobalVendorTagCache(cache); + } if (err != OK) { return hardware::ICameraService::ERROR_INVALID_OPERATION; diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp index 80f9d5750d8a..71211948dac3 100644 --- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp +++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp @@ -182,7 +182,7 @@ static status_t produceFrame(const sp<ANativeWindow>& anw, err = native_window_dequeue_buffer_and_wait(anw.get(), &anb); if (err != NO_ERROR) return err; - sp<GraphicBuffer> buf(new GraphicBuffer(anb, /*keepOwnership*/false)); + sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); uint32_t grallocBufWidth = buf->getWidth(); uint32_t grallocBufHeight = buf->getHeight(); uint32_t grallocBufStride = buf->getStride(); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8e7f9d2119a5..313041e8e0c2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -528,6 +528,7 @@ <protected-broadcast android:name="com.android.internal.autofill.action.REQUEST_AUTOFILL" /> <protected-broadcast android:name="android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED" /> <protected-broadcast android:name="com.android.server.wm.ACTION_REVOKE_SYSTEM_ALERT_WINDOW_PERMISSION" /> + <protected-broadcast android:name="android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED" /> <protected-broadcast android:name="android.content.pm.action.SESSION_COMMITTED" /> <protected-broadcast android:name="android.os.action.USER_RESTRICTIONS_CHANGED" /> diff --git a/core/res/res/drawable/autofilled_highlight.xml b/core/res/res/drawable/autofilled_highlight.xml new file mode 100644 index 000000000000..c7aacb92af60 --- /dev/null +++ b/core/res/res/drawable/autofilled_highlight.xml @@ -0,0 +1,26 @@ +<?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. + --> + +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:insetLeft="4dp" + android:insetRight="4dp" + android:insetBottom="4dp" + android:insetTop="4dp"> + <shape> + <solid android:color="@color/autofilled_highlight" /> + </shape> +</inset> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index a165621b9696..5a2bf4eb3f62 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -17,27 +17,24 @@ <NotificationHeaderView xmlns:android="http://schemas.android.com/apk/res/android" + android:theme="@style/Theme.Material.Notification" android:id="@+id/notification_header" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="@dimen/notification_header_height" android:clipChildren="false" - android:paddingTop="@dimen/notification_header_padding_top" - android:paddingBottom="@dimen/notification_header_padding_bottom" - android:layout_marginBottom="5dp" - android:paddingStart="@dimen/notification_content_margin_start" - android:paddingEnd="16dp"> + style="?attr/notificationHeaderStyle"> <com.android.internal.widget.CachingIconView android:id="@+id/icon" - android:layout_width="@dimen/notification_header_icon_size" - android:layout_height="@dimen/notification_header_icon_size" + android:layout_width="?attr/notificationHeaderIconSize" + android:layout_height="?attr/notificationHeaderIconSize" android:layout_marginEnd="3dp" /> <TextView android:id="@+id/app_name_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.Material.Notification.Info" + android:textAppearance="?attr/notificationHeaderTextAppearance" android:layout_marginStart="3dp" android:layout_marginEnd="2dp" android:singleLine="true" @@ -46,7 +43,7 @@ android:id="@+id/header_text_divider" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.Material.Notification.Info" + android:textAppearance="?attr/notificationHeaderTextAppearance" android:layout_marginStart="2dp" android:layout_marginEnd="2dp" android:text="@string/notification_header_divider_symbol" @@ -55,7 +52,7 @@ android:id="@+id/header_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.Material.Notification.Info" + android:textAppearance="?attr/notificationHeaderTextAppearance" android:layout_marginStart="2dp" android:layout_marginEnd="2dp" android:visibility="gone" @@ -64,7 +61,7 @@ android:id="@+id/time_divider" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.Material.Notification.Info" + android:textAppearance="?attr/notificationHeaderTextAppearance" android:layout_marginStart="2dp" android:layout_marginEnd="2dp" android:text="@string/notification_header_divider_symbol" diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml index e2c68b51b733..026bc6e5cb43 100644 --- a/core/res/res/layout/notification_template_material_ambient.xml +++ b/core/res/res/layout/notification_template_material_ambient.xml @@ -20,8 +20,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:tag="ambient" + android:paddingStart="@dimen/notification_extra_margin_ambient" + android:paddingEnd="@dimen/notification_extra_margin_ambient" > - <include layout="@layout/notification_template_header" /> + <include layout="@layout/notification_template_header" + android:theme="@style/Theme.Material.Notification.Ambient" /> <LinearLayout android:id="@+id/notification_action_list_margin_target" @@ -52,7 +55,7 @@ android:ellipsize="marquee" android:fadingEdge="horizontal" android:textSize="20sp" - android:textColor="#e6fafafa" + android:textColor="#ffffffff" /> <TextView android:id="@+id/text" android:layout_width="match_parent" @@ -63,7 +66,7 @@ android:gravity="top" android:visibility="gone" android:textSize="16sp" - android:textColor="#ccfafafa" + android:textColor="#eeffffff" android:layout_marginTop="4dp" /> </LinearLayout> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index ee73b6983888..d26d952224f1 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -7739,13 +7739,6 @@ <attr name="settingsActivity" /> </declare-styleable> - <!-- TODO(b/35956626): temporary until clients change to AutofillService --> - <declare-styleable name="AutoFillService"> - <!-- Fully qualified class name of an activity that allows the user to modify - the settings for this service. --> - <attr name="settingsActivity" /> - </declare-styleable> - <!-- =============================== --> <!-- Contacts meta-data attributes --> <!-- =============================== --> @@ -8635,5 +8628,12 @@ <attr name="stackFromEnd" format="boolean" /> </declare-styleable> + <!-- @hide --> + <declare-styleable name="NotificationTheme"> + <attr name="notificationHeaderStyle" format="reference" /> + <attr name="notificationHeaderTextAppearance" format="reference" /> + <attr name="notificationHeaderIconSize" format="dimension" /> + </declare-styleable> + <attr name="lockPatternStyle" format="reference" /> </resources> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index f9fd57cf5df9..937fc6fba247 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -150,6 +150,7 @@ <color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color> <color name="accessibility_focus_highlight">#bf39b500</color> + <color name="autofilled_highlight">#4dffeb3b</color> <color name="system_notification_accent_color">#ff607D8B</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 85ecaff26b21..221e30874367 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2822,4 +2822,11 @@ <!-- Whether the device supports quick settings and its associated APIs --> <bool name="config_quickSettingsSupported">true</bool> + + <!-- The component name, flattened to a string, for the default autofill service + to enabled for an user. This service must be trusted, as it can be activated + without explicit consent of the user. If no autofill service with the + specified name exists on the device, autofill will be disabled by default. + --> + <string name="config_defaultAutofillService" translatable="false"></string> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 112964778053..c5316c6133cc 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -165,6 +165,9 @@ --> <dimen name="notification_content_plus_picture_margin_end">72dp</dimen> + <!-- The additional margin on the sides of the ambient view. --> + <dimen name="notification_extra_margin_ambient">16dp</dimen> + <!-- The height of the notification action list --> <dimen name="notification_action_list_height">56dp</dimen> @@ -188,6 +191,9 @@ <!-- size (width and height) of the icon in the notification header --> <dimen name="notification_header_icon_size">18dp</dimen> + <!-- size (width and height) of the icon in the notification header --> + <dimen name="notification_header_icon_size_ambient">20dp</dimen> + <!-- Height of a small notification in the status bar --> <dimen name="notification_min_height">92dp</dimen> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 624eb59eef99..1d1fd5e470a2 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2825,6 +2825,10 @@ <public name="autofill" /> </public-group> + <public-group type="drawable" first-id="0x010800b4"> + <public name="autofilled_highlight" /> + </public-group> + <!-- =============================================================== DO NOT ADD UN-GROUPED ITEMS HERE diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 831cf89e0e97..459b48fc5423 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -20,8 +20,10 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Suffix added to a number to signify size in bytes. --> <string name="byteShort">B</string> - <!-- Suffix added to a number to signify size in kilobytes (1000 bytes). --> - <string name="kilobyteShort">KB</string> + <!-- Suffix added to a number to signify size in kilobytes (1000 bytes). + If you retain the Latin script for the localization, please use the lowercase + 'k', as it signifies 1000 bytes as opposed to 1024 bytes. --> + <string name="kilobyteShort">kB</string> <!-- Suffix added to a number to signify size in megabytes. --> <string name="megabyteShort">MB</string> <!-- Suffix added to a number to signify size in gigabytes. --> @@ -3172,7 +3174,7 @@ <string name="alert_windows_notification_channel_name"><xliff:g id="name" example="Google Maps">%s</xliff:g> displaying over other apps</string> <!-- Notification title when an application is displaying ui on-top of other apps [CHAR LIMIT=30] --> - <string name="alert_windows_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is displaying over other apps.</string> + <string name="alert_windows_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is displaying over other apps</string> <!-- Notification body when an application is displaying ui on-top of other apps [CHAR LIMIT=NONE] --> <string name="alert_windows_notification_message">If you don’t want <xliff:g id="name" example="Google Maps">%s</xliff:g> to use this feature, tap to open settings and turn it off.</string> @@ -4600,6 +4602,10 @@ <string name="autofill_save_type_address">address</string> <!-- Label for the type of data being saved for autofill when it represents a credit card [CHAR LIMIT=NONE] --> <string name="autofill_save_type_credit_card">credit card</string> + <!-- Label for the type of data being saved for autofill when it represents an username [CHAR LIMIT=NONE] --> + <string name="autofill_save_type_username">username</string> + <!-- Label for the type of data being saved for autofill when it represents an email address [CHAR LIMIT=NONE] --> + <string name="autofill_save_type_email_address">email address</string> <!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for earthquake --> <string name="etws_primary_default_message_earthquake">Stay calm and seek shelter nearby.</string> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 8f061a3862a3..ec1661176ba6 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -488,6 +488,10 @@ please see styles_device_defaults.xml. <style name="TextAppearance.Material.Notification.Time" parent="TextAppearance.Material.Notification.Info" /> + <style name="TextAppearance.Material.Notification.Info.Ambient"> + <item name="textSize">@dimen/notification_text_size</item> + </style> + <style name="TextAppearance.Material.Notification.Emphasis"> <item name="textColor">#66000000</item> </style> @@ -1283,4 +1287,12 @@ please see styles_device_defaults.xml. <style name="DialogWindowTitle.Material.Light" /> + <style name="Notification.Header" parent=""> + <item name="paddingTop">@dimen/notification_header_padding_top</item> + <item name="paddingBottom">@dimen/notification_header_padding_bottom</item> + <item name="layout_marginBottom">5dp</item> + <item name="paddingStart">@dimen/notification_content_margin_start</item> + <item name="paddingEnd">16dp</item> + </style> + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ae05a69da20d..428db87fbf39 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1333,6 +1333,7 @@ <java-symbol type="drawable" name="ic_sim_card_multi_48px_clr" /> <java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" /> + <java-symbol type="drawable" name="autofilled_highlight"/> <java-symbol type="drawable" name="ic_account_circle" /> <java-symbol type="color" name="user_icon_1" /> @@ -2883,6 +2884,8 @@ <java-symbol type="string" name="autofill_save_type_password" /> <java-symbol type="string" name="autofill_save_type_address" /> <java-symbol type="string" name="autofill_save_type_credit_card" /> + <java-symbol type="string" name="autofill_save_type_username" /> + <java-symbol type="string" name="autofill_save_type_email_address" /> <!-- Accessibility fingerprint gestures --> <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" /> @@ -2944,6 +2947,7 @@ <java-symbol type="string" name="notification_channel_alerts" /> <java-symbol type="string" name="notification_channel_retail_mode" /> <java-symbol type="string" name="notification_channel_usb" /> + <java-symbol type="string" name="config_defaultAutofillService" /> <!-- ETWS primary messages --> <java-symbol type="string" name="etws_primary_default_message_earthquake" /> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index 008c817dd73c..9dafa7a02849 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -1321,6 +1321,19 @@ please see themes_device_defaults.xml. <item name="windowNoTitle">true</item> </style> + <!-- Theme for inflating notifications --> + <style name="Theme.Material.Notification" parent=""> + <item name="notificationHeaderStyle">@style/Notification.Header</item> + <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info</item> + <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size</item> + </style> + + <!-- Theme for inflating ambient notification --> + <style name="Theme.Material.Notification.Ambient"> + <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info.Ambient</item> + <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size_ambient</item> + </style> + <!-- Default theme for Settings and activities launched from Settings. --> <style name="Theme.Material.Settings" parent="Theme.Material.Light.LightStatusBar"> <item name="colorPrimary">@color/primary_material_settings_light</item> diff --git a/core/res/res/xml/time_zones_by_country.xml b/core/res/res/xml/time_zones_by_country.xml index 6c1ce448fa4a..22bfea191d96 100644 --- a/core/res/res/xml/time_zones_by_country.xml +++ b/core/res/res/xml/time_zones_by_country.xml @@ -31,11 +31,11 @@ <!-- ANTIGUA AND BARBUDA, -4:00 --> - <timezone code="ag">America/Port_of_Spain</timezone> + <timezone code="ag">America/Antigua</timezone> <!-- ANGUILLA, -4:00 --> - <timezone code="ai">America/Port_of_Spain</timezone> + <timezone code="ai">America/Anguilla</timezone> <!-- ALBANIA, 1:00 --> @@ -47,11 +47,11 @@ <!-- ANGOLA, 1:00 --> - <timezone code="ao">Africa/Lagos</timezone> + <timezone code="ao">Africa/Luanda</timezone> <!-- ANTARCTICA, 12:00 --> - <timezone code="aq">Pacific/Auckland</timezone> + <timezone code="aq">Antarctica/McMurdo</timezone> <!-- ANTARCTICA, 10:00 --> @@ -144,11 +144,11 @@ <!-- ARUBA, -4:00 --> - <timezone code="aw">America/Curacao</timezone> + <timezone code="aw">America/Aruba</timezone> <!-- ALAND ISLANDS, 2:00 --> - <timezone code="ax">Europe/Helsinki</timezone> + <timezone code="ax">Europe/Mariehamn</timezone> <!-- AZERBAIJAN, 4:00 --> @@ -156,7 +156,7 @@ <!-- BOSNIA AND HERZEGOVINA, 1:00 --> - <timezone code="ba">Europe/Belgrade</timezone> + <timezone code="ba">Europe/Sarajevo</timezone> <!-- BARBADOS, -4:00 --> @@ -172,7 +172,7 @@ <!-- BURKINA FASO, 0:00 --> - <timezone code="bf">Africa/Abidjan</timezone> + <timezone code="bf">Africa/Ouagadougou</timezone> <!-- BULGARIA, 2:00 --> @@ -180,19 +180,19 @@ <!-- BAHRAIN, 3:00 --> - <timezone code="bh">Asia/Qatar</timezone> + <timezone code="bh">Asia/Bahrain</timezone> <!-- BURUNDI, 2:00 --> - <timezone code="bi">Africa/Maputo</timezone> + <timezone code="bi">Africa/Bujumbura</timezone> <!-- BENIN, 1:00 --> - <timezone code="bj">Africa/Lagos</timezone> + <timezone code="bj">Africa/Porto-Novo</timezone> <!-- Saint Barthélemy, -4:00 --> - <timezone code="bl">America/Port_of_Spain</timezone> + <timezone code="bl">America/St_Barthelemy</timezone> <!-- BERMUDA, -4:00 --> @@ -208,7 +208,7 @@ <!-- Caribbean Netherlands, -4:00 --> - <timezone code="bq">America/Curacao</timezone> + <timezone code="bq">America/Kralendijk</timezone> <!-- BRAZIL, -2:00 --> @@ -248,7 +248,7 @@ <!-- BOTSWANA, 2:00 --> - <timezone code="bw">Africa/Maputo</timezone> + <timezone code="bw">Africa/Gaborone</timezone> <!-- BELARUS, 3:00 --> @@ -310,19 +310,19 @@ <!-- CONGO, THE DEMOCRATIC REPUBLIC OF THE, 2:00 --> - <timezone code="cd">Africa/Maputo</timezone> + <timezone code="cd">Africa/Lubumbashi</timezone> <!-- CONGO, THE DEMOCRATIC REPUBLIC OF THE, 1:00 --> - <timezone code="cd">Africa/Lagos</timezone> + <timezone code="cd">Africa/Kinshasa</timezone> <!-- CENTRAL AFRICAN REPUBLIC, 1:00 --> - <timezone code="cf">Africa/Lagos</timezone> + <timezone code="cf">Africa/Bangui</timezone> <!-- CONGO, 1:00 --> - <timezone code="cg">Africa/Lagos</timezone> + <timezone code="cg">Africa/Brazzaville</timezone> <!-- SWITZERLAND, 1:00 --> @@ -350,7 +350,7 @@ <!-- CAMEROON, 1:00 --> - <timezone code="cm">Africa/Lagos</timezone> + <timezone code="cm">Africa/Douala</timezone> <!-- CHINA, 8:00 --> @@ -388,6 +388,10 @@ <timezone code="cy">Asia/Nicosia</timezone> + <!-- CYPRUS, 3:00 --> + + <timezone code="cy">Asia/Famagusta</timezone> + <!-- CZECH REPUBLIC, 1:00 --> <timezone code="cz">Europe/Prague</timezone> @@ -395,11 +399,11 @@ <!-- GERMANY, 1:00 --> <timezone code="de">Europe/Berlin</timezone> - <timezone code="de">Europe/Zurich</timezone> + <timezone code="de">Europe/Busingen</timezone> <!-- DJIBOUTI, 3:00 --> - <timezone code="dj">Africa/Nairobi</timezone> + <timezone code="dj">Africa/Djibouti</timezone> <!-- DENMARK, 1:00 --> @@ -407,7 +411,7 @@ <!-- DOMINICA, -4:00 --> - <timezone code="dm">America/Port_of_Spain</timezone> + <timezone code="dm">America/Dominica</timezone> <!-- DOMINICAN REPUBLIC, -4:00 --> @@ -439,7 +443,7 @@ <!-- ERITREA, 3:00 --> - <timezone code="er">Africa/Nairobi</timezone> + <timezone code="er">Africa/Asmara</timezone> <!-- SPAIN, 1:00 --> @@ -452,7 +456,7 @@ <!-- ETHIOPIA, 3:00 --> - <timezone code="et">Africa/Nairobi</timezone> + <timezone code="et">Africa/Addis_Ababa</timezone> <!-- FINLAND, 2:00 --> @@ -468,7 +472,7 @@ <!-- MICRONESIA, FEDERATED STATES OF, 11:00 --> - <timezone code="fm">Pacific/Ponape</timezone> + <timezone code="fm">Pacific/Pohnpei</timezone> <timezone code="fm">Pacific/Kosrae</timezone> <!-- MICRONESIA, FEDERATED STATES OF, 10:00 --> @@ -485,7 +489,7 @@ <!-- GABON, 1:00 --> - <timezone code="ga">Africa/Lagos</timezone> + <timezone code="ga">Africa/Libreville</timezone> <!-- UNITED KINGDOM, 0:00 --> @@ -493,7 +497,7 @@ <!-- GRENADA, -4:00 --> - <timezone code="gd">America/Port_of_Spain</timezone> + <timezone code="gd">America/Grenada</timezone> <!-- GEORGIA, 4:00 --> @@ -505,7 +509,7 @@ <!-- GUERNSEY, 0:00 --> - <timezone code="gg">Europe/London</timezone> + <timezone code="gg">Europe/Guernsey</timezone> <!-- GHANA, 0:00 --> @@ -533,19 +537,19 @@ <!-- GAMBIA, 0:00 --> - <timezone code="gm">Africa/Abidjan</timezone> + <timezone code="gm">Africa/Banjul</timezone> <!-- GUINEA, 0:00 --> - <timezone code="gn">Africa/Abidjan</timezone> + <timezone code="gn">Africa/Conakry</timezone> <!-- GUADELOUPE, -4:00 --> - <timezone code="gp">America/Port_of_Spain</timezone> + <timezone code="gp">America/Guadeloupe</timezone> <!-- EQUATORIAL GUINEA, 1:00 --> - <timezone code="gq">Africa/Lagos</timezone> + <timezone code="gq">Africa/Malabo</timezone> <!-- GREECE, 2:00 --> @@ -581,7 +585,7 @@ <!-- CROATIA, 1:00 --> - <timezone code="hr">Europe/Belgrade</timezone> + <timezone code="hr">Europe/Zagreb</timezone> <!-- HAITI, -5:00 --> @@ -614,7 +618,7 @@ <!-- ISLE OF MAN, 0:00 --> - <timezone code="im">Europe/London</timezone> + <timezone code="im">Europe/Isle_of_Man</timezone> <!-- INDIA, 5:30 --> @@ -642,7 +646,7 @@ <!-- JERSEY, 0:00 --> - <timezone code="je">Europe/London</timezone> + <timezone code="je">Europe/Jersey</timezone> <!-- JAMAICA, -5:00 --> @@ -666,7 +670,7 @@ <!-- CAMBODIA, 7:00 --> - <timezone code="kh">Asia/Bangkok</timezone> + <timezone code="kh">Asia/Phnom_Penh</timezone> <!-- KIRIBATI, 14:00 --> @@ -682,11 +686,11 @@ <!-- COMOROS, 3:00 --> - <timezone code="km">Africa/Nairobi</timezone> + <timezone code="km">Indian/Comoro</timezone> <!-- SAINT KITTS AND NEVIS, -4:00 --> - <timezone code="kn">America/Port_of_Spain</timezone> + <timezone code="kn">America/St_Kitts</timezone> <!-- KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF, 8:30 --> @@ -698,11 +702,11 @@ <!-- KUWAIT, 3:00 --> - <timezone code="kw">Asia/Riyadh</timezone> + <timezone code="kw">Asia/Kuwait</timezone> <!-- CAYMAN ISLANDS, -5:00 --> - <timezone code="ky">America/Panama</timezone> + <timezone code="ky">America/Cayman</timezone> <!-- KAZAKHSTAN, 6:00 --> @@ -714,10 +718,11 @@ <timezone code="kz">Asia/Aqtau</timezone> <timezone code="kz">Asia/Oral</timezone> <timezone code="kz">Asia/Aqtobe</timezone> + <timezone code="kz">Asia/Atyrau</timezone> <!-- LAO PEOPLE'S DEMOCRATIC REPUBLIC, 7:00 --> - <timezone code="la">Asia/Bangkok</timezone> + <timezone code="la">Asia/Vientiane</timezone> <!-- LEBANON, 2:00 --> @@ -725,11 +730,11 @@ <!-- SAINT LUCIA, -4:00 --> - <timezone code="lc">America/Port_of_Spain</timezone> + <timezone code="lc">America/St_Lucia</timezone> <!-- LIECHTENSTEIN, 1:00 --> - <timezone code="li">Europe/Zurich</timezone> + <timezone code="li">Europe/Vaduz</timezone> <!-- SRI LANKA, 5:30 --> @@ -741,7 +746,7 @@ <!-- LESOTHO, 2:00 --> - <timezone code="ls">Africa/Johannesburg</timezone> + <timezone code="ls">Africa/Maseru</timezone> <!-- LITHUANIA, 2:00 --> @@ -773,15 +778,15 @@ <!-- MONTENEGRO, 1:00 --> - <timezone code="me">Europe/Belgrade</timezone> + <timezone code="me">Europe/Podgorica</timezone> <!-- Collectivity of Saint Martin, -4:00 --> - <timezone code="mf">America/Port_of_Spain</timezone> + <timezone code="mf">America/Marigot</timezone> <!-- MADAGASCAR, 3:00 --> - <timezone code="mg">Africa/Nairobi</timezone> + <timezone code="mg">Indian/Antananarivo</timezone> <!-- MARSHALL ISLANDS, 12:00 --> @@ -790,11 +795,11 @@ <!-- MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF, 1:00 --> - <timezone code="mk">Europe/Belgrade</timezone> + <timezone code="mk">Europe/Skopje</timezone> <!-- MALI, 0:00 --> - <timezone code="ml">Africa/Abidjan</timezone> + <timezone code="ml">Africa/Bamako</timezone> <!-- MYANMAR, 6:30 --> @@ -815,7 +820,7 @@ <!-- NORTHERN MARIANA ISLANDS, 10:00 --> - <timezone code="mp">Pacific/Guam</timezone> + <timezone code="mp">Pacific/Saipan</timezone> <!-- MARTINIQUE, -4:00 --> @@ -823,11 +828,11 @@ <!-- MAURITANIA, 0:00 --> - <timezone code="mr">Africa/Abidjan</timezone> + <timezone code="mr">Africa/Nouakchott</timezone> <!-- MONTSERRAT, -4:00 --> - <timezone code="ms">America/Port_of_Spain</timezone> + <timezone code="ms">America/Montserrat</timezone> <!-- MALTA, 1:00 --> @@ -843,7 +848,7 @@ <!-- MALAWI, 2:00 --> - <timezone code="mw">Africa/Maputo</timezone> + <timezone code="mw">Africa/Blantyre</timezone> <!-- MEXICO, -6:00 --> @@ -887,7 +892,7 @@ <!-- NIGER, 1:00 --> - <timezone code="ne">Africa/Lagos</timezone> + <timezone code="ne">Africa/Niamey</timezone> <!-- NORFOLK ISLAND, 11:30 --> @@ -911,7 +916,7 @@ <!-- NEPAL, 5:45 --> - <timezone code="np">Asia/Katmandu</timezone> + <timezone code="np">Asia/Kathmandu</timezone> <!-- NAURU, 12:00 --> @@ -931,7 +936,7 @@ <!-- OMAN, 4:00 --> - <timezone code="om">Asia/Dubai</timezone> + <timezone code="om">Asia/Muscat</timezone> <!-- PANAMA, -5:00 --> @@ -1070,6 +1075,7 @@ <timezone code="ru">Europe/Samara</timezone> <timezone code="ru">Europe/Astrakhan</timezone> <timezone code="ru">Europe/Ulyanovsk</timezone> + <timezone code="ru">Europe/Saratov</timezone> <!-- RUSSIAN FEDERATION, 3:00 --> @@ -1084,7 +1090,7 @@ <!-- RWANDA, 2:00 --> - <timezone code="rw">Africa/Maputo</timezone> + <timezone code="rw">Africa/Kigali</timezone> <!-- SAUDI ARABIA, 3:00 --> @@ -1112,35 +1118,35 @@ <!-- SAINT HELENA, 0:00 --> - <timezone code="sh">Africa/Abidjan</timezone> + <timezone code="sh">Atlantic/St_Helena</timezone> <!-- SLOVENIA, 1:00 --> - <timezone code="si">Europe/Belgrade</timezone> + <timezone code="si">Europe/Ljubljana</timezone> <!-- SVALBARD AND JAN MAYEN, 1:00 --> - <timezone code="sj">Europe/Oslo</timezone> + <timezone code="sj">Arctic/Longyearbyen</timezone> <!-- SLOVAKIA, 1:00 --> - <timezone code="sk">Europe/Prague</timezone> + <timezone code="sk">Europe/Bratislava</timezone> <!-- SIERRA LEONE, 0:00 --> - <timezone code="sl">Africa/Abidjan</timezone> + <timezone code="sl">Africa/Freetown</timezone> <!-- SAN MARINO, 1:00 --> - <timezone code="sm">Europe/Rome</timezone> + <timezone code="sm">Europe/San_Marino</timezone> <!-- SENEGAL, 0:00 --> - <timezone code="sn">Africa/Abidjan</timezone> + <timezone code="sn">Africa/Dakar</timezone> <!-- SOMALIA, 3:00 --> - <timezone code="so">Africa/Nairobi</timezone> + <timezone code="so">Africa/Mogadishu</timezone> <!-- SURINAME, -3:00 --> @@ -1148,11 +1154,11 @@ <!-- South Sudan, 3:00 --> - <timezone code="ss">Africa/Khartoum</timezone> + <timezone code="ss">Africa/Juba</timezone> <!-- SAO TOME AND PRINCIPE, 0:00 --> - <timezone code="st">Africa/Abidjan</timezone> + <timezone code="st">Africa/Sao_Tome</timezone> <!-- EL SALVADOR, -6:00 --> @@ -1160,7 +1166,7 @@ <!-- Sint Maarten, -4:00 --> - <timezone code="sx">America/Curacao</timezone> + <timezone code="sx">America/Lower_Princes</timezone> <!-- SYRIAN ARAB REPUBLIC, 2:00 --> @@ -1168,7 +1174,7 @@ <!-- SWAZILAND, 2:00 --> - <timezone code="sz">Africa/Johannesburg</timezone> + <timezone code="sz">Africa/Mbabane</timezone> <!-- TURKS AND CAICOS ISLANDS, -4:00 --> @@ -1182,13 +1188,9 @@ <timezone code="tf">Indian/Kerguelen</timezone> - <!-- FRENCH SOUTHERN TERRITORIES, 4:00 --> - - <timezone code="tf">Indian/Reunion</timezone> - <!-- TOGO, 0:00 --> - <timezone code="tg">Africa/Abidjan</timezone> + <timezone code="tg">Africa/Lome</timezone> <!-- THAILAND, 7:00 --> @@ -1236,7 +1238,7 @@ <!-- TANZANIA, UNITED REPUBLIC OF, 3:00 --> - <timezone code="tz">Africa/Nairobi</timezone> + <timezone code="tz">Africa/Dar_es_Salaam</timezone> <!-- UKRAINE, 2:00 --> @@ -1246,19 +1248,15 @@ <!-- UGANDA, 3:00 --> - <timezone code="ug">Africa/Nairobi</timezone> + <timezone code="ug">Africa/Kampala</timezone> <!-- UNITED STATES MINOR OUTLYING ISLANDS, 12:00 --> <timezone code="um">Pacific/Wake</timezone> - <!-- UNITED STATES MINOR OUTLYING ISLANDS, -10:00 --> - - <timezone code="um">Pacific/Honolulu</timezone> - <!-- UNITED STATES MINOR OUTLYING ISLANDS, -11:00 --> - <timezone code="um">Pacific/Pago_Pago</timezone> + <timezone code="um">Pacific/Midway</timezone> <!-- UNITED STATES, -5:00 --> @@ -1318,11 +1316,11 @@ <!-- HOLY SEE (VATICAN CITY STATE), 1:00 --> - <timezone code="va">Europe/Rome</timezone> + <timezone code="va">Europe/Vatican</timezone> <!-- SAINT VINCENT AND THE GRENADINES, -4:00 --> - <timezone code="vc">America/Port_of_Spain</timezone> + <timezone code="vc">America/St_Vincent</timezone> <!-- VENEZUELA, -4:00 --> @@ -1330,16 +1328,15 @@ <!-- VIRGIN ISLANDS, BRITISH, -4:00 --> - <timezone code="vg">America/Port_of_Spain</timezone> + <timezone code="vg">America/Tortola</timezone> <!-- VIRGIN ISLANDS, U.S., -4:00 --> - <timezone code="vi">America/Port_of_Spain</timezone> + <timezone code="vi">America/St_Thomas</timezone> <!-- VIET NAM, 7:00 --> <timezone code="vn">Asia/Ho_Chi_Minh</timezone> - <timezone code="vn">Asia/Bangkok</timezone> <!-- VANUATU, 11:00 --> @@ -1355,11 +1352,11 @@ <!-- YEMEN, 3:00 --> - <timezone code="ye">Asia/Riyadh</timezone> + <timezone code="ye">Asia/Aden</timezone> <!-- MAYOTTE, 3:00 --> - <timezone code="yt">Africa/Nairobi</timezone> + <timezone code="yt">Indian/Mayotte</timezone> <!-- SOUTH AFRICA, 2:00 --> @@ -1367,9 +1364,9 @@ <!-- ZAMBIA, 2:00 --> - <timezone code="zm">Africa/Maputo</timezone> + <timezone code="zm">Africa/Lusaka</timezone> <!-- ZIMBABWE, 2:00 --> - <timezone code="zw">Africa/Maputo</timezone> + <timezone code="zw">Africa/Harare</timezone> </timezones> diff --git a/core/tests/coretests/src/android/provider/FontsContractTest.java b/core/tests/coretests/src/android/provider/FontsContractTest.java index 6820e92866a2..1dd3ef6f012e 100644 --- a/core/tests/coretests/src/android/provider/FontsContractTest.java +++ b/core/tests/coretests/src/android/provider/FontsContractTest.java @@ -94,7 +94,8 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { FontResult fontResult = resultList.get(0); assertEquals(TestFontsProvider.TTC_INDEX, fontResult.getTtcIndex()); assertEquals(TestFontsProvider.VARIATION_SETTINGS, fontResult.getFontVariationSettings()); - assertEquals(TestFontsProvider.STYLE, fontResult.getStyle()); + assertEquals(TestFontsProvider.NORMAL_WEIGHT, fontResult.getWeight()); + assertEquals(TestFontsProvider.ITALIC, fontResult.getItalic()); assertNotNull(fontResult.getFileDescriptor()); } @@ -115,7 +116,8 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { FontResult fontResult = resultList.get(0); assertEquals(0, fontResult.getTtcIndex()); assertNull(fontResult.getFontVariationSettings()); - assertEquals(Typeface.NORMAL, fontResult.getStyle()); + assertEquals(400, fontResult.getWeight()); + assertFalse(fontResult.getItalic()); assertNotNull(fontResult.getFileDescriptor()); } @@ -146,10 +148,10 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { public void testGetFontFromProvider_resultFontNotFoundSecondRow() { MatrixCursor cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID, FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS, - FontsContract.Columns.STYLE, FontsContract.Columns.RESULT_CODE }); - cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, - FontsContract.Columns.RESULT_CODE_OK}); - cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, + FontsContract.Columns.WEIGHT, FontsContract.Columns.ITALIC, + FontsContract.Columns.RESULT_CODE }); + cursor.addRow(new Object[] { 1, 0, null, 400, 0, FontsContract.Columns.RESULT_CODE_OK}); + cursor.addRow(new Object[] { 1, 0, null, 400, 0, FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND}); mProvider.setCustomCursor(cursor); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); @@ -160,13 +162,12 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { public void testGetFontFromProvider_resultFontNotFoundOtherRow() { MatrixCursor cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID, FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS, - FontsContract.Columns.STYLE, FontsContract.Columns.RESULT_CODE }); - cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, - FontsContract.Columns.RESULT_CODE_OK}); - cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, + FontsContract.Columns.WEIGHT, FontsContract.Columns.ITALIC, + FontsContract.Columns.RESULT_CODE }); + cursor.addRow(new Object[] { 1, 0, null, 400, 0, FontsContract.Columns.RESULT_CODE_OK}); + cursor.addRow(new Object[] { 1, 0, null, 400, 0, FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND}); - cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, - FontsContract.Columns.RESULT_CODE_OK}); + cursor.addRow(new Object[] { 1, 0, null, 400, 0, FontsContract.Columns.RESULT_CODE_OK}); mProvider.setCustomCursor(cursor); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); @@ -176,10 +177,10 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { public void testGetFontFromProvider_resultCodeIsNegativeNumber() { MatrixCursor cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID, FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS, - FontsContract.Columns.STYLE, FontsContract.Columns.RESULT_CODE }); - cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, - FontsContract.Columns.RESULT_CODE_OK}); - cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, -5}); + FontsContract.Columns.WEIGHT, FontsContract.Columns.ITALIC, + FontsContract.Columns.RESULT_CODE }); + cursor.addRow(new Object[] { 1, 0, null, 400, 0, FontsContract.Columns.RESULT_CODE_OK}); + cursor.addRow(new Object[] { 1, 0, null, 400, 0, -5}); mProvider.setCustomCursor(cursor); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); diff --git a/core/tests/coretests/src/android/provider/TestFontsProvider.java b/core/tests/coretests/src/android/provider/TestFontsProvider.java index 13f53182a42e..46906dfa1284 100644 --- a/core/tests/coretests/src/android/provider/TestFontsProvider.java +++ b/core/tests/coretests/src/android/provider/TestFontsProvider.java @@ -37,7 +37,8 @@ public class TestFontsProvider extends ContentProvider { static final String AUTHORITY = "android.provider.TestFontsProvider"; static final int TTC_INDEX = 2; static final String VARIATION_SETTINGS = "'wdth' 1"; - static final int STYLE = Typeface.BOLD; + static final int NORMAL_WEIGHT = 400; + static final boolean ITALIC = false; private ParcelFileDescriptor mPfd; private boolean mReturnAllFields = true; @@ -81,8 +82,9 @@ public class TestFontsProvider extends ContentProvider { if (mReturnAllFields) { cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID, FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS, - FontsContract.Columns.STYLE, FontsContract.Columns.RESULT_CODE }); - cursor.addRow(new Object[] { 1, TTC_INDEX, VARIATION_SETTINGS, STYLE, mResultCode }); + FontsContract.Columns.WEIGHT, FontsContract.Columns.ITALIC, + FontsContract.Columns.RESULT_CODE }); + cursor.addRow(new Object[] { 1, TTC_INDEX, VARIATION_SETTINGS, 400, 0, mResultCode }); } else { cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID }); cursor.addRow(new Object[] { 1 }); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java index 6b52b98e4758..828333528efd 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java @@ -21,6 +21,7 @@ import android.app.ActivityManager; import android.os.BatteryStats; import android.os.WorkSource; import android.support.test.filters.SmallTest; +import android.util.ArrayMap; import junit.framework.TestCase; @@ -187,4 +188,65 @@ public class BatteryStatsBackgroundStatsTest extends TestCase { assertEquals((305 - 202) * 1000, actualTime); assertEquals((305 - 254) * 1000, bgTime); } + + @SmallTest + public void testJob() throws Exception { + final MockClocks clocks = new MockClocks(); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + final String jobName = "job_name"; + long curr = 0; // realtime in us + + // On battery + curr = 1000 * (clocks.realtime = clocks.uptime = 100); + bi.updateTimeBasesLocked(true, false, curr, curr); // on battery + // App in foreground + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + + // Start timer + curr = 1000 * (clocks.realtime = clocks.uptime = 151); + bi.noteJobStartLocked(jobName, UID); + + // Stop timer + curr = 1000 * (clocks.realtime = clocks.uptime = 161); + bi.noteJobFinishLocked(jobName, UID); + + // Start timer + curr = 1000 * (clocks.realtime = clocks.uptime = 202); + bi.noteJobStartLocked(jobName, UID); + + // Move to background + curr = 1000 * (clocks.realtime = clocks.uptime = 254); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + + // Off battery + curr = 1000 * (clocks.realtime = clocks.uptime = 305); + bi.updateTimeBasesLocked(false, false, curr, curr); // off battery + + // Stop timer + curr = 1000 * (clocks.realtime = clocks.uptime = 409); + bi.noteJobFinishLocked(jobName, UID); + + // Test + curr = 1000 * (clocks.realtime = clocks.uptime = 657); + final ArrayMap<String, ? extends BatteryStats.Timer> jobs = + bi.getUidStats().get(UID).getJobStats(); + assertEquals(1, jobs.size()); + BatteryStats.Timer timer = jobs.valueAt(0); + BatteryStats.Timer bgTimer = timer.getSubTimer(); + long time = timer.getTotalTimeLocked(curr, STATS_SINCE_CHARGED); + int count = timer.getCountLocked(STATS_SINCE_CHARGED); + int bgCount = bgTimer.getCountLocked(STATS_SINCE_CHARGED); + long bgTime = bgTimer.getTotalTimeLocked(curr, STATS_SINCE_CHARGED); + assertEquals((161 - 151 + 305 - 202) * 1000, time); + assertEquals(2, count); + assertEquals(1, bgCount); + assertEquals((305 - 254) * 1000, bgTime); + + // Test that a second job is separate. + curr = 1000 * (clocks.realtime = clocks.uptime = 3000); + final String jobName2 = "second_job"; + bi.noteJobStartLocked(jobName2, UID); + assertEquals(2, bi.getUidStats().get(UID).getJobStats().size()); + bi.noteJobFinishLocked(jobName2, UID); + } } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 560d29f1934d..5afe5e9f14e9 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -357,8 +357,8 @@ public class Typeface { long fontSize = fileChannel.size(); ByteBuffer fontBuffer = fileChannel.map( FileChannel.MapMode.READ_ONLY, 0, fontSize); - int style = result.getStyle(); - int weight = (style & BOLD) != 0 ? 700 : 400; + int weight = result.getWeight(); + int italic = result.getItalic() ? Builder.ITALIC : Builder.NORMAL; FontVariationAxis[] axes = null; try { axes = FontVariationAxis.fromFontVariationSettings( @@ -366,8 +366,8 @@ public class Typeface { } 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)) { + if (!fontFamily.addFontFromBuffer(fontBuffer, result.getTtcIndex(), + axes, weight, italic)) { Log.e(TAG, "Error creating font " + request.getQuery()); callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); 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/FontResult.java b/graphics/java/android/graphics/fonts/FontResult.java index 3ef99fdd62b7..20e736eb1fc3 100644 --- a/graphics/java/android/graphics/fonts/FontResult.java +++ b/graphics/java/android/graphics/fonts/FontResult.java @@ -35,7 +35,8 @@ public final class FontResult implements Parcelable { private final ParcelFileDescriptor mFileDescriptor; private final int mTtcIndex; private final String mFontVariationSettings; - private final int mStyle; + private final int mWeight; + private final boolean mItalic; /** * Creates a FontResult with all the information needed about a provided font. @@ -45,16 +46,16 @@ public final class FontResult implements Parcelable { * will fail to load in the client application. * @param ttcIndex If providing a TTC_INDEX file, the index to point to. Otherwise, 0. * @param fontVariationSettings If providing a variation font, the settings for it. May be null. - * @param style One of {@link android.graphics.Typeface#NORMAL}, - * {@link android.graphics.Typeface#BOLD}, {@link android.graphics.Typeface#ITALIC} - * or {@link android.graphics.Typeface#BOLD_ITALIC} + * @param weight An integer that indicates the font weight. + * @param italic A boolean that indicates the font is italic style or not. */ public FontResult(@NonNull ParcelFileDescriptor fileDescriptor, int ttcIndex, - @Nullable String fontVariationSettings, int style) { + @Nullable String fontVariationSettings, int weight, boolean italic) { mFileDescriptor = Preconditions.checkNotNull(fileDescriptor); mTtcIndex = ttcIndex; mFontVariationSettings = fontVariationSettings; - mStyle = style; + mWeight = weight; + mItalic = italic; } public ParcelFileDescriptor getFileDescriptor() { @@ -69,8 +70,12 @@ public final class FontResult implements Parcelable { return mFontVariationSettings; } - public int getStyle() { - return mStyle; + public int getWeight() { + return mWeight; + } + + public boolean getItalic() { + return mItalic; } @Override @@ -83,14 +88,16 @@ public final class FontResult implements Parcelable { dest.writeParcelable(mFileDescriptor, flags); dest.writeInt(mTtcIndex); dest.writeString(mFontVariationSettings); - dest.writeInt(mStyle); + dest.writeInt(mWeight); + dest.writeBoolean(mItalic); } private FontResult(Parcel in) { mFileDescriptor = in.readParcelable(null); mTtcIndex = in.readInt(); mFontVariationSettings = in.readString(); - mStyle = in.readInt(); + mWeight = in.readInt(); + mItalic = in.readBoolean(); } public static final Parcelable.Creator<FontResult> CREATOR = 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/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index e36ceb8a3e13..77a82ec8cacd 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -913,6 +913,29 @@ public final class AudioAttributes implements Parcelable { } } + // TODO remove, replaced by non-static API getVolumeControlStream() + /** + * Returns the stream type matching the given attributes for volume control. + * Use this method to derive the stream type needed to configure the volume + * control slider in an {@link android.app.Activity} with + * {@link android.app.Activity#setVolumeControlStream(int)}. + * <BR>Do not use this method to set the stream type on an audio player object + * (e.g. {@link AudioTrack}, {@link MediaPlayer}) as this is deprecated, + * use <code>AudioAttributes</code> instead. + * @param aa non-null AudioAttributes. + * @return a valid stream type for <code>Activity</code> or stream volume control that matches + * the attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct + * match. Note that <code>USE_DEFAULT_STREAM_TYPE</code> is not a valid value + * for {@link AudioManager#setStreamVolume(int, int, int)}. + * @deprecated use {@link #getVolumeControlStream()} + */ + public static int getVolumeControlStream(@NonNull AudioAttributes aa) { + if (aa == null) { + throw new IllegalArgumentException("Invalid null audio attributes"); + } + return toVolumeStreamType(true /*fromGetVolumeControlStream*/, aa); + } + /** * Returns the stream type matching this {@code AudioAttributes} instance for volume control. * Use this method to derive the stream type needed to configure the volume diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 2723826cc92e..97dd9337fc61 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -753,9 +753,12 @@ public final class MediaDrm { * @param init container-specific data, its meaning is interpreted based on the * mime type provided in the mimeType parameter. It could contain, for example, * the content ID, key ID or other data obtained from the content metadata that is - * required in generating the key request. init may be null when keyType is - * KEY_TYPE_RELEASE. - * @param mimeType identifies the mime type of the content + * required in generating the key request. May be null when keyType is + * KEY_TYPE_RELEASE or if the request is a renewal, i.e. not the first key + * request for the session. + * @param mimeType identifies the mime type of the content. May be null if the + * keyType is KEY_TYPE_RELEASE or if the request is a renewal, i.e. not the + * first key request for the session. * @param keyType specifes the type of the request. The request may be to acquire * keys for streaming or offline content, or to release previously acquired * keys, which are identified by a keySetId. @@ -779,13 +782,17 @@ public final class MediaDrm { * response is for an offline key request, a keySetId is returned that can be * used to later restore the keys to a new session with the method * {@link #restoreKeys}. - * When the response is for a streaming or release request, null is returned. + * When the response is for a streaming or release request, an empty byte array + * is returned. * * @param scope may be a sessionId or keySetId depending on the type of the * response. Scope should be set to the sessionId when the response is for either * streaming or offline key requests. Scope should be set to the keySetId when * the response is for a release request. * @param response the byte array response from the server + * @return If the response is for an offline request, the keySetId for the offline + * keys will be returned. If the response is for a streaming or release request + * an empty byte array will be returned. * * @throws NotProvisionedException if the response indicates that * reprovisioning is required diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index d8c3eca300a3..0b27d183021d 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -568,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. * @@ -836,9 +837,10 @@ 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 */ @IntDef({ @@ -2227,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 + "/" @@ -2541,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 + "/" @@ -2663,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 + "/" @@ -2709,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 + "/" diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 23bf3d6711f6..431d5d83a4bb 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -34,6 +34,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libbinder \ libmedia \ + libmediametrics \ libmediadrm \ libmidi \ libskia \ diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp index d5d9fc94bf29..0149d7623660 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -342,7 +342,7 @@ static void ImageWriter_dequeueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, } // New GraphicBuffer object doesn't own the handle, thus the native buffer // won't be freed when this object is destroyed. - sp<GraphicBuffer> buffer(new GraphicBuffer(anb, /*keepOwnership*/false)); + sp<GraphicBuffer> buffer(GraphicBuffer::from(anb)); // Note that: // 1. No need to lock buffer now, will only lock it when the first getPlanes() is called. diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java index cd0e587744ed..54442b37d7d9 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java @@ -154,18 +154,18 @@ public class CameraMetadataTest extends junit.framework.TestCase { @SmallTest public void testGetTypeFromTag() { assertEquals(TYPE_BYTE, - CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE)); + CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE, Long.MAX_VALUE)); assertEquals(TYPE_RATIONAL, - CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM)); + CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM, Long.MAX_VALUE)); assertEquals(TYPE_FLOAT, - CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS)); + CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS, Long.MAX_VALUE)); assertEquals(TYPE_BYTE, - CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE)); + CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE, Long.MAX_VALUE)); assertEquals(TYPE_INT32, - CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION)); + CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, Long.MAX_VALUE)); try { - CameraMetadataNative.getNativeType(0xDEADF00D); + CameraMetadataNative.getNativeType(0xDEADF00D, Long.MAX_VALUE); fail("No type should exist for invalid tag 0xDEADF00D"); } catch(IllegalArgumentException e) { } diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml index f9342b7c5899..fa5c3fff283f 100644 --- a/packages/CarrierDefaultApp/res/values/strings.xml +++ b/packages/CarrierDefaultApp/res/values/strings.xml @@ -6,6 +6,7 @@ <string name="no_data_notification_id">Your mobile data has been deactivated</string> <string name="portal_notification_detail">Tap to visit the %s website</string> <string name="no_data_notification_detail">Please contact your service provider %s</string> + <string name="mobile_data_status_notification_channel_name">Mobile data status</string> <string name="action_bar_label">Sign in to mobile network</string> <string name="ssl_error_warning">The network you’re trying to join has security issues.</string> <string name="ssl_error_example">For example, the login page may not belong to the organization shown.</string> diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java index 73ff3a9b5d1e..7fd16019267e 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java @@ -16,6 +16,7 @@ package com.android.carrierdefaultapp; import android.app.Notification; +import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; @@ -35,6 +36,7 @@ public class CarrierActionUtils { private static final String PORTAL_NOTIFICATION_TAG = "CarrierDefault.Portal.Notification"; private static final String NO_DATA_NOTIFICATION_TAG = "CarrierDefault.NoData.Notification"; + private static final String NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS = "mobile_data_status"; private static final int PORTAL_NOTIFICATION_ID = 0; private static final int NO_DATA_NOTIFICATION_ID = 1; private static boolean ENABLE = true; @@ -150,9 +152,18 @@ public class CarrierActionUtils { private static Notification getNotification(Context context, int titleId, int textId, PendingIntent pendingIntent) { final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class); + final NotificationManager notificationManager = context.getSystemService( + NotificationManager.class); final Resources resources = context.getResources(); final Bundle extras = Bundle.forPair(Notification.EXTRA_SUBSTITUTE_APP_NAME, resources.getString(R.string.android_system_label)); + /* Creates the notification channel and registers it with NotificationManager. If a channel + * with the same ID is already registered, NotificationManager will ignore this call. + */ + notificationManager.createNotificationChannel(new NotificationChannel( + NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS, + resources.getString(R.string.mobile_data_status_notification_channel_name), + NotificationManager.IMPORTANCE_DEFAULT)); Notification.Builder builder = new Notification.Builder(context) .setContentTitle(resources.getString(titleId)) .setContentText(String.format(resources.getString(textId), @@ -167,7 +178,8 @@ public class CarrierActionUtils { .setLocalOnly(true) .setWhen(System.currentTimeMillis()) .setShowWhen(false) - .setExtras(extras); + .setExtras(extras) + .setChannel(NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS); if (pendingIntent != null) { builder.setContentIntent(pendingIntent); diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml index 2e6ed69b4892..63850aea59e6 100644 --- a/packages/PrintSpooler/res/values/strings.xml +++ b/packages/PrintSpooler/res/values/strings.xml @@ -149,6 +149,12 @@ <!-- Description of printer info icon. [CHAR LIMIT=50] --> <string name="printer_info_desc">More information about this printer</string> + <!-- Label for the notification channel that contains print jobs without problems. [CHAR LIMIT=40] --> + <string name="notification_channel_progress">Running print jobs</string> + + <!-- Label for the notification channel that contains print jobs with problems. [CHAR LIMIT=40] --> + <string name="notification_channel_failure">Failed print jobs</string> + <!-- Notification that we could not create a file name for the printed PDF. [CHAR LIMIT=50] --> <string name="could_not_create_file">Could not create file</string> diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java index cd1d540d922b..9d737e0cd9d5 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java @@ -20,13 +20,12 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.Notification.Action; -import android.app.Notification.InboxStyle; +import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.AsyncTask; @@ -57,14 +56,14 @@ final class NotificationController { public static final String LOG_TAG = "NotificationController"; + private static final String NOTIFICATION_CHANNEL_PROGRESS = "PRINT_PROGRESS"; + private static final String NOTIFICATION_CHANNEL_FAILURES = "PRINT_FAILURES"; + private static final String INTENT_ACTION_CANCEL_PRINTJOB = "INTENT_ACTION_CANCEL_PRINTJOB"; private static final String INTENT_ACTION_RESTART_PRINTJOB = "INTENT_ACTION_RESTART_PRINTJOB"; private static final String EXTRA_PRINT_JOB_ID = "EXTRA_PRINT_JOB_ID"; - private static final String PRINT_JOB_NOTIFICATION_GROUP_KEY = "PRINT_JOB_NOTIFICATIONS"; - private static final String PRINT_JOB_NOTIFICATION_SUMMARY = "PRINT_JOB_NOTIFICATIONS_SUMMARY"; - private final Context mContext; private final NotificationManager mNotificationManager; @@ -78,6 +77,15 @@ final class NotificationController { mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mNotifications = new ArraySet<>(0); + + mNotificationManager.createNotificationChannel( + new NotificationChannel(NOTIFICATION_CHANNEL_PROGRESS, + context.getString(R.string.notification_channel_progress), + NotificationManager.IMPORTANCE_LOW)); + mNotificationManager.createNotificationChannel( + new NotificationChannel(NOTIFICATION_CHANNEL_FAILURES, + context.getString(R.string.notification_channel_failure), + NotificationManager.IMPORTANCE_DEFAULT)); } public void onUpdateNotifications(List<PrintJobInfo> printJobs) { @@ -104,13 +112,6 @@ final class NotificationController { final int numPrintJobs = printJobs.size(); - // Create summary notification - if (numPrintJobs > 1) { - createStackedNotification(printJobs); - } else { - mNotificationManager.cancel(PRINT_JOB_NOTIFICATION_SUMMARY, 0); - } - // Create per print job notification for (int i = 0; i < numPrintJobs; i++) { PrintJobInfo printJob = printJobs.get(i); @@ -178,16 +179,16 @@ final class NotificationController { */ private void createNotification(@NonNull PrintJobInfo printJob, @Nullable Action firstAction, @Nullable Action secondAction) { - Notification.Builder builder = new Notification.Builder(mContext) + Notification.Builder builder = new Notification.Builder(mContext, computeChannel(printJob)) .setContentIntent(createContentIntent(printJob.getId())) .setSmallIcon(computeNotificationIcon(printJob)) .setContentTitle(computeNotificationTitle(printJob)) .setWhen(System.currentTimeMillis()) .setOngoing(true) .setShowWhen(true) + .setOnlyAlertOnce(true) .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)) - .setGroup(PRINT_JOB_NOTIFICATION_GROUP_KEY); + com.android.internal.R.color.system_notification_accent_color)); if (firstAction != null) { builder.addAction(firstAction); @@ -238,43 +239,6 @@ final class NotificationController { createNotification(printJob, null, null); } - private void createStackedNotification(List<PrintJobInfo> printJobs) { - Notification.Builder builder = new Notification.Builder(mContext) - .setContentIntent(createContentIntent(null)) - .setWhen(System.currentTimeMillis()) - .setOngoing(true) - .setShowWhen(true) - .setGroup(PRINT_JOB_NOTIFICATION_GROUP_KEY) - .setGroupSummary(true); - - final int printJobCount = printJobs.size(); - - InboxStyle inboxStyle = new InboxStyle(); - - int icon = com.android.internal.R.drawable.ic_print; - for (int i = printJobCount - 1; i>= 0; i--) { - PrintJobInfo printJob = printJobs.get(i); - - inboxStyle.addLine(computeNotificationTitle(printJob)); - - // if any print job is in an error state show an error icon for the summary - if (printJob.getState() == PrintJobInfo.STATE_FAILED - || printJob.getState() == PrintJobInfo.STATE_BLOCKED) { - icon = com.android.internal.R.drawable.ic_print_error; - } - } - - builder.setSmallIcon(icon); - builder.setLargeIcon( - ((BitmapDrawable) mContext.getResources().getDrawable(icon, null)).getBitmap()); - builder.setNumber(printJobCount); - builder.setStyle(inboxStyle); - builder.setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)); - - mNotificationManager.notify(PRINT_JOB_NOTIFICATION_SUMMARY, 0, builder.build()); - } - private String computeNotificationTitle(PrintJobInfo printJob) { switch (printJob.getState()) { case PrintJobInfo.STATE_FAILED: { @@ -359,6 +323,22 @@ final class NotificationController { } } + private static String computeChannel(PrintJobInfo printJob) { + if (printJob.isCancelling()) { + return NOTIFICATION_CHANNEL_PROGRESS; + } + + switch (printJob.getState()) { + case PrintJobInfo.STATE_FAILED: + case PrintJobInfo.STATE_BLOCKED: { + return NOTIFICATION_CHANNEL_FAILURES; + } + default: { + return NOTIFICATION_CHANNEL_PROGRESS; + } + } + } + public static final class NotificationBroadcastReceiver extends BroadcastReceiver { @SuppressWarnings("hiding") private static final String LOG_TAG = "NotificationBroadcastReceiver"; diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk index 67ef40a23e4e..1ad4fea5e0a5 100644 --- a/packages/SettingsLib/Android.mk +++ b/packages/SettingsLib/Android.mk @@ -6,6 +6,7 @@ LOCAL_USE_AAPT2 := true LOCAL_MODULE := SettingsLib LOCAL_SHARED_ANDROID_LIBRARIES := \ + android-support-annotations \ android-support-v4 \ android-support-v7-recyclerview \ android-support-v7-preference \ 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/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java index 77f2e19abd97..a1c8de566578 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java @@ -122,9 +122,6 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { return true; } } - for (BluetoothDevice src : srcs) { - mService.disconnect(src); - } } return mService.connect(device); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java index 9b699bcfd38c..169aac9eb60f 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java @@ -132,11 +132,6 @@ final class HfpClientProfile implements LocalBluetoothProfile { return true; } } - // Handsfree HF only supports one source connection and hence it is OK to disconnect - // the only connected device here. - for (BluetoothDevice src : srcs) { - mService.disconnect(src); - } } return mService.connect(device); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java index a7621fcf02fb..6efa4687d913 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java @@ -115,10 +115,10 @@ public final class MapClientProfile implements LocalBluetoothProfile { public boolean connect(BluetoothDevice device) { if (mService == null) return false; List<BluetoothDevice> connectedDevices = getConnectedDevices(); - if (connectedDevices != null) { - for (BluetoothDevice connectedDevice : connectedDevices) { - mService.disconnect(connectedDevice); - } + if (connectedDevices != null && connectedDevices.contains(device)) { + // Connect to same device, Ignore it + Log.d(TAG,"Ignoring Connect"); + return true; } return mService.connect(device); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java index 72a3b3a00e22..bd37abeb3b0c 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java @@ -144,9 +144,6 @@ final class PbapClientProfile implements LocalBluetoothProfile { return true; } } - for (BluetoothDevice src : srcs) { - mService.disconnect(src); - } } Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress()); diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java index 4bfca9b61d6b..474de9074062 100644 --- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java +++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java @@ -211,7 +211,16 @@ public class ZoneGetter { if (preferLongName) { displayName = getZoneLongName(timeZoneNames, tz, now); } else { - displayName = timeZoneNames.getExemplarLocationName(tz.getID()); + // Canonicalize the zone ID for ICU. It will only return valid strings for zone IDs + // that match ICUs zone IDs (which are similar but not guaranteed the same as those + // in timezones.xml). timezones.xml and related files uses the IANA IDs. ICU IDs are + // stable and IANA IDs have changed over time so they have drifted. + // See http://bugs.icu-project.org/trac/ticket/13070 / http://b/36469833. + String canonicalZoneId = android.icu.util.TimeZone.getCanonicalID(tz.getID()); + if (canonicalZoneId == null) { + canonicalZoneId = tz.getID(); + } + displayName = timeZoneNames.getExemplarLocationName(canonicalZoneId); if (displayName == null || displayName.isEmpty()) { // getZoneExemplarLocation can return null. Fall back to the long name. displayName = getZoneLongName(timeZoneNames, tz, now); @@ -325,4 +334,4 @@ public class ZoneGetter { } } } -}
\ No newline at end of file +} diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java index 40abb6c8c2c4..88f133ce57c2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java @@ -41,8 +41,8 @@ public class PrivateStorageInfo { long privateTotalBytes = 0; for (VolumeInfo info : sm.getVolumes()) { if (info.getType() == VolumeInfo.TYPE_PRIVATE && info.isMountedReadable()) { - privateTotalBytes += stats.getTotalBytes(info.getFsUuid()); - privateFreeBytes += stats.getFreeBytes(info.getFsUuid()); + privateTotalBytes += sm.getTotalBytes(stats, info); + privateFreeBytes += sm.getFreeBytes(stats, info); } } return new PrivateStorageInfo(privateFreeBytes, privateTotalBytes); diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java index 320494c68faf..11060e6c1a05 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java @@ -16,6 +16,7 @@ package com.android.settingslib.deviceinfo; +import android.app.usage.StorageStatsManager; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; @@ -46,4 +47,14 @@ public class StorageManagerVolumeProvider implements StorageVolumeProvider { public VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume) { return mStorageManager.findEmulatedForPrivate(privateVolume); } + + @Override + public long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) { + return stats.getTotalBytes(volume.getFsUuid()); + } + + @Override + public long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) { + return stats.getFreeBytes(volume.getFsUuid()); + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java index 646c42f05a8e..e5d85d147bee 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java @@ -16,6 +16,7 @@ package com.android.settingslib.deviceinfo; +import android.app.usage.StorageStatsManager; import android.os.storage.VolumeInfo; import java.util.List; @@ -39,4 +40,18 @@ public interface StorageVolumeProvider { * Returns the emulated volume for a given private volume. */ VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume); + + /** + * Returns the total bytes for a given storage volume. + * + * @pre The volume is a private volume and is readable. + */ + long getTotalBytes(StorageStatsManager stats, VolumeInfo volume); + + /** + * Returns the free bytes for a given storage volume. + * + * @pre The volume is a private volume and is readable. + */ + long getFreeBytes(StorageStatsManager stats, VolumeInfo volume); } 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/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 1ea4183fc326..901848af43a0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -149,6 +149,7 @@ public class AccessPoint implements Comparable<AccessPoint> { private int mRankingScore = Integer.MIN_VALUE; private int mBadge = NetworkBadging.BADGING_NONE; + private boolean mIsScoredNetworkMetered = false; // used to co-relate internal vs returned accesspoint. int mId; @@ -248,6 +249,7 @@ public class AccessPoint implements Comparable<AccessPoint> { this.mScanResultCache.putAll(that.mScanResultCache); this.mId = that.mId; this.mBadge = that.mBadge; + this.mIsScoredNetworkMetered = that.mIsScoredNetworkMetered; this.mRankingScore = that.mRankingScore; } @@ -336,16 +338,32 @@ public class AccessPoint implements Comparable<AccessPoint> { builder.append(",level=").append(getLevel()); builder.append(",rankingScore=").append(mRankingScore); builder.append(",badge=").append(mBadge); + builder.append(",metered=").append(isMetered()); return builder.append(')').toString(); } /** + * Updates the AccessPoint rankingScore, metering, and badge, returning true if the data has + * changed. + * + * @param scoreCache The score cache to use to retrieve scores. + * @param scoringUiEnabled Whether to show scoring and badging UI. + */ + boolean update(WifiNetworkScoreCache scoreCache, boolean scoringUiEnabled) { + boolean scoreChanged = false; + if (scoringUiEnabled) { + scoreChanged = updateScores(scoreCache); + } + return updateMetered(scoreCache) || scoreChanged; + } + + /** * Updates the AccessPoint rankingScore and badge, returning true if the data has changed. * * @param scoreCache The score cache to use to retrieve scores. */ - boolean updateScores(WifiNetworkScoreCache scoreCache) { + private boolean updateScores(WifiNetworkScoreCache scoreCache) { int oldBadge = mBadge; int oldRankingScore = mRankingScore; mBadge = NetworkBadging.BADGING_NONE; @@ -366,6 +384,23 @@ public class AccessPoint implements Comparable<AccessPoint> { return (oldBadge != mBadge || oldRankingScore != mRankingScore); } + /** + * Updates the AccessPoint's metering based on {@link ScoredNetwork#meteredHint}, returning + * true if the metering changed. + */ + private boolean updateMetered(WifiNetworkScoreCache scoreCache) { + boolean oldMetering = mIsScoredNetworkMetered; + mIsScoredNetworkMetered = false; + for (ScanResult result : mScanResultCache.values()) { + ScoredNetwork score = scoreCache.getScoredNetwork(result); + if (score == null) { + continue; + } + mIsScoredNetworkMetered |= score.meteredHint; + } + return oldMetering == mIsScoredNetworkMetered; + } + private void evictOldScanResults() { long nowMs = SystemClock.elapsedRealtime(); for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) { @@ -474,6 +509,17 @@ public class AccessPoint implements Comparable<AccessPoint> { mSeen = seen; } + /** + * Returns if the network is marked metered. Metering can be marked through its config in + * {@link WifiConfiguration}, after connection in {@link WifiInfo}, or from a score config in + * {@link ScoredNetwork}. + */ + public boolean isMetered() { + return mIsScoredNetworkMetered + || (mConfig != null && mConfig.meteredHint) + || (mInfo != null && mInfo.getMeteredHint()); + } + public NetworkInfo getNetworkInfo() { return mNetworkInfo; } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index 8f8167ee8187..e82bf8114fe9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -183,7 +183,7 @@ public class AccessPointPreference extends Preference { } if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) { mFrictionSld.setState(STATE_SECURED); - } else if (mAccessPoint.getConfig() != null && mAccessPoint.getConfig().meteredHint) { + } else if (mAccessPoint.isMetered()) { mFrictionSld.setState(STATE_METERED); } Drawable drawable = mFrictionSld.getCurrent(); diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 50f294c00eaa..fc8c42cd663f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -543,11 +543,9 @@ public class WifiTracker { } } - if (mNetworkScoringUiEnabled) { - requestScoresForNetworkKeys(scoresToRequest); - for (AccessPoint ap : accessPoints) { - ap.updateScores(mScoreCache); - } + requestScoresForNetworkKeys(scoresToRequest); + for (AccessPoint ap : accessPoints) { + ap.update(mScoreCache, mNetworkScoringUiEnabled); } // Pre-sort accessPoints to speed preference insertion @@ -648,7 +646,7 @@ public class WifiTracker { if (ap.update(connectionConfig, mLastInfo, mLastNetworkInfo)) { reorder = true; } - if (mNetworkScoringUiEnabled && ap.updateScores(mScoreCache)) { + if (ap.update(mScoreCache, mNetworkScoringUiEnabled)) { reorder = true; } } @@ -659,15 +657,11 @@ public class WifiTracker { } /** - * Update all the internal access points rankingScores and badge. + * Update all the internal access points rankingScores, badge and metering. * * <p>Will trigger a resort and notify listeners of changes if applicable. */ private void updateNetworkScores() { - if (!mNetworkScoringUiEnabled) { - return; - } - // Lock required to prevent accidental copying of AccessPoint states while the modification // is in progress. see #copyAndNotifyListeners long before = System.currentTimeMillis(); @@ -679,7 +673,7 @@ public class WifiTracker { boolean reorder = false; for (int i = 0; i < mInternalAccessPoints.size(); i++) { - if (mInternalAccessPoints.get(i).updateScores(mScoreCache)) { + if (mInternalAccessPoints.get(i).update(mScoreCache, mNetworkScoringUiEnabled)) { reorder = true; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java index d3bdeb770c01..ab7c6d2eaeb3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java @@ -17,6 +17,7 @@ package com.android.settingslib.wifi; import android.content.Context; import android.os.Looper; +import android.support.annotation.Keep; /** * Factory method used to inject WifiTracker instances. @@ -26,14 +27,7 @@ public class WifiTrackerFactory { private static WifiTracker sTestingWifiTracker; - public static void enableTestingMode() { - sTestingMode = true; - } - - public static void disableTestingMode() { - sTestingMode = false; - } - + @Keep public static void setTestingWifiTracker(WifiTracker tracker) { sTestingWifiTracker = tracker; } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index 762d9f876cbe..3e01b34765c0 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -17,15 +17,19 @@ package com.android.settingslib.wifi; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.when; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.net.NetworkKey; +import android.net.ScoredNetwork; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; +import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.WifiSsid; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.pps.HomeSp; @@ -36,10 +40,11 @@ import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.SpannableString; import android.text.style.TtsSpan; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Collections; @@ -50,9 +55,11 @@ public class AccessPointTest { private static final String TEST_SSID = "test_ssid"; private Context mContext; + @Mock private WifiNetworkScoreCache mWifiNetworkScoreCache; @Before public void setUp() { + MockitoAnnotations.initMocks(this); mContext = InstrumentationRegistry.getTargetContext(); } @@ -74,6 +81,7 @@ public class AccessPointTest { @Test public void testCopyAccessPoint_dataShouldMatch() { WifiConfiguration configuration = createWifiConfiguration(); + configuration.meteredHint = true; NetworkInfo networkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE"); @@ -88,6 +96,7 @@ public class AccessPointTest { assertThat(originalAccessPoint.getBssid()).isEqualTo(copy.getBssid()); assertThat(originalAccessPoint.getConfig()).isEqualTo(copy.getConfig()); assertThat(originalAccessPoint.getSecurity()).isEqualTo(copy.getSecurity()); + assertThat(originalAccessPoint.isMetered()).isEqualTo(copy.isMetered()); assertThat(originalAccessPoint.compareTo(copy) == 0).isTrue(); } @@ -230,6 +239,55 @@ public class AccessPointTest { assertTrue(ap.isPasspointConfig()); } + @Test + public void testIsMetered_returnTrueWhenWifiConfigurationIsMetered() { + WifiConfiguration configuration = createWifiConfiguration(); + configuration.meteredHint = true; + + NetworkInfo networkInfo = + new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE"); + AccessPoint accessPoint = new AccessPoint(mContext, configuration); + WifiInfo wifiInfo = new WifiInfo(); + wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID)); + wifiInfo.setBSSID(configuration.BSSID); + wifiInfo.setNetworkId(configuration.networkId); + accessPoint.update(configuration, wifiInfo, networkInfo); + + assertTrue(accessPoint.isMetered()); + }; + + @Test + public void testIsMetered_returnTrueWhenWifiInfoIsMetered() { + WifiConfiguration configuration = createWifiConfiguration(); + + NetworkInfo networkInfo = + new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE"); + AccessPoint accessPoint = new AccessPoint(mContext, configuration); + WifiInfo wifiInfo = new WifiInfo(); + wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID)); + wifiInfo.setBSSID(configuration.BSSID); + wifiInfo.setNetworkId(configuration.networkId); + wifiInfo.setMeteredHint(true); + accessPoint.update(configuration, wifiInfo, networkInfo); + + assertTrue(accessPoint.isMetered()); + }; + + @Test + public void testIsMetered_returnTrueWhenScoredNetworkIsMetered() { + AccessPoint ap = createAccessPointWithScanResultCache(); + + when(mWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class))) + .thenReturn( + new ScoredNetwork( + null /* NetworkKey */, + null /* rssiCurve */, + true /* metered */)); + ap.update(mWifiNetworkScoreCache, false /* scoringUiEnabled */); + + assertTrue(ap.isMetered()); + }; + private AccessPoint createAccessPointWithScanResultCache() { Bundle bundle = new Bundle(); ArrayList<ScanResult> scanResults = new ArrayList<>(); diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index 02deb44d0cd4..b71915f30e1c 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -18,6 +18,7 @@ package com.android.settingslib.wifi; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; @@ -302,7 +303,7 @@ public class WifiTrackerTest { new ScoredNetwork( NETWORK_KEY_2, mockCurve2, - false /* meteredHint */, + true /* meteredHint */, attr2); WifiNetworkScoreCache scoreCache = mScoreCacheCaptor.getValue(); @@ -515,6 +516,23 @@ public class WifiTrackerTest { } @Test + public void scoreCacheUpdateMeteredShouldUpdateAccessPointMetering() + throws InterruptedException { + WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); + updateScoresAndWaitForAccessPointsChangedCallback(); + + List<AccessPoint> aps = tracker.getAccessPoints(); + + for (AccessPoint ap : aps) { + if (ap.getSsidStr().equals(SSID_1)) { + assertFalse(ap.isMetered()); + } else if (ap.getSsidStr().equals(SSID_2)) { + assertTrue(ap.isMetered()); + } + } + } + + @Test public void noBadgesShouldBeInsertedIntoAccessPointWhenScoringUiDisabled() throws InterruptedException { Settings.Global.putInt( diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 0d0ddf2046a7..169a034f447c 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -994,6 +994,15 @@ public class SettingsProvider extends ContentProvider { return false; } + private PackageInfo getCallingPackageInfo(int userId) { + try { + return mPackageManager.getPackageInfo(getCallingPackage(), + PackageManager.GET_SIGNATURES, userId); + } catch (RemoteException e) { + throw new IllegalStateException("Package " + getCallingPackage() + " doesn't exist"); + } + } + private Cursor getAllSecureSettings(int userId, String[] projection) { if (DEBUG) { Slog.v(LOG_TAG, "getAllSecureSettings(" + userId + ")"); @@ -1002,6 +1011,13 @@ public class SettingsProvider extends ContentProvider { // Resolve the userId on whose behalf the call is made. final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); + // The relevant "calling package" userId will be the owning userId for some + // profiles, and we can't do the lookup inside our [lock held] loop, so work out + // up front who the effective "new SSAID" user ID for that settings name will be. + final int ssaidUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, + Settings.Secure.ANDROID_ID); + final PackageInfo ssaidCallingPkg = getCallingPackageInfo(ssaidUserId); + synchronized (mLock) { List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_SECURE, callingUserId); @@ -1026,7 +1042,7 @@ public class SettingsProvider extends ContentProvider { // SETTINGS_FILE_SSAID, unless accessed by a system process. final Setting setting; if (isNewSsaidSetting(name)) { - setting = getSsaidSettingLocked(owningUserId); + setting = getSsaidSettingLocked(ssaidCallingPkg, owningUserId); } else { setting = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name); @@ -1060,14 +1076,17 @@ public class SettingsProvider extends ContentProvider { return settings != null ? settings.getNullSetting() : null; } - // Get the value. - synchronized (mLock) { - // As of Android O, the SSAID is read from an app-specific entry in table - // SETTINGS_FILE_SSAID, unless accessed by a system process. - if (isNewSsaidSetting(name)) { - return getSsaidSettingLocked(owningUserId); + // As of Android O, the SSAID is read from an app-specific entry in table + // SETTINGS_FILE_SSAID, unless accessed by a system process. + if (isNewSsaidSetting(name)) { + PackageInfo callingPkg = getCallingPackageInfo(owningUserId); + synchronized (mLock) { + return getSsaidSettingLocked(callingPkg, owningUserId); } + } + // Not the SSAID; do a straight lookup + synchronized (mLock) { return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name); } @@ -1078,7 +1097,7 @@ public class SettingsProvider extends ContentProvider { && UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID; } - private Setting getSsaidSettingLocked(int owningUserId) { + private Setting getSsaidSettingLocked(PackageInfo callingPkg, int owningUserId) { // Get uid of caller (key) used to store ssaid value String name = Integer.toString( UserHandle.getUid(owningUserId, UserHandle.getAppId(Binder.getCallingUid()))); @@ -1093,7 +1112,7 @@ public class SettingsProvider extends ContentProvider { // Lazy initialize ssaid if not yet present in ssaid table. if (ssaid == null || ssaid.isNull() || ssaid.getValue() == null) { - return mSettingsRegistry.generateSsaidLocked(getCallingPackage(), owningUserId); + return mSettingsRegistry.generateSsaidLocked(callingPkg, owningUserId); } return ssaid; @@ -2070,15 +2089,7 @@ public class SettingsProvider extends ContentProvider { return ByteBuffer.allocate(4).putInt(data.length).array(); } - public Setting generateSsaidLocked(String packageName, int userId) { - final PackageInfo packageInfo; - try { - packageInfo = mPackageManager.getPackageInfo(packageName, - PackageManager.GET_SIGNATURES, userId); - } catch (RemoteException e) { - throw new IllegalStateException("Package info doesn't exist"); - } - + public Setting generateSsaidLocked(PackageInfo callingPkg, int userId) { // Read the user's key from the ssaid table. Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY); if (userKeySetting == null || userKeySetting.isNull() @@ -2113,11 +2124,12 @@ public class SettingsProvider extends ContentProvider { } // Mac the package name and each of the signatures. - byte[] packageNameBytes = packageInfo.packageName.getBytes(StandardCharsets.UTF_8); + final String packageName = callingPkg.packageName; + byte[] packageNameBytes = packageName.getBytes(StandardCharsets.UTF_8); m.update(getLengthPrefix(packageNameBytes), 0, 4); m.update(packageNameBytes); - for (int i = 0; i < packageInfo.signatures.length; i++) { - byte[] sig = packageInfo.signatures[i].toByteArray(); + for (int i = 0; i < callingPkg.signatures.length; i++) { + byte[] sig = callingPkg.signatures[i].toByteArray(); m.update(getLengthPrefix(sig), 0, 4); m.update(sig); } @@ -2127,7 +2139,7 @@ public class SettingsProvider extends ContentProvider { .toLowerCase(Locale.US); // Save the ssaid in the ssaid table. - final String uid = Integer.toString(packageInfo.applicationInfo.uid); + final String uid = Integer.toString(callingPkg.applicationInfo.uid); final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId); final boolean success = ssaidSettings.insertSettingLocked(uid, ssaid, null, true, packageName); @@ -2798,7 +2810,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 143; + private static final int SETTINGS_VERSION = 144; private final int mUserId; @@ -3324,7 +3336,7 @@ public class SettingsProvider extends ContentProvider { } if (currentVersion == 141) { - // Version 141: We added the notion of a default and whether the system set + // Version 142: We added the notion of a default and whether the system set // the setting. This is used for resetting the internal state and we need // to make sure this value is updated for the existing settings, otherwise // we would delete system set settings while they should stay unmodified. @@ -3344,7 +3356,7 @@ public class SettingsProvider extends ContentProvider { } if (currentVersion == 142) { - // Version 142: Set a default value for Wi-Fi wakeup feature. + // Version 143: Set a default value for Wi-Fi wakeup feature. if (userId == UserHandle.USER_SYSTEM) { final SettingsState globalSettings = getGlobalSettingsLocked(); Setting currentSetting = globalSettings.getSettingLocked( @@ -3361,6 +3373,27 @@ public class SettingsProvider extends ContentProvider { currentVersion = 143; } + if (currentVersion == 143) { + // Version 144: Set a default value for Autofill service. + final SettingsState secureSettings = getSecureSettingsLocked(userId); + final Setting currentSetting = secureSettings + .getSettingLocked(Settings.Secure.AUTOFILL_SERVICE); + if (currentSetting.isNull()) { + final String defaultValue = getContext().getResources().getString( + com.android.internal.R.string.config_defaultAutofillService); + if (defaultValue != null) { + Slog.d(LOG_TAG, "Setting [" + defaultValue + "] as Autofill Service " + + "for user " + userId); + secureSettings.insertSettingLocked(Settings.Secure.AUTOFILL_SERVICE, + defaultValue, null, true, SettingsState.SYSTEM_PACKAGE_NAME); + } + } + + currentVersion = 144; + } + + // vXXX: Add new settings above this point. + if (currentVersion != newVersion) { Slog.wtf("SettingsProvider", "warning: upgrading settings database to version " + newVersion + " left it at " @@ -3372,8 +3405,6 @@ public class SettingsProvider extends ContentProvider { } } - // vXXX: Add new settings above this point. - // Return the current version. return currentVersion; } diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 195eb9bafdab..ff22ffb319fb 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -97,6 +97,15 @@ android:layout_height="wrap_content" android:text="@string/notification_channel_disabled" style="@style/TextAppearance.NotificationInfo.Secondary" /> + <!-- Optional link to app. Only appears if the channel is not disabled --> + <TextView + android:id="@+id/app_settings" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:ellipsize="end" + android:maxLines="1" + style="@style/TextAppearance.NotificationInfo.Secondary.Link"/> </LinearLayout> <!-- Ban Channel Switch --> <Switch diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index a8cf3daec41e..43aeaa3c9314 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1472,6 +1472,9 @@ <!-- Notification: Control panel: Label for button that launches notification settings. Used when this app has only defined a single channel for notifications. --> <string name="notification_more_settings">More settings</string> + <!-- Notification: Control panel: Label for a link that launches notification settings in the + app that sent the notification. --> + <string name="notification_app_settings">Customize: <xliff:g id="sub_category" example="Work chats">%1$s</xliff:g></string> <!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] --> <string name="notification_done">Done</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index d6abda6bd4c9..c9479b8b41f7 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -370,6 +370,10 @@ <item name="android:textColor">?android:attr/colorError</item> </style> + <style name="TextAppearance.NotificationInfo.Secondary.Link"> + <item name="android:textColor">?android:attr/colorAccent</item> + </style> + <style name="TextAppearance.NotificationInfo.Button"> <item name="android:fontFamily">sans-serif-medium</item> <item name="android:textSize">14sp</item> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 26558377c3cc..80b4da8e2acf 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -34,9 +34,11 @@ import android.widget.GridLayout; import android.widget.TextClock; import android.widget.TextView; +import com.android.internal.util.ArrayUtils; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.ChargingView; +import java.util.Arrays; import java.util.Locale; public class KeyguardStatusView extends GridLayout { @@ -53,6 +55,10 @@ public class KeyguardStatusView extends GridLayout { private ViewGroup mClockContainer; private ChargingView mBatteryDoze; + private View[] mVisibleInDoze; + private boolean mPulsing; + private boolean mDark; + private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { @Override @@ -117,6 +123,7 @@ public class KeyguardStatusView extends GridLayout { mClockView.setShowCurrentUserTime(true); mOwnerInfo = (TextView) findViewById(R.id.owner_info); mBatteryDoze = (ChargingView) findViewById(R.id.battery_doze); + mVisibleInDoze = new View[]{mBatteryDoze, mClockView}; boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive(); setEnableMarquee(shouldMarquee); @@ -273,14 +280,28 @@ public class KeyguardStatusView extends GridLayout { } public void setDark(boolean dark) { + mDark = dark; + final int N = mClockContainer.getChildCount(); for (int i = 0; i < N; i++) { View child = mClockContainer.getChildAt(i); - if (child == mClockView || child == mBatteryDoze) { + if (ArrayUtils.contains(mVisibleInDoze, child)) { continue; } child.setAlpha(dark ? 0 : 1); } + updateDozeVisibleViews(); mBatteryDoze.setDark(dark); } + + public void setPulsing(boolean pulsing) { + mPulsing = pulsing; + updateDozeVisibleViews(); + } + + private void updateDozeVisibleViews() { + for (View child : mVisibleInDoze) { + child.setAlpha(mDark && mPulsing ? 0.5f : 1); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index bcf1957109de..4e7cf723f662 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -86,7 +86,10 @@ public class PipMenuActivity extends Activity { private static final float DISABLED_ACTION_ALPHA = 0.54f; private boolean mMenuVisible; + private boolean mAllowMenuTimeout = true; + private final List<RemoteAction> mActions = new ArrayList<>(); + private View mViewRoot; private Drawable mBackgroundDrawable; private View mMenuContainer; @@ -190,7 +193,9 @@ public class PipMenuActivity extends Activity { @Override public void onUserInteraction() { - repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY); + if (mAllowMenuTimeout) { + repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY); + } } @Override @@ -255,6 +260,7 @@ public class PipMenuActivity extends Activity { } private void showMenu(Rect stackBounds, Rect movementBounds, boolean allowMenuTimeout) { + mAllowMenuTimeout = allowMenuTimeout; if (!mMenuVisible) { updateActionViews(stackBounds); if (mMenuContainerAnimator != null) { @@ -262,7 +268,6 @@ public class PipMenuActivity extends Activity { } notifyMenuVisibility(true); updateExpandButtonFromBounds(stackBounds, movementBounds); - setDecorViewVisibility(true); mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA, mMenuContainer.getAlpha(), 1f); mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN); @@ -307,11 +312,15 @@ public class PipMenuActivity extends Activity { if (animationFinishedRunnable != null) { animationFinishedRunnable.run(); } - setDecorViewVisibility(false); + + finish(); } }); mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener); mMenuContainerAnimator.start(); + } else { + // If the menu is not visible, just finish now + finish(); } } @@ -427,7 +436,6 @@ public class PipMenuActivity extends Activity { alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255); } mBackgroundDrawable.setAlpha(alpha); - setDecorViewVisibility(alpha > 0); } private void notifyRegisterInputConsumer() { @@ -504,16 +512,4 @@ public class PipMenuActivity extends Activity { v.removeCallbacks(mFinishRunnable); v.postDelayed(mFinishRunnable, delay); } - - /** - * Sets the visibility of the root view of the window to disable drawing and touches for the - * activity. This differs from {@link Activity#setVisible(boolean)} in that it does not set - * the internal mVisibleFromClient state. - */ - private void setDecorViewVisibility(boolean visible) { - final View decorView = getWindow().getDecorView(); - if (decorView != null) { - decorView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index bcaa39528837..875fb141bb7b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -50,6 +50,7 @@ import java.util.List; public class PipMenuActivityController { private static final String TAG = "PipMenuActController"; + private static final boolean DEBUG = false; public static final String EXTRA_CONTROLLER_MESSENGER = "messenger"; public static final String EXTRA_ACTIONS = "actions"; @@ -195,6 +196,10 @@ public class PipMenuActivityController { * Updates the appearance of the menu and scrim on top of the PiP while dismissing. */ public void setDismissFraction(float fraction) { + if (DEBUG) { + Log.d(TAG, "setDismissFraction() hasActivity=" + (mToActivityMessenger != null) + + " fraction=" + fraction); + } if (mToActivityMessenger != null) { mTmpDismissFractionData.clear(); mTmpDismissFractionData.putFloat(EXTRA_DISMISS_FRACTION, fraction); @@ -216,6 +221,9 @@ public class PipMenuActivityController { * Shows the menu activity. */ public void showMenu(Rect stackBounds, Rect movementBounds, boolean allowMenuTimeout) { + if (DEBUG) { + Log.d(TAG, "showMenu() hasActivity=" + (mToActivityMessenger != null)); + } if (mToActivityMessenger != null) { Bundle data = new Bundle(); data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds); @@ -238,6 +246,9 @@ public class PipMenuActivityController { * Pokes the menu, indicating that the user is interacting with it. */ public void pokeMenu() { + if (DEBUG) { + Log.d(TAG, "pokeMenu() hasActivity=" + (mToActivityMessenger != null)); + } if (mToActivityMessenger != null) { Message m = Message.obtain(); m.what = PipMenuActivity.MESSAGE_POKE_MENU; @@ -253,6 +264,9 @@ public class PipMenuActivityController { * Hides the menu activity. */ public void hideMenu() { + if (DEBUG) { + Log.d(TAG, "hideMenu() hasActivity=" + (mToActivityMessenger != null)); + } if (mToActivityMessenger != null) { Message m = Message.obtain(); m.what = PipMenuActivity.MESSAGE_HIDE_MENU; @@ -365,6 +379,10 @@ public class PipMenuActivityController { * Handles changes in menu visibility. */ private void onMenuVisibilityChanged(boolean visible, boolean resize) { + if (DEBUG) { + Log.d(TAG, "onMenuVisibilityChanged() mMenuVisible=" + mMenuVisible + + " menuVisible=" + visible + " resize=" + resize); + } if (visible) { mInputConsumerController.unregisterInputConsumer(); } else { @@ -389,6 +407,7 @@ public class PipMenuActivityController { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.println(innerPrefix + "mMenuVisible=" + mMenuVisible); + pw.println(innerPrefix + "mToActivityMessenger=" + mToActivityMessenger); pw.println(innerPrefix + "mListeners=" + mListeners.size()); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index a14a71247da3..fb8574da8e81 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -23,6 +23,7 @@ import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN; import android.animation.Animator; +import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.RectEvaluator; import android.animation.ValueAnimator; @@ -253,7 +254,7 @@ public class PipMotionHelper { * Flings the PiP to the closest snap target. */ Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds, - AnimatorUpdateListener listener) { + AnimatorUpdateListener updateListener, AnimatorListener listener) { cancelAnimations(); Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds, velocityX, velocityY); @@ -263,8 +264,11 @@ public class PipMotionHelper { mFlingAnimationUtils.apply(mBoundsAnimator, 0, distanceBetweenRectOffsets(mBounds, toBounds), velocity); - if (listener != null) { - mBoundsAnimator.addUpdateListener(listener); + if (updateListener != null) { + mBoundsAnimator.addUpdateListener(updateListener); + } + if (listener != null){ + mBoundsAnimator.addListener(listener); } mBoundsAnimator.start(); } @@ -274,14 +278,18 @@ public class PipMotionHelper { /** * Animates the PiP to the closest snap target. */ - Rect animateToClosestSnapTarget(Rect movementBounds, AnimatorUpdateListener listener) { + Rect animateToClosestSnapTarget(Rect movementBounds, AnimatorUpdateListener updateListener, + AnimatorListener listener) { cancelAnimations(); Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds); if (!mBounds.equals(toBounds)) { mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, SNAP_STACK_DURATION, FAST_OUT_SLOW_IN, mUpdateBoundsListener); - if (listener != null) { - mBoundsAnimator.addUpdateListener(listener); + if (updateListener != null) { + mBoundsAnimator.addUpdateListener(updateListener); + } + if (listener != null){ + mBoundsAnimator.addListener(listener); } mBoundsAnimator.start(); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index d68836c717e4..161bdac77f89 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -16,6 +16,8 @@ package com.android.systemui.pip.phone; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.IActivityManager; @@ -391,7 +393,10 @@ public class PipTouchHandler implements TunerService.Tunable { final float distance = bounds.bottom - target; fraction = Math.min(distance / bounds.height(), 1f); } - mMenuController.setDismissFraction(fraction); + if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) { + // Update if the fraction > 0, or if fraction == 0 and the menu was already visible + mMenuController.setDismissFraction(fraction); + } } } @@ -611,22 +616,34 @@ public class PipTouchHandler implements TunerService.Tunable { setMinimizedStateInternal(false); } - // If the menu is still visible, and we aren't minimized, then just poke the menu - // so that it will timeout after the user stops touching it + AnimatorListenerAdapter postAnimationCallback = null; if (mMenuController.isMenuVisible()) { + // If the menu is still visible, and we aren't minimized, then just poke the + // menu so that it will timeout after the user stops touching it mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */); + } else { + // If the menu is not visible, then we can still be showing the activity for the + // dismiss overlay, so just finish it after the animation completes + postAnimationCallback = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mMenuController.hideMenu(); + } + }; } if (isFling) { mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds, - mUpdateScrimListener); + mUpdateScrimListener, postAnimationCallback); } else { - mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener); + mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener, + postAnimationCallback); } } else if (mIsMinimized) { // This was a tap, so no longer minimized - mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* listener */); + mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* updateListener */, + null /* animatorListener */); setMinimizedStateInternal(false); } else if (!mIsMenuVisible) { mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds, diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java index 9efe224ff52f..d2a291944d8d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java @@ -242,7 +242,7 @@ public class QSDetail extends LinearLayout { mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE); mDetailSettingsButton.setOnClickListener(v -> { Dependency.get(MetricsLogger.class).action(ACTION_QS_MORE_SETTINGS, - mDetailAdapter.getMetricsCategory()); + adapter.getMetricsCategory()); Dependency.get(ActivityStarter.class) .postStartActivityDismissingKeyguard(settingsIntent, 0); }); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index dc666e90fbdb..311f8ffe258f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -524,7 +524,7 @@ public class TaskViewHeader extends FrameLayout * changes. */ public void onTaskDataLoaded() { - if (mTask.icon != null) { + if (mTask != null && mTask.icon != null) { mIconView.setImageDrawable(mTask.icon); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index 21a0dc9f24e9..b4b1cd3409f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -16,45 +16,34 @@ package com.android.systemui.statusbar; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; +import static android.app.NotificationManager.IMPORTANCE_NONE; + import android.app.INotificationManager; +import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; -import android.app.NotificationManager; import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.ParceledListSlice; -import android.content.res.ColorStateList; -import android.content.res.TypedArray; -import android.graphics.Canvas; +import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; -import android.os.Handler; import android.os.RemoteException; -import android.os.ServiceManager; -import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.text.TextUtils; import android.util.AttributeSet; -import android.util.Log; import android.view.View; -import android.view.ViewAnimationUtils; -import android.view.ViewGroup; -import android.view.View.OnClickListener; -import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.SeekBar; import android.widget.Switch; import android.widget.TextView; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.Utils; -import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.statusbar.NotificationGuts.OnSettingsClickListener; -import com.android.systemui.statusbar.stack.StackStateAnimator; import java.lang.IllegalArgumentException; import java.util.List; @@ -71,12 +60,16 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private int mAppUid; private List<NotificationChannel> mNotificationChannels; private NotificationChannel mSingleNotificationChannel; + private StatusBarNotification mSbn; private int mStartingUserImportance; private TextView mNumChannelsView; private View mChannelDisabledView; + private TextView mSettingsLinkView; private Switch mChannelEnabledSwitch; private CheckSaveListener mCheckSaveListener; + private OnAppSettingsClickListener mAppSettingsClickListener; + private PackageManager mPm; private NotificationGuts mGutsContainer; @@ -95,11 +88,18 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G void onClick(View v, NotificationChannel channel, int appUid); } + public interface OnAppSettingsClickListener { + void onClick(View v, Intent intent); + } + public void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager, final String pkg, final List<NotificationChannel> notificationChannels, + int startingUserImportance, + final StatusBarNotification sbn, OnSettingsClickListener onSettingsClick, + OnAppSettingsClickListener onAppSettingsClick, OnClickListener onDoneClick, CheckSaveListener checkSaveListener, final Set<String> nonBlockablePkgs) @@ -108,16 +108,21 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mPkg = pkg; mNotificationChannels = notificationChannels; mCheckSaveListener = checkSaveListener; + mSbn = sbn; + mPm = pm; + mAppSettingsClickListener = onAppSettingsClick; boolean isSingleDefaultChannel = false; + mStartingUserImportance = startingUserImportance; if (mNotificationChannels.isEmpty()) { throw new IllegalArgumentException("bindNotification requires at least one channel"); - } else if (mNotificationChannels.size() == 1) { - mSingleNotificationChannel = mNotificationChannels.get(0); - mStartingUserImportance = mSingleNotificationChannel.getImportance(); - isSingleDefaultChannel = mSingleNotificationChannel.getId() - .equals(NotificationChannel.DEFAULT_CHANNEL_ID); - } else { - mSingleNotificationChannel = null; + } else { + if (mNotificationChannels.size() == 1) { + mSingleNotificationChannel = mNotificationChannels.get(0); + isSingleDefaultChannel = mSingleNotificationChannel.getId() + .equals(NotificationChannel.DEFAULT_CHANNEL_ID); + } else { + mSingleNotificationChannel = null; + } } String appName = mPkg; @@ -228,6 +233,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G // Settings button. final TextView settingsButton = (TextView) findViewById(R.id.more_settings); if (mAppUid >= 0 && onSettingsClick != null) { + settingsButton.setVisibility(View.VISIBLE); final int appUidF = mAppUid; settingsButton.setOnClickListener( (View view) -> { @@ -247,6 +253,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G final TextView doneButton = (TextView) findViewById(R.id.done); doneButton.setText(R.string.notification_done); doneButton.setOnClickListener(onDoneClick); + + // Optional settings link + updateAppSettingsLink(); } private boolean hasImportanceChanged() { @@ -275,7 +284,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private int getSelectedImportance() { if (!mChannelEnabledSwitch.isChecked()) { - return NotificationManager.IMPORTANCE_NONE; + return IMPORTANCE_NONE; } else { return mStartingUserImportance; } @@ -285,7 +294,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G // Enabled Switch mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch); mChannelEnabledSwitch.setChecked( - mStartingUserImportance != NotificationManager.IMPORTANCE_NONE); + mStartingUserImportance != IMPORTANCE_NONE); final boolean visible = !nonBlockable && mSingleNotificationChannel != null; mChannelEnabledSwitch.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); @@ -295,12 +304,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mGutsContainer.resetFalsingCheck(); } updateSecondaryText(); + updateAppSettingsLink(); }); } private void updateSecondaryText() { final boolean disabled = mSingleNotificationChannel != null && - getSelectedImportance() == NotificationManager.IMPORTANCE_NONE; + getSelectedImportance() == IMPORTANCE_NONE; if (disabled) { mChannelDisabledView.setVisibility(View.VISIBLE); mNumChannelsView.setVisibility(View.GONE); @@ -310,6 +320,45 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } + private void updateAppSettingsLink() { + mSettingsLinkView = findViewById(R.id.app_settings); + Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel, + mSbn.getId(), mSbn.getTag()); + if (settingsIntent != null && getSelectedImportance() != IMPORTANCE_NONE + && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { + mSettingsLinkView.setVisibility(View.VISIBLE); + mSettingsLinkView.setText(mContext.getString(R.string.notification_app_settings, + mSbn.getNotification().getSettingsText())); + mSettingsLinkView.setOnClickListener((View view) -> { + mAppSettingsClickListener.onClick(view, settingsIntent); + }); + } else { + mSettingsLinkView.setVisibility(View.GONE); + } + } + + private Intent getAppSettingsIntent(PackageManager pm, String packageName, + NotificationChannel channel, int id, String tag) { + Intent intent = new Intent(Intent.ACTION_MAIN) + .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES) + .setPackage(packageName); + final List<ResolveInfo> resolveInfos = pm.queryIntentActivities( + intent, + PackageManager.MATCH_DEFAULT_ONLY + ); + if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) { + return null; + } + final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo; + intent.setClassName(activityInfo.packageName, activityInfo.name); + if (channel != null) { + intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId()); + } + intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id); + intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag); + return intent; + } + @Override public void setGutsParent(NotificationGuts guts) { mGutsContainer = guts; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java index 5055dda0d863..bb82b602afbb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java @@ -120,6 +120,9 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl @Override public void createMenu(ViewGroup parent) { mParent = (ExpandableNotificationRow) parent; + if (mMenuContainer != null) { + mMenuContainer.removeAllViews(); + } mMenuContainer = new FrameLayout(mContext); for (int i = 0; i < mMenuItems.size(); i++) { addMenuView(mMenuItems.get(i), mMenuContainer); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 8da17fa76bd7..715dc82d1a1f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -23,7 +23,6 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; -import com.android.internal.widget.CachingIconView; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.ViewInvertHelper; @@ -127,12 +126,8 @@ public class NotificationShelf extends ActivatableNotificationView implements super.setDark(dark, fade, delay); if (mDark == dark) return; mDark = dark; - if (fade) { - mViewInvertHelper.fade(dark, delay); - } else { - mViewInvertHelper.update(dark); - } - mShelfIcons.setAmbient(dark); + mShelfIcons.setDark(dark, fade, delay); + updateInteractiveness(); } @Override @@ -167,7 +162,7 @@ public class NotificationShelf extends ActivatableNotificationView implements openedAmount = Math.min(1.0f, openedAmount); mShelfState.openedAmount = openedAmount; mShelfState.clipTopAmount = 0; - mShelfState.alpha = 1.0f; + mShelfState.alpha = mAmbientState.isPulsing() ? 0 : 1; mShelfState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0; mShelfState.shadowAlpha = 1.0f; mShelfState.hideSensitive = false; @@ -439,7 +434,8 @@ public class NotificationShelf extends ActivatableNotificationView implements iconState.scaleY = 1.0f; iconState.hidden = false; } - if (row.isAboveShelf() || (!row.isInShelf() && isLastChild && row.areGutsExposed())) { + if (row.isAboveShelf() || (!row.isInShelf() && (isLastChild && row.areGutsExposed() + || row.getTranslationZ() > mAmbientState.getBaseZHeight()))) { iconState.hidden = true; } int shelfColor = icon.getStaticDrawableColor(); @@ -576,7 +572,8 @@ public class NotificationShelf extends ActivatableNotificationView implements } private void updateInteractiveness() { - mInteractive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf; + mInteractive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf + && !mDark; setClickable(mInteractive); setFocusable(mInteractive); setImportantForAccessibility(mInteractive ? View.IMPORTANT_FOR_ACCESSIBILITY_YES @@ -602,6 +599,11 @@ public class NotificationShelf extends ActivatableNotificationView implements } @Override + public boolean hasOverlappingRendering() { + return false; // Shelf only uses alpha for transitions where the difference can't be seen. + } + + @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { updateRelativeOffset(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index b9c8a784e779..92bfae9e3f5c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -27,6 +27,7 @@ import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -46,6 +47,7 @@ import android.view.animation.Interpolator; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.notification.NotificationIconDozeHelper; import com.android.systemui.statusbar.notification.NotificationUtils; import java.text.NumberFormat; @@ -99,7 +101,6 @@ public class StatusBarIconView extends AnimatedImageView { private int mDensity; private float mIconScale = 1.0f; private final Paint mDotPaint = new Paint(); - private boolean mDotVisible; private float mDotRadius; private int mStaticDotRadius; private int mVisibleState = STATE_ICON; @@ -110,6 +111,8 @@ public class StatusBarIconView extends AnimatedImageView { private OnVisibilityChangedListener mOnVisibilityChangedListener; private int mDrawableColor; private int mIconColor; + private int mDecorColor; + private float mDarkAmount; private ValueAnimator mColorAnimator; private int mCurrentSetColor = NO_COLOR; private int mAnimationStartColor = NO_COLOR; @@ -119,6 +122,7 @@ public class StatusBarIconView extends AnimatedImageView { animation.getAnimatedFraction()); setColorInternal(newColor); }; + private final NotificationIconDozeHelper mDozer; public StatusBarIconView(Context context, String slot, Notification notification) { this(context, slot, notification, false); @@ -127,6 +131,7 @@ public class StatusBarIconView extends AnimatedImageView { public StatusBarIconView(Context context, String slot, Notification notification, boolean blocked) { super(context); + mDozer = new NotificationIconDozeHelper(context); mBlocked = blocked; mSlot = slot; mNumberPain = new Paint(); @@ -190,6 +195,7 @@ public class StatusBarIconView extends AnimatedImageView { public StatusBarIconView(Context context, AttributeSet attrs) { super(context, attrs); + mDozer = new NotificationIconDozeHelper(context); mBlocked = false; mAlwaysScaleIcon = true; updateIconScale(); @@ -466,7 +472,19 @@ public class StatusBarIconView extends AnimatedImageView { * to the drawable. */ public void setDecorColor(int iconTint) { - mDotPaint.setColor(iconTint); + mDecorColor = iconTint; + updateDecorColor(); + } + + private void updateDecorColor() { + int color = NotificationUtils.interpolateColors(mDecorColor, Color.WHITE, mDarkAmount); + if (mDotPaint.getColor() != color) { + mDotPaint.setColor(color); + + if (mDotAppearAmount != 0) { + invalidate(); + } + } } /** @@ -477,6 +495,7 @@ public class StatusBarIconView extends AnimatedImageView { mDrawableColor = color; setColorInternal(color); mIconColor = color; + mDozer.setColor(color); } private void setColorInternal(int color) { @@ -649,6 +668,14 @@ public class StatusBarIconView extends AnimatedImageView { mOnVisibilityChangedListener = listener; } + public void setDark(boolean dark, boolean fade, long delay) { + mDozer.setImageDark(this, dark, fade, delay, mIconColor == NO_COLOR); + mDozer.setIntensityDark(f -> { + mDarkAmount = f; + updateDecorColor(); + }, dark, fade, delay); + } + public interface OnVisibilityChangedListener { void onVisibilityChanged(int newVisibility); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java index 3efa29f87450..bca4b43afc0c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; +import android.content.Context; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.view.View; @@ -38,8 +38,8 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { private boolean mIsLegacy; private int mLegacyColor; - protected NotificationCustomViewWrapper(View view, ExpandableNotificationRow row) { - super(view, row); + protected NotificationCustomViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { + super(ctx, view, row); mInvertHelper = new ViewInvertHelper(view, NotificationPanelView.DOZE_ANIMATION_DURATION); mLegacyColor = row.getContext().getColor(R.color.notification_legacy_background_color); } @@ -67,13 +67,11 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { } protected void fadeGrayscale(final boolean dark, long delay) { - startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - updateGrayscaleMatrix((float) animation.getAnimatedValue()); - mGreyPaint.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); - mView.setLayerPaint(mGreyPaint); - } + getDozer().startIntensityAnimation(animation -> { + getDozer().updateGrayscaleMatrix((float) animation.getAnimatedValue()); + mGreyPaint.setColorFilter( + new ColorMatrixColorFilter(getDozer().getGrayscaleColorMatrix())); + mView.setLayerPaint(mGreyPaint); }, dark, delay, new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -86,9 +84,9 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { protected void updateGrayscale(boolean dark) { if (dark) { - updateGrayscaleMatrix(1f); + getDozer().updateGrayscaleMatrix(1f); mGreyPaint.setColorFilter( - new ColorMatrixColorFilter(mGrayscaleColorMatrix)); + new ColorMatrixColorFilter(getDozer().getGrayscaleColorMatrix())); mView.setLayerPaint(mGreyPaint); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java new file mode 100644 index 000000000000..d592c5f5b7f3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.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.systemui.statusbar.notification; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.widget.ImageView; + +import com.android.systemui.Interpolators; +import com.android.systemui.statusbar.phone.NotificationPanelView; + +import java.util.function.Consumer; + +public class NotificationDozeHelper { + private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix(); + + public void fadeGrayscale(final ImageView target, final boolean dark, long delay) { + startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateGrayscaleMatrix((float) animation.getAnimatedValue()); + target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); + } + }, dark, delay, new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!dark) { + target.setColorFilter(null); + } + } + }); + } + + public void updateGrayscale(ImageView target, boolean dark) { + if (dark) { + updateGrayscaleMatrix(1f); + target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); + } else { + target.setColorFilter(null); + } + } + + public void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener, + boolean dark, long delay, Animator.AnimatorListener listener) { + float startIntensity = dark ? 0f : 1f; + float endIntensity = dark ? 1f : 0f; + ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity); + animator.addUpdateListener(updateListener); + animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION); + animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + animator.setStartDelay(delay); + if (listener != null) { + animator.addListener(listener); + } + animator.start(); + } + + public void setIntensityDark(Consumer<Float> listener, boolean dark, + boolean animate, long delay) { + if (animate) { + startIntensityAnimation(a -> listener.accept((Float) a.getAnimatedValue()), dark, delay, + null /* listener */); + } else { + listener.accept(dark ? 1f : 0f); + } + } + + public void updateGrayscaleMatrix(float intensity) { + mGrayscaleColorMatrix.setSaturation(1 - intensity); + } + + public ColorMatrix getGrayscaleColorMatrix() { + return mGrayscaleColorMatrix; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java index 38e4ec1a4d7f..1ffc94480dab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java @@ -16,17 +16,10 @@ package com.android.systemui.statusbar.notification; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; import android.app.Notification; import android.content.Context; -import android.graphics.Color; import android.graphics.ColorFilter; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; -import android.graphics.drawable.Drawable; import android.util.ArraySet; import android.view.NotificationHeaderView; import android.view.View; @@ -37,7 +30,6 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.systemui.Interpolators; -import com.android.systemui.R; import com.android.systemui.ViewInvertHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.TransformableView; @@ -55,10 +47,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private static final Interpolator LOW_PRIORITY_HEADER_CLOSE = new PathInterpolator(0.4f, 0f, 0.7f, 1f); - private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter( - 0, PorterDuff.Mode.SRC_ATOP); - private final int mIconDarkAlpha; - private final int mIconDarkColor = 0xffffffff; protected final ViewInvertHelper mInvertHelper; protected final ViewTransformationHelper mTransformationHelper; @@ -74,8 +62,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private boolean mTransformLowPriorityTitle; protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { - super(view, row); - mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha); + super(ctx, view, row); mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION); mTransformationHelper = new ViewTransformationHelper(); @@ -108,6 +95,16 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { updateInvertHelper(); } + @Override + protected NotificationDozeHelper createDozer(Context ctx) { + return new NotificationIconDozeHelper(ctx); + } + + @Override + protected NotificationIconDozeHelper getDozer() { + return (NotificationIconDozeHelper) super.getDozer(); + } + protected void resolveHeaderViews() { mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon); mHeaderText = (TextView) mView.findViewById(com.android.internal.R.id.header_text); @@ -116,6 +113,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mColor = resolveColor(mExpandButton); mNotificationHeader = (NotificationHeaderView) mView.findViewById( com.android.internal.R.id.notification_header); + getDozer().setColor(mColor); } private int resolveColor(ImageView icon) { @@ -223,90 +221,8 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { // It also may lead to bugs where the icon isn't correctly greyed out. boolean hadColorFilter = mNotificationHeader.getOriginalIconColor() != NotificationHeaderView.NO_COLOR; - if (fade) { - if (hadColorFilter) { - fadeIconColorFilter(mIcon, dark, delay); - fadeIconAlpha(mIcon, dark, delay); - } else { - fadeGrayscale(mIcon, dark, delay); - } - } else { - if (hadColorFilter) { - updateIconColorFilter(mIcon, dark); - updateIconAlpha(mIcon, dark); - } else { - updateGrayscale(mIcon, dark); - } - } - } - } - - private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) { - startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - updateIconColorFilter(target, (Float) animation.getAnimatedValue()); - } - }, dark, delay, null /* listener */); - } - - private void fadeIconAlpha(final ImageView target, boolean dark, long delay) { - startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float t = (float) animation.getAnimatedValue(); - target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t)); - } - }, dark, delay, null /* listener */); - } - - protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) { - startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - updateGrayscaleMatrix((float) animation.getAnimatedValue()); - target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); - } - }, dark, delay, new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (!dark) { - target.setColorFilter(null); - } - } - }); - } - - private void updateIconColorFilter(ImageView target, boolean dark) { - updateIconColorFilter(target, dark ? 1f : 0f); - } - private void updateIconColorFilter(ImageView target, float intensity) { - int color = interpolateColor(mColor, mIconDarkColor, intensity); - mIconColorFilter.setColor(color); - Drawable iconDrawable = target.getDrawable(); - - // Also, the notification might have been modified during the animation, so background - // might be null here. - if (iconDrawable != null) { - Drawable d = iconDrawable.mutate(); - // DrawableContainer ignores the color filter if it's already set, so clear it first to - // get it set and invalidated properly. - d.setColorFilter(null); - d.setColorFilter(mIconColorFilter); - } - } - - private void updateIconAlpha(ImageView target, boolean dark) { - target.setImageAlpha(dark ? mIconDarkAlpha : 255); - } - - protected void updateGrayscale(ImageView target, boolean dark) { - if (dark) { - updateGrayscaleMatrix(1f); - target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); - } else { - target.setColorFilter(null); + getDozer().setImageDark(mIcon, dark, fade, delay, !hadColorFilter); } } @@ -316,22 +232,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mNotificationHeader.setOnClickListener(expandable ? onClickListener : null); } - private static int interpolateColor(int source, int target, float t) { - int aSource = Color.alpha(source); - int rSource = Color.red(source); - int gSource = Color.green(source); - int bSource = Color.blue(source); - int aTarget = Color.alpha(target); - int rTarget = Color.red(target); - int gTarget = Color.green(target); - int bTarget = Color.blue(target); - return Color.argb( - (int) (aSource * (1f - t) + aTarget * t), - (int) (rSource * (1f - t) + rTarget * t), - (int) (gSource * (1f - t) + gTarget * t), - (int) (bSource * (1f - t) + bTarget * t)); - } - @Override public NotificationHeaderView getNotificationHeader() { return mNotificationHeader; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java new file mode 100644 index 000000000000..9f79ef2491d9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java @@ -0,0 +1,101 @@ +/* + * 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.systemui.statusbar.notification; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.widget.ImageView; + +import com.android.systemui.R; + +public class NotificationIconDozeHelper extends NotificationDozeHelper { + + private final int mImageDarkAlpha; + private final int mImageDarkColor = 0xffffffff; + private final PorterDuffColorFilter mImageColorFilter = new PorterDuffColorFilter( + 0, PorterDuff.Mode.SRC_ATOP); + + private int mColor = Color.BLACK; + + public NotificationIconDozeHelper(Context ctx) { + mImageDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha); + } + + public void setColor(int color) { + mColor = color; + } + + public void setImageDark(ImageView target, boolean dark, boolean fade, long delay, + boolean useGrayscale) { + if (fade) { + if (!useGrayscale) { + fadeImageColorFilter(target, dark, delay); + fadeImageAlpha(target, dark, delay); + } else { + fadeGrayscale(target, dark, delay); + } + } else { + if (!useGrayscale) { + updateImageColorFilter(target, dark); + updateImageAlpha(target, dark); + } else { + updateGrayscale(target, dark); + } + } + } + + private void fadeImageColorFilter(final ImageView target, boolean dark, long delay) { + startIntensityAnimation(animation -> { + updateImageColorFilter(target, (Float) animation.getAnimatedValue()); + }, dark, delay, null /* listener */); + } + + private void fadeImageAlpha(final ImageView target, boolean dark, long delay) { + startIntensityAnimation(animation -> { + float t = (float) animation.getAnimatedValue(); + target.setImageAlpha((int) (255 * (1f - t) + mImageDarkAlpha * t)); + }, dark, delay, null /* listener */); + } + + private void updateImageColorFilter(ImageView target, boolean dark) { + updateImageColorFilter(target, dark ? 1f : 0f); + } + + private void updateImageColorFilter(ImageView target, float intensity) { + int color = NotificationUtils.interpolateColors(mColor, mImageDarkColor, intensity); + mImageColorFilter.setColor(color); + Drawable imageDrawable = target.getDrawable(); + + // Also, the notification might have been modified during the animation, so background + // might be null here. + if (imageDrawable != null) { + Drawable d = imageDrawable.mutate(); + // DrawableContainer ignores the color filter if it's already set, so clear it first to + // get it set and invalidated properly. + d.setColorFilter(null); + d.setColorFilter(mImageColorFilter); + } + } + + private void updateImageAlpha(ImageView target, boolean dark) { + target.setImageAlpha(dark ? mImageDarkAlpha : 255); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java index 846d03ac74dd..f0b6b2e89e9f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification; -import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Color; import android.service.notification.StatusBarNotification; @@ -46,7 +45,8 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp private int mContentHeight; private int mMinHeightHint; - protected NotificationTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { + protected NotificationTemplateViewWrapper(Context ctx, View view, + ExpandableNotificationRow row) { super(ctx, view, row); mTransformationHelper.setCustomTransformation( new ViewTransformationHelper.CustomTransformation() { @@ -154,16 +154,20 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp // This also clears the existing types super.updateTransformedTypes(); if (mTitle != null) { - mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mTitle); + mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, + mTitle); } if (mText != null) { - mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, mText); + mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, + mText); } if (mPicture != null) { - mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE, mPicture); + mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE, + mPicture); } if (mProgressBar != null) { - mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS, mProgressBar); + mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS, + mProgressBar); } } @@ -173,7 +177,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp return; } super.setDark(dark, fade, delay); - setPictureGrayscale(dark, fade, delay); + setPictureDark(dark, fade, delay); setProgressBarDark(dark, fade, delay); } @@ -188,12 +192,9 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp } private void fadeProgressDark(final ProgressBar target, final boolean dark, long delay) { - startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float t = (float) animation.getAnimatedValue(); - updateProgressDark(target, t); - } + getDozer().startIntensityAnimation(animation -> { + float t = (float) animation.getAnimatedValue(); + updateProgressDark(target, t); }, dark, delay, null /* listener */); } @@ -207,13 +208,9 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp updateProgressDark(target, dark ? 1f : 0f); } - protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) { + private void setPictureDark(boolean dark, boolean fade, long delay) { if (mPicture != null) { - if (fade) { - fadeGrayscale(mPicture, grayscale, delay); - } else { - updateGrayscale(mPicture, grayscale); - } + getDozer().setImageDark(mPicture, dark, fade, delay, true /* useGrayscale */); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java index c85e8d853b0d..f4db9a1977f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java @@ -16,24 +16,17 @@ package com.android.systemui.statusbar.notification; -import android.animation.Animator; -import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Color; -import android.graphics.ColorMatrix; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.service.notification.StatusBarNotification; import android.support.v4.graphics.ColorUtils; import android.view.NotificationHeaderView; import android.view.View; -import com.android.systemui.Interpolators; -import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.TransformableView; -import com.android.systemui.statusbar.phone.NotificationPanelView; /** * Wraps the actual notification content view; used to implement behaviors which are different for @@ -41,14 +34,14 @@ import com.android.systemui.statusbar.phone.NotificationPanelView; */ public abstract class NotificationViewWrapper implements TransformableView { - protected final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix(); protected final View mView; protected final ExpandableNotificationRow mRow; + private final NotificationDozeHelper mDozer; + protected boolean mDark; private int mBackgroundColor = 0; protected boolean mShouldInvertDark; protected boolean mDarkInitialized = false; - private boolean mForcedInvisible; public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) { if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) { @@ -65,13 +58,22 @@ public abstract class NotificationViewWrapper implements TransformableView { } else if (v instanceof NotificationHeaderView) { return new NotificationHeaderViewWrapper(ctx, v, row); } else { - return new NotificationCustomViewWrapper(v, row); + return new NotificationCustomViewWrapper(ctx, v, row); } } - protected NotificationViewWrapper(View view, ExpandableNotificationRow row) { + protected NotificationViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { mView = view; mRow = row; + mDozer = createDozer(ctx); + } + + protected NotificationDozeHelper createDozer(Context ctx) { + return new NotificationDozeHelper(); + } + + protected NotificationDozeHelper getDozer() { + return mDozer; } /** @@ -112,26 +114,6 @@ public abstract class NotificationViewWrapper implements TransformableView { || ColorUtils.calculateLuminance(backgroundColor) > 0.5; } - - protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener, - boolean dark, long delay, Animator.AnimatorListener listener) { - float startIntensity = dark ? 0f : 1f; - float endIntensity = dark ? 1f : 0f; - ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity); - animator.addUpdateListener(updateListener); - animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION); - animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - animator.setStartDelay(delay); - if (listener != null) { - animator.addListener(listener); - } - animator.start(); - } - - protected void updateGrayscaleMatrix(float intensity) { - mGrayscaleColorMatrix.setSaturation(1 - intensity); - } - /** * Update the appearance of the expand button. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 3706dc8242b9..dee15d8163a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -95,7 +95,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private int mActualLayoutWidth = NO_VALUE; private float mActualPaddingEnd = NO_VALUE; private float mActualPaddingStart = NO_VALUE; - private boolean mCentered; + private boolean mDark; private boolean mChangingViewPositions; private int mAddAnimationStartIndex = -1; private int mCannedAnimationStartIndex = -1; @@ -183,6 +183,9 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex); } } + if (mDark && child instanceof StatusBarIconView) { + ((StatusBarIconView) child).setDark(mDark, false, 0); + } } @Override @@ -312,7 +315,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { numDots++; } } - if (mCentered && translationX < getLayoutEnd()) { + boolean center = mDark; + if (center && translationX < getLayoutEnd()) { float delta = (getLayoutEnd() - translationX) / 2; for (int i = 0; i < childCount; i++) { View view = getChildAt(i); @@ -390,9 +394,15 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mChangingViewPositions = changingViewPositions; } - public void setAmbient(boolean ambient) { - mCentered = ambient; + public void setDark(boolean dark, boolean fade, long delay) { + mDark = dark; mDisallowNextAnimation = true; + for (int i = 0; i < getChildCount(); i++) { + View view = getChildAt(i); + if (view instanceof StatusBarIconView) { + ((StatusBarIconView) view).setDark(dark, fade, delay); + } + } } public IconState getIconState(StatusBarIconView icon) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index c24a2a09f3cb..307a8c7b16dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -2472,4 +2472,8 @@ public class NotificationPanelView extends PanelView implements public void setNoVisibleNotifications(boolean noNotifications) { mNoVisibleNotifications = noNotifications; } + + public void setPulsing(boolean pulsing) { + mKeyguardStatusView.setPulsing(pulsing); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 9a71ed7d2e6b..9304de52a6be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -48,8 +48,10 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; +import android.content.pm.ActivityInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; @@ -89,7 +91,6 @@ import android.os.Vibrator; import android.provider.Settings; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; -import android.support.annotation.VisibleForTesting; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; @@ -242,7 +243,6 @@ import com.android.systemui.RecentsComponent; import com.android.systemui.SwipeHelper; import com.android.systemui.SystemUI; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; -import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.recents.Recents; import com.android.systemui.statusbar.policy.RemoteInputView; import com.android.systemui.statusbar.stack.StackStateAnimator; @@ -5012,19 +5012,25 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onPulseStarted() { callback.onPulseStarted(); - if (!mHeadsUpManager.getAllEntries().isEmpty()) { + Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries = + mHeadsUpManager.getAllEntries(); + if (!pulsingEntries.isEmpty()) { // Only pulse the stack scroller if there's actually something to show. // Otherwise just show the always-on screen. - mStackScroller.setPulsing(true); - mVisualStabilityManager.setPulsing(true); + setPulsing(pulsingEntries); } } @Override public void onPulseFinished() { callback.onPulseFinished(); - mStackScroller.setPulsing(false); - mVisualStabilityManager.setPulsing(false); + setPulsing(null); + } + + private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) { + mStackScroller.setPulsing(pulsing); + mNotificationPanel.setPulsing(pulsing != null); + mVisualStabilityManager.setPulsing(pulsing != null); } }, reason); } @@ -5697,7 +5703,7 @@ public class StatusBar extends SystemUI implements DemoMode, .findViewById(com.android.internal.R.id.media_actions) != null; } - // The (i) button in the guts that links to the system notification settings for that app + // The button in the guts that links to the system notification settings for that app private void startAppNotificationSettingsActivity(String packageName, final int appUid, final NotificationChannel channel) { final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS); @@ -5767,16 +5773,27 @@ public class StatusBar extends SystemUI implements DemoMode, ServiceManager.getService(Context.NOTIFICATION_SERVICE)); final String pkg = sbn.getPackageName(); NotificationInfo info = (NotificationInfo) gutsView; - final NotificationInfo.OnSettingsClickListener onSettingsClick = (View v, - NotificationChannel channel, int appUid) -> { - mMetricsLogger.action(MetricsEvent.ACTION_NOTE_INFO); + // Settings link is only valid for notifications that specify a user, unless this is the + // system user. + NotificationInfo.OnSettingsClickListener onSettingsClick = null; + if (!userHandle.equals(UserHandle.ALL) || mCurrentUserId == UserHandle.USER_SYSTEM) { + onSettingsClick = (View v, NotificationChannel channel, int appUid) -> { + mMetricsLogger.action(MetricsEvent.ACTION_NOTE_INFO); + guts.resetFalsingCheck(); + startAppNotificationSettingsActivity(pkg, appUid, channel); + }; + } + final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v, + Intent intent) -> { + mMetricsLogger.action(MetricsEvent.ACTION_APP_NOTE_SETTINGS); guts.resetFalsingCheck(); - startAppNotificationSettingsActivity(pkg, appUid, channel); + startNotificationGutsIntent(intent, sbn.getUid()); }; final View.OnClickListener onDoneClick = (View v) -> { saveAndCloseNotificationMenu(info, row, guts, v); }; - final NotificationInfo.CheckSaveListener checkSaveListener = (Runnable saveImportance) -> { + final NotificationInfo.CheckSaveListener checkSaveListener = + (Runnable saveImportance) -> { // If the user has security enabled, show challenge if the setting is changed. if (isLockscreenPublicMode(userHandle.getIdentifier()) && (mState == StatusBarState.KEYGUARD @@ -5809,7 +5826,9 @@ public class StatusBar extends SystemUI implements DemoMode, } try { info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels), - onSettingsClick, onDoneClick, checkSaveListener, mNonBlockablePkgs); + row.getEntry().channel.getImportance(), sbn, onSettingsClick, + onAppSettingsClick, onDoneClick, checkSaveListener, + mNonBlockablePkgs); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index fe83dc47649e..b2b23a55cd20 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -59,6 +59,7 @@ public class AmbientState { private boolean mPanelTracking; private boolean mExpansionChanging; private boolean mPanelFullWidth; + private boolean mPulsing; public AmbientState(Context context) { reload(context); @@ -285,6 +286,14 @@ public class AmbientState { mPanelTracking = panelTracking; } + public boolean isPulsing() { + return mPulsing; + } + + public void setPulsing(boolean pulsing) { + mPulsing = pulsing; + } + public boolean isPanelTracking() { return mPanelTracking; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 7d2d0df3f0d4..15fcb38ccf6c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -72,6 +72,7 @@ import com.android.systemui.statusbar.DismissView; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; +import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationGuts; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StackScrollerDecorView; @@ -86,6 +87,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ScrollAdapter; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -331,7 +333,7 @@ public class NotificationStackScrollLayout extends ViewGroup } }; private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); - private boolean mPulsing; + private Collection<HeadsUpManager.HeadsUpEntry> mPulsing; private boolean mDrawBackgroundAsSrc; private boolean mFadingOut; private boolean mParentNotFullyVisible; @@ -1917,15 +1919,19 @@ public class NotificationStackScrollLayout extends ViewGroup int numShownItems = 0; boolean finish = false; int maxDisplayedNotifications = mAmbientState.isDark() - ? (mPulsing ? 1 : 0) + ? (isPulsing() ? 1 : 0) : mMaxDisplayedNotifications; for (int i = 0; i < getChildCount(); i++) { ExpandableView expandableView = (ExpandableView) getChildAt(i); if (expandableView.getVisibility() != View.GONE && !expandableView.hasNoContentHeight()) { - if (maxDisplayedNotifications != -1 - && numShownItems >= maxDisplayedNotifications) { + boolean limitReached = maxDisplayedNotifications != -1 + && numShownItems >= maxDisplayedNotifications; + boolean notificationOnAmbientThatIsNotPulsing = isPulsing() + && expandableView instanceof ExpandableNotificationRow + && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry()); + if (limitReached || notificationOnAmbientThatIsNotPulsing) { expandableView = mShelf; finish = true; } @@ -1971,6 +1977,19 @@ public class NotificationStackScrollLayout extends ViewGroup mAmbientState.setLayoutMaxHeight(mContentHeight); } + private boolean isPulsing(NotificationData.Entry entry) { + for (HeadsUpManager.HeadsUpEntry e : mPulsing) { + if (e.entry == entry) { + return true; + } + } + return false; + } + + private boolean isPulsing() { + return mPulsing != null; + } + private void updateScrollability() { boolean scrollable = getScrollRange() > 0; if (scrollable != mScrollable) { @@ -2784,7 +2803,7 @@ public class NotificationStackScrollLayout extends ViewGroup } private void updateNotificationAnimationStates() { - boolean running = mAnimationsEnabled || mPulsing; + boolean running = mAnimationsEnabled || isPulsing(); mShelf.setAnimationsEnabled(running); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { @@ -2795,7 +2814,7 @@ public class NotificationStackScrollLayout extends ViewGroup } private void updateAnimationState(View child) { - updateAnimationState((mAnimationsEnabled || mPulsing) + updateAnimationState((mAnimationsEnabled || isPulsing()) && (mIsExpanded || isPinnedHeadsUp(child)), child); } @@ -4055,14 +4074,16 @@ public class NotificationStackScrollLayout extends ViewGroup return mIsExpanded; } - public void setPulsing(boolean pulsing) { - if (mPulsing == pulsing) { + public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) { + if (mPulsing == null && pulsing == null) { return; } mPulsing = pulsing; + mAmbientState.setPulsing(isPulsing()); updateNotificationAnimationStates(); updateContentHeight(); notifyHeightChangeListener(mShelf); + requestChildrenUpdate(); } public void setFadingOut(boolean fadingOut) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java index c8659fb62051..5b594be507d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java @@ -190,7 +190,9 @@ public class ViewState { view.setScaleY(scaleY); } - boolean becomesInvisible = this.alpha == 0.0f || (this.hidden && !isAnimating(view)); + int oldVisibility = view.getVisibility(); + boolean becomesInvisible = this.alpha == 0.0f + || (this.hidden && (!isAnimating(view) || oldVisibility != View.VISIBLE)); boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA); if (animatingAlpha) { updateAlphaAnimation(view); @@ -212,7 +214,6 @@ public class ViewState { } // apply visibility - int oldVisibility = view.getVisibility(); int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE; if (newVisibility != oldVisibility) { if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java index c67cccc06169..8609eeb7e6b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java @@ -100,4 +100,10 @@ public class QSDetailTest extends SysuiTestCase { ViewUtils.detachView(mQsDetail); mTestableLooper.processAllMessages(); } + + @Test + public void testNullAdapterClick() { + mQsDetail.setupDetailFooter(mock(DetailAdapter.class)); + mQsDetail.findViewById(android.R.id.button2).performClick(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java index 21930a3d8e8d..08c580bc0be9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -18,9 +18,9 @@ package com.android.systemui.statusbar; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; @@ -33,13 +33,18 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.INotificationManager; +import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; +import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; +import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -56,6 +61,8 @@ import com.android.systemui.SysuiTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; + +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -74,6 +81,7 @@ public class NotificationInfoTest extends SysuiTestCase { private final PackageManager mMockPackageManager = mock(PackageManager.class); private NotificationChannel mNotificationChannel; private NotificationChannel mDefaultNotificationChannel; + private StatusBarNotification mSbn; @Before public void setUp() throws Exception { @@ -101,6 +109,8 @@ public class NotificationInfoTest extends SysuiTestCase { mDefaultNotificationChannel = new NotificationChannel( NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0, + new Notification(), UserHandle.CURRENT, null, 0); } private CharSequence getStringById(int resId) { @@ -135,7 +145,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); } @@ -146,7 +158,9 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); final ImageView iconView = (ImageView) mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -154,7 +168,9 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.GONE, groupNameView.getVisibility()); final TextView groupDividerView = @@ -171,7 +187,9 @@ public class NotificationInfoTest extends SysuiTestCase { eq("test_group_id"), eq(TEST_PACKAGE_NAME), anyInt())) .thenReturn(notificationChannelGroup); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); @@ -183,7 +201,9 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsTextChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @@ -193,10 +213,11 @@ public class NotificationInfoTest extends SysuiTestCase { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, (View v, NotificationChannel c, int appUid) -> { - assertEquals(mNotificationChannel, c); - latch.countDown(); - }, null, null, null); + assertEquals(mNotificationChannel, c); + latch.countDown(); + }, null, null, null, null); final TextView settingsButton = (TextView) mNotificationInfo.findViewById(R.id.more_settings); @@ -206,15 +227,40 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception { + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); + final TextView settingsButton = + (TextView) mNotificationInfo.findViewById(R.id.more_settings); + assertTrue(settingsButton.getVisibility() != View.VISIBLE); + } + + @Test + public void testBindNotification_SettingsButtonReappersAfterSecondBind() throws Exception { + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + null, null, null, null); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, + (View v, NotificationChannel c, int appUid) -> {}, null, null, null, null); + final TextView settingsButton = + (TextView) mNotificationInfo.findViewById(R.id.more_settings); + assertEquals(View.VISIBLE, settingsButton.getVisibility()); + } + + @Test public void testOnClickListenerPassesNullChannelForBundle() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), + mNotificationChannel.getImportance(), mSbn, (View v, NotificationChannel c, int appUid) -> { - assertEquals(null, c); - latch.countDown(); - }, null, null, null); + assertEquals(null, c); + latch.countDown(); + }, null, null, null, null); final TextView settingsButton = (TextView) mNotificationInfo.findViewById(R.id.more_settings); @@ -227,7 +273,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SettingsTextWithOneChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - (View v, NotificationChannel c, int appUid) -> {}, null, null, null); + mNotificationChannel.getImportance(), mSbn, + (View v, NotificationChannel c, int appUid) -> { + }, null, null, null, null); final TextView settingsButton = (TextView) mNotificationInfo.findViewById(R.id.more_settings); assertEquals(getStringById(R.string.notification_more_settings), settingsButton.getText()); @@ -239,7 +287,9 @@ public class NotificationInfoTest extends SysuiTestCase { eq(TEST_PACKAGE_NAME), anyInt(), anyBoolean())).thenReturn(2); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - (View v, NotificationChannel c, int appUid) -> {}, null, null, null); + mNotificationChannel.getImportance(), mSbn, + (View v, NotificationChannel c, int appUid) -> { + }, null, null, null, null); final TextView settingsButton = (TextView) mNotificationInfo.findViewById(R.id.more_settings); assertEquals(getStringById(R.string.notification_all_categories), settingsButton.getText()); @@ -249,8 +299,11 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SetsOnClickListenerForDone() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, - (View v) -> { latch.countDown(); }, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, + null, (View v) -> { + latch.countDown(); + }, null, null); final TextView doneButton = (TextView) mNotificationInfo.findViewById(R.id.done); @@ -263,7 +316,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_NumChannelsTextUniqueWhenDefaultChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - null, null, null, null); + mNotificationChannel.getImportance(), mSbn, null, null, + null, null, null); final TextView numChannelsView = (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); assertEquals(View.VISIBLE, numChannelsView.getVisibility()); @@ -275,7 +329,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_NumChannelsTextDisplaysWhenNotDefaultChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); final TextView numChannelsView = (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); assertEquals(numChannelsView.getVisibility(), View.VISIBLE); @@ -288,7 +344,9 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockINotificationManager.getNumNotificationChannelsForPackage( eq(TEST_PACKAGE_NAME), anyInt(), anyBoolean())).thenReturn(2); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); final TextView numChannelsView = (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); assertEquals(getNumChannelsDescString(2), numChannelsView.getText()); @@ -300,7 +358,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - null, null, null, null); + mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); final TextView numChannelsView = (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); assertEquals(getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel), @@ -316,7 +374,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel), - null, null, null, null); + mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); final TextView numChannelsView = (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); assertEquals( @@ -336,8 +394,8 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel, - fourthChannel), - null, null, null, null); + fourthChannel), mNotificationChannel.getImportance(), mSbn, null, null, + null, null, null); final TextView numChannelsView = (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); assertEquals( @@ -352,7 +410,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - null, null, null, null); + mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); final TextView channelNameView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); assertEquals(getNumChannelsString(2), channelNameView.getText()); @@ -363,7 +421,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testEnabledSwitchInvisibleIfBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - null, null, null, null); + mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); } @@ -371,7 +429,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testbindNotification_ChannelDisabledTextGoneWhenNotDisabled() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); final TextView channelDisabledView = (TextView) mNotificationInfo.findViewById(R.id.channel_disabled); assertEquals(channelDisabledView.getVisibility(), View.GONE); @@ -381,7 +440,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testbindNotification_ChannelDisabledTextVisibleWhenDisabled() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); final TextView channelDisabledView = (TextView) mNotificationInfo.findViewById(R.id.channel_disabled); assertEquals(channelDisabledView.getVisibility(), View.VISIBLE); @@ -398,7 +459,8 @@ public class NotificationInfoTest extends SysuiTestCase { mDefaultNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - null, null, null, null); + mDefaultNotificationChannel.getImportance(), mSbn, null, null, + null, null, null); final TextView channelDisabledView = (TextView) mNotificationInfo.findViewById(R.id.channel_disabled); assertEquals(View.VISIBLE, channelDisabledView.getVisibility()); @@ -407,7 +469,9 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), anyInt(), any()); } @@ -416,7 +480,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); enabledSwitch.setChecked(false); @@ -428,7 +494,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); mNotificationInfo.handleCloseControls(true); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -440,7 +508,9 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); mNotificationInfo.handleCloseControls(true); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -451,7 +521,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testEnabledSwitchOnByDefault() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); assertTrue(enabledSwitch.isChecked()); @@ -461,7 +533,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testEnabledButtonOffWhenAlreadyBanned() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); assertFalse(enabledSwitch.isChecked()); @@ -471,7 +545,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testEnabledSwitchVisibleByDefault() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, null); Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); assertEquals(View.VISIBLE, enabledSwitch.getVisibility()); @@ -481,8 +557,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, Collections.singleton(TEST_PACKAGE_NAME)); Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); @@ -492,8 +569,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, Collections.singleton(TEST_PACKAGE_NAME)); mNotificationInfo.handleCloseControls(true); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), anyInt(), any()); @@ -503,8 +581,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, Collections.singleton(TEST_PACKAGE_NAME)); Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); enabledSwitch.setChecked(false); @@ -517,8 +596,9 @@ public class NotificationInfoTest extends SysuiTestCase { public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + null, Collections.singleton(TEST_PACKAGE_NAME)); Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); enabledSwitch.setChecked(false); @@ -531,8 +611,10 @@ public class NotificationInfoTest extends SysuiTestCase { public void testCloseControlsDoesNotUpdateIfCheckSaveListenerIsNoOp() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, - (Runnable saveImportance) -> {}, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + (Runnable saveImportance) -> { + }, Collections.singleton(TEST_PACKAGE_NAME)); Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); @@ -546,8 +628,11 @@ public class NotificationInfoTest extends SysuiTestCase { public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, - (Runnable saveImportance) -> { saveImportance.run(); }, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), mSbn, null, null, null, + (Runnable saveImportance) -> { + saveImportance.run(); + }, Collections.singleton(TEST_PACKAGE_NAME)); Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); @@ -556,4 +641,137 @@ public class NotificationInfoTest extends SysuiTestCase { verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel)); } + + @Test + public void testDisplaySettingsLink() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final String settingsText = "work chats"; + final ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = new ActivityInfo(); + ri.activityInfo.packageName = TEST_PACKAGE_NAME; + ri.activityInfo.name = "something"; + List<ResolveInfo> ris = new ArrayList<>(); + ris.add(ri); + when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); + mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) + .setSettingsText(settingsText).build(); + StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, + 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); + + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), sbn, null, + (View v, Intent intent) -> { + latch.countDown(); + }, null, null, null); + final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); + assertEquals(View.VISIBLE, settingsLink.getVisibility()); + assertTrue(settingsLink.getText().toString().length() > settingsText.length()); + assertTrue(settingsLink.getText().toString().contains(settingsText)); + settingsLink.performClick(); + assertEquals(0, latch.getCount()); + } + + @Test + public void testDisplaySettingsLink_multipleChannels() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final String settingsText = "work chats"; + final ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = new ActivityInfo(); + ri.activityInfo.packageName = TEST_PACKAGE_NAME; + ri.activityInfo.name = "something"; + List<ResolveInfo> ris = new ArrayList<>(); + ris.add(ri); + when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); + mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) + .setSettingsText(settingsText).build(); + StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, + 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); + + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), + mNotificationChannel.getImportance(), sbn, null, (View v, Intent intent) -> { + latch.countDown(); + }, null, null, null); + final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); + assertEquals(View.VISIBLE, settingsLink.getVisibility()); + settingsLink.performClick(); + assertEquals(0, latch.getCount()); + } + + @Test + public void testNoSettingsLink_noHandlingActivity() throws Exception { + final String settingsText = "work chats"; + when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null); + mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) + .setSettingsText(settingsText).build(); + StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, + 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); + + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), sbn, null, null, null, + null, null); + final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); + assertEquals(View.GONE, settingsLink.getVisibility()); + } + + @Test + public void testNoSettingsLink_noLinkText() throws Exception { + final ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = new ActivityInfo(); + ri.activityInfo.packageName = TEST_PACKAGE_NAME; + ri.activityInfo.name = "something"; + List<ResolveInfo> ris = new ArrayList<>(); + ris.add(ri); + when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); + mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build(); + StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, + 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); + + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), sbn, null, null, null, + null, null); + final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); + assertEquals(View.GONE, settingsLink.getVisibility()); + } + + @Test + public void testNoSettingsLink_afterBlockingChannel() throws Exception { + final String settingsText = "work chats"; + final ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = new ActivityInfo(); + ri.activityInfo.packageName = TEST_PACKAGE_NAME; + ri.activityInfo.name = "something"; + List<ResolveInfo> ris = new ArrayList<>(); + ris.add(ri); + when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); + mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) + .setSettingsText(settingsText).build(); + StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, + 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); + + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), + mNotificationChannel.getImportance(), sbn, null, null, null, + null, null); + final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); + assertEquals(View.VISIBLE, settingsLink.getVisibility()); + + // Block channel + Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); + enabledSwitch.setChecked(false); + + assertEquals(View.GONE, settingsLink.getVisibility()); + + //unblock + enabledSwitch.setChecked(true); + assertEquals(View.VISIBLE, settingsLink.getVisibility()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java index 31b9bae846d9..efb9fea4f895 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java @@ -44,4 +44,11 @@ public class NotificationMenuRowTest extends LeakCheckedTest { ViewUtils.detachView(row.getMenuView()); TestableLooper.get(this).processAllMessages(); } + + @Test + public void testRecreateMenu() { + NotificationMenuRowPlugin row = new NotificationMenuRow(mContext); + row.createMenu(null); + row.createMenu(null); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java new file mode 100644 index 000000000000..a69de7a68291 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java @@ -0,0 +1,51 @@ +/* + * 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.systemui.statusbar.notification; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.view.View; + +import com.android.systemui.statusbar.ExpandableNotificationRow; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class NotificationViewWrapperTest { + + private Context mContext; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getTargetContext(); + } + + @Test + public void constructor_doesntUseViewContext() throws Exception { + new TestableNotificationViewWrapper(mContext, new View(null /* context */), null /* row */); + } + + static class TestableNotificationViewWrapper extends NotificationViewWrapper { + protected TestableNotificationViewWrapper(Context ctx, View view, + ExpandableNotificationRow row) { + super(ctx, view, row); + } + } +}
\ No newline at end of file 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/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 0482e734c234..ac81565fbf3c 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -1631,7 +1631,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku @Override public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter, - int profileId) { + int profileId, String packageName) { final int userId = UserHandle.getCallingUserId(); if (DEBUG) { @@ -1653,8 +1653,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku Provider provider = mProviders.get(i); AppWidgetProviderInfo info = provider.info; - // Ignore an invalid provider or one not matching the filter. - if (provider.zombie || (info.widgetCategory & categoryFilter) == 0) { + // Ignore an invalid provider, one not matching the filter, + // or one that isn't in the given package, if any. + boolean inPackage = packageName == null + || provider.id.componentName.getPackageName().equals(packageName); + if (provider.zombie || (info.widgetCategory & categoryFilter) == 0 || !inPackage) { continue; } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index be14440a6a5c..502b5fc1ea6f 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -23,7 +23,6 @@ import static com.android.server.autofill.Helper.DEBUG; import static com.android.server.autofill.Helper.VERBOSE; import static com.android.server.autofill.Helper.bundleToString; -import android.Manifest; import android.annotation.NonNull; import android.app.ActivityManagerInternal; import android.content.BroadcastReceiver; @@ -36,7 +35,6 @@ import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Rect; import android.net.Uri; -import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 3d1c2511db27..c8a5780fedf1 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -57,6 +57,7 @@ import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.HandlerCaller; import com.android.internal.os.IResultReceiver; @@ -147,7 +148,7 @@ final class AutofillManagerServiceImpl { // TODO(b/33197203): since service is fetching the data (to use for save later), // we should optimize what's sent (for example, remove layout containers, // color / font info, etc...) - session.mStructure = structure; + session.setStructureLocked(structure); } @@ -193,13 +194,17 @@ final class AutofillManagerServiceImpl { } } + private String getComponentNameFromSettings() { + return Settings.Secure.getStringForUser( + mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId); + } + void updateLocked(boolean disabled) { final boolean wasEnabled = isEnabled(); mDisabled = disabled; ComponentName serviceComponent = null; ServiceInfo serviceInfo = null; - final String componentName = Settings.Secure.getStringForUser( - mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId); + final String componentName = getComponentNameFromSettings(); if (!TextUtils.isEmpty(componentName)) { try { serviceComponent = ComponentName.unflattenFromString(componentName); @@ -290,7 +295,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... @@ -411,8 +417,7 @@ final class AutofillManagerServiceImpl { void disableSelf() { final long identity = Binder.clearCallingIdentity(); try { - final String autoFillService = Settings.Secure.getStringForUser( - mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId); + final String autoFillService = getComponentNameFromSettings(); if (mInfo.getServiceInfo().getComponentName().equals( ComponentName.unflattenFromString(autoFillService))) { Settings.Secure.putStringForUser(mContext.getContentResolver(), @@ -430,9 +435,14 @@ final class AutofillManagerServiceImpl { void dumpLocked(String prefix, PrintWriter pw) { final String prefix2 = prefix + " "; - pw.print(prefix); pw.print("Component:"); pw.println(mInfo != null + pw.print(prefix); pw.print("User: "); pw.println(mUserId); + pw.print(prefix); pw.print("Component: "); pw.println(mInfo != null ? mInfo.getServiceInfo().getComponentName() : null); - pw.print(prefix); pw.print("Disabled:"); pw.println(mDisabled); + pw.print(prefix); pw.print("Component from settings: "); + pw.println(getComponentNameFromSettings()); + pw.print(prefix); pw.print("Default component: "); + pw.println(mContext.getString(R.string.config_defaultAutofillService)); + pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled); if (VERBOSE && mInfo != null) { // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps) diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index ac7d19eaba46..a3da50e9aaea 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -29,6 +29,7 @@ import static com.android.server.autofill.Helper.VERBOSE; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.assist.AssistStructure; +import android.app.assist.AssistStructure.AutofillOverlay; import android.app.assist.AssistStructure.ViewNode; import android.app.assist.AssistStructure.WindowNode; import android.content.ComponentName; @@ -48,6 +49,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; @@ -99,7 +101,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull private final String mPackageName; @GuardedBy("mLock") - private final Map<AutofillId, ViewState> mViewStates = new ArrayMap<>(); + private final ArrayMap<AutofillId, ViewState> mViewStates = new ArrayMap<>(); /** * Id of the View currently being displayed. @@ -112,27 +114,27 @@ 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; /** * Assist structure sent by the app; it will be updated (sanitized, change values for save) * before sent to {@link AutofillService}. */ - @GuardedBy("mLock") AssistStructure mStructure; + @GuardedBy("mLock") + private AssistStructure mStructure; /** * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. @@ -140,6 +142,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private boolean mHasCallback; /** + * Extras sent by service on {@code onFillRequest()} calls; the first non-null extra is saved + * and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls. + */ + @GuardedBy("mLock") + private Bundle mExtras; + + /** * Flags used to start the session. */ int mFlags; @@ -163,8 +172,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 +202,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 +331,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; + } } } } @@ -344,6 +361,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mHasCallback = hasIt; } + public void setStructureLocked(AssistStructure structure) { + mStructure = structure; + } + /** * Shows the save UI, when session can be saved. * @@ -354,15 +375,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 +410,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 +417,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,8 +487,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates); } - final Bundle extras = this.mCurrentResponse.getExtras(); - for (Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) { final AutofillValue value = entry.getValue().getCurrentValue(); if (value == null) { @@ -493,20 +516,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mStructure.dump(); } - mRemoteFillService.onSaveRequest(mStructure, extras); + mRemoteFillService.onSaveRequest(mStructure, mExtras); } 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, value, this, ViewState.STATE_INITIAL); + mViewStates.put(id, viewState); + } else if ((flags & FLAG_VIEW_ENTERED) != 0) { + viewState = startPartitionLocked(id, value); + } else { + if (VERBOSE) Slog.v(TAG, "Ignored " + getFlagAsString(flags) + " for " + id); + return; + } } if ((flags & FLAG_START_SESSION) != 0) { @@ -530,7 +558,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 +580,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 +591,41 @@ 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, AutofillValue value) { + if (DEBUG) { + Slog.d(TAG, "Starting partition for view id " + id); + } + final ViewState newViewState = + new ViewState(this, id, value, this,ViewState.STATE_STARTED_PARTITION); + mViewStates.put(id, newViewState); + + // Must update value of nodes so: + // - proper node is focused + // - autofillValue is sent back to service when it was previously autofilled + for (int i = 0; i < mViewStates.size(); i++) { + final ViewState viewState = mViewStates.valueAt(i); + + final ViewNode node = findViewNodeByIdLocked(viewState.id); + if (node == null) { + Slog.w(TAG, "startPartitionLocked(): no node for " + viewState.id); + continue; + } + + final AutofillValue initialValue = viewState.getInitialValue(); + final AutofillValue filledValue = viewState.getAutofilledValue(); + final AutofillOverlay overlay = new AutofillOverlay(); + if (filledValue != null && !filledValue.equals(initialValue)) { + overlay.value = filledValue; + } + overlay.focused = id.equals(viewState.id); + node.setAutofillOverlay(overlay); + } + mRemoteFillService.onFillRequest(mStructure, mExtras, 0); + + return newViewState; } @Override @@ -580,6 +639,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 +660,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 +669,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - mCurrentResponse = response; + if (mResponses == null) { + mResponses = new ArrayList<>(4); + } + mResponses.add(response); + if (response != null) { + mExtras = response.getExtras(); + } setViewStatesLocked(response, ViewState.STATE_FILLABLE); @@ -653,7 +721,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (viewState != null) { viewState.setState(state); } else { - viewState = new ViewState(this, id, this, state); + viewState = new ViewState(this, id, null, this, state); if (DEBUG) { // TODO(b/33197203): change to VERBOSE once stable Slog.d(TAG, "Adding autofillable view with id " + id + " and state " + state); } @@ -669,10 +737,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 +760,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 +772,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 +796,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()); @@ -735,6 +817,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback); + pw.print(prefix); pw.print("mExtras: "); pw.println(Helper.bundleToString(mExtras)); mRemoteFillService.dump(prefix, pw); } @@ -811,4 +894,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 20def0c5a4f8..f8919eeb060b 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,21 +57,27 @@ 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; private final Session mSession; private FillResponse mResponse; + private AutofillValue mInitialValue; private AutofillValue mCurrentValue; private AutofillValue mAutofilledValue; private Rect mVirtualBounds; private int mState; - ViewState(Session session, AutofillId id, Listener listener, int state) { + ViewState(Session session, AutofillId id, AutofillValue value, Listener listener, int state) { mSession = session; this.id = id; + mInitialValue = value; mListener = listener; mState = state; } @@ -101,6 +112,11 @@ final class ViewState { } @Nullable + AutofillValue getInitialValue() { + return mInitialValue; + } + + @Nullable FillResponse getResponse() { return mResponse; } @@ -122,9 +138,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 +169,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,22 +183,24 @@ 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 != null && currentResponse.getAuthentication() != null) { - mListener.onFillReady(currentResponse, this.id, mCurrentValue); + final FillResponse responseWaitingAuth = mSession.getResponseWaitingAuth(); + if (responseWaitingAuth != null) { + mListener.onFillReady(responseWaitingAuth, this.id, mCurrentValue); } } @Override public String toString() { - return "ViewState: [id=" + id + ", currentValue=" + mCurrentValue - + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() +"]"; + return "ViewState: [id=" + id + ", initialValue=" + mInitialValue + + ", currentValue=" + mCurrentValue + ", autofilledValue=" + mAutofilledValue + + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() + "]"; } void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("id:" ); pw.println(this.id); pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString()); pw.print(prefix); pw.print("has response:" ); pw.println(mResponse != null); + pw.print(prefix); pw.print("initialValue:" ); pw.println(mInitialValue); pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue); pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue); pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds); diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index d38fb9664124..b69d1dc3d97c 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -101,8 +101,9 @@ final class FillUi { content.measure(widthMeasureSpec, heightMeasureSpec); content.setOnClickListener(v -> mCallback.onResponsePicked(response)); content.setElevation(context.getResources().getDimension(R.dimen.floating_window_z)); - mContentWidth = content.getMeasuredWidth(); - mContentHeight = content.getMeasuredHeight(); + // TODO(b/33197203 , b/36660292): temporary limiting maximum height and minimum width + mContentWidth = Math.max(content.getMeasuredWidth(), 1000); + mContentHeight = Math.min(content.getMeasuredHeight(), 500); mWindow = new AnchoredWindow(content); mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter); diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 509351bf0858..2c08afadf10b 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -117,6 +117,12 @@ final class SaveUi { case SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD: type = context.getString(R.string.autofill_save_type_credit_card); break; + case SaveInfo.SAVE_DATA_TYPE_USERNAME: + type = context.getString(R.string.autofill_save_type_username); + break; + case SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS: + type = context.getString(R.string.autofill_save_type_email_address); + break; default: type = null; } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java new file mode 100644 index 000000000000..a7ce95ba0c88 --- /dev/null +++ b/services/core/java/com/android/server/IpSecService.java @@ -0,0 +1,494 @@ +/* + * 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 static android.Manifest.permission.DUMP; +import static android.net.IpSecManager.INVALID_RESOURCE_ID; +import static android.net.IpSecManager.KEY_RESOURCE_ID; +import static android.net.IpSecManager.KEY_SPI; +import static android.net.IpSecManager.KEY_STATUS; + +import android.content.Context; +import android.net.IIpSecService; +import android.net.INetd; +import android.net.IpSecAlgorithm; +import android.net.IpSecConfig; +import android.net.IpSecManager; +import android.net.IpSecTransform; +import android.net.util.NetdService; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.concurrent.atomic.AtomicInteger; + +/** @hide */ +public class IpSecService extends IIpSecService.Stub { + private static final String TAG = "IpSecService"; + private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); + private static final String NETD_SERVICE_NAME = "netd"; + private static final int[] DIRECTIONS = + new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}; + + /** Binder context for this service */ + private final Context mContext; + + private Object mLock = new Object(); + + private static final int NETD_FETCH_TIMEOUT = 5000; //ms + + private AtomicInteger mNextResourceId = new AtomicInteger(0x00FADED0); + + private abstract class ManagedResource implements IBinder.DeathRecipient { + final int pid; + final int uid; + private IBinder mBinder; + + ManagedResource(IBinder binder) { + super(); + mBinder = binder; + pid = Binder.getCallingPid(); + uid = Binder.getCallingUid(); + + try { + mBinder.linkToDeath(this, 0); + } catch (RemoteException e) { + binderDied(); + } + } + + /** + * When this record is no longer needed for managing system resources this function should + * unlink all references held by the record to allow efficient garbage collection. + */ + public final void release() { + //Release all the underlying system resources first + releaseResources(); + + if (mBinder != null) { + mBinder.unlinkToDeath(this, 0); + } + mBinder = null; + + //remove this record so that it can be cleaned up + nullifyRecord(); + } + + /** + * If the Binder object dies, this function is called to free the system resources that are + * being managed by this record and to subsequently release this record for garbage + * collection + */ + public final void binderDied() { + release(); + } + + /** + * Implement this method to release all object references contained in the subclass to allow + * efficient garbage collection of the record. This should remove any references to the + * record from all other locations that hold a reference as the record is no longer valid. + */ + protected abstract void nullifyRecord(); + + /** + * Implement this method to release all system resources that are being protected by this + * record. Once the resources are released, the record should be invalidated and no longer + * used by calling releaseRecord() + */ + protected abstract void releaseResources(); + }; + + private final class TransformRecord extends ManagedResource { + private IpSecConfig mConfig; + private int mResourceId; + + TransformRecord(IpSecConfig config, int resourceId, IBinder binder) { + super(binder); + mConfig = config; + mResourceId = resourceId; + } + + public IpSecConfig getConfig() { + return mConfig; + } + + @Override + protected void releaseResources() { + for (int direction : DIRECTIONS) { + try { + getNetdInstance() + .ipSecDeleteSecurityAssociation( + mResourceId, + direction, + (mConfig.getLocalAddress() != null) + ? mConfig.getLocalAddress().getHostAddress() + : "", + (mConfig.getRemoteAddress() != null) + ? mConfig.getRemoteAddress().getHostAddress() + : "", + mConfig.getSpi(direction)); + } catch (ServiceSpecificException e) { + // FIXME: get the error code and throw is at an IOException from Errno Exception + } catch (RemoteException e) { + Log.e(TAG, "Failed to delete SA with ID: " + mResourceId); + } + } + } + + @Override + protected void nullifyRecord() { + mConfig = null; + mResourceId = INVALID_RESOURCE_ID; + } + } + + private final class SpiRecord extends ManagedResource { + private final int mDirection; + private final String mLocalAddress; + private final String mRemoteAddress; + private final IBinder mBinder; + private int mSpi; + private int mResourceId; + + SpiRecord( + int resourceId, + int direction, + String localAddress, + String remoteAddress, + int spi, + IBinder binder) { + super(binder); + mResourceId = resourceId; + mDirection = direction; + mLocalAddress = localAddress; + mRemoteAddress = remoteAddress; + mSpi = spi; + mBinder = binder; + } + + protected void releaseResources() { + try { + getNetdInstance() + .ipSecDeleteSecurityAssociation( + mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi); + } catch (ServiceSpecificException e) { + // FIXME: get the error code and throw is at an IOException from Errno Exception + } catch (RemoteException e) { + Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId); + } + } + + protected void nullifyRecord() { + mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; + mResourceId = INVALID_RESOURCE_ID; + } + } + + @GuardedBy("mSpiRecords") + private final SparseArray<SpiRecord> mSpiRecords = new SparseArray<>(); + + @GuardedBy("mTransformRecords") + private final SparseArray<TransformRecord> mTransformRecords = new SparseArray<>(); + + /** + * Constructs a new IpSecService instance + * + * @param context Binder context for this service + */ + private IpSecService(Context context) { + mContext = context; + } + + static IpSecService create(Context context) throws InterruptedException { + final IpSecService service = new IpSecService(context); + service.connectNativeNetdService(); + return service; + } + + public void systemReady() { + if (isNetdAlive()) { + Slog.d(TAG, "IpSecService is ready"); + } else { + Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!"); + } + } + + private void connectNativeNetdService() { + // Avoid blocking the system server to do this + Thread t = + new Thread( + new Runnable() { + @Override + public void run() { + synchronized (mLock) { + NetdService.get(NETD_FETCH_TIMEOUT); + } + } + }); + t.run(); + } + + INetd getNetdInstance() throws RemoteException { + final INetd netd = NetdService.getInstance(); + if (netd == null) { + throw new RemoteException("Failed to Get Netd Instance"); + } + return netd; + } + + boolean isNetdAlive() { + synchronized (mLock) { + try { + final INetd netd = getNetdInstance(); + if (netd == null) { + return false; + } + return netd.isAlive(); + } catch (RemoteException re) { + return false; + } + } + } + + @Override + /** Get a new SPI and maintain the reservation in the system server */ + public Bundle reserveSecurityParameterIndex( + int direction, String remoteAddress, int requestedSpi, IBinder binder) + throws RemoteException { + int resourceId = mNextResourceId.getAndIncrement(); + + int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; + String localAddress = ""; + Bundle retBundle = new Bundle(3); + try { + spi = + getNetdInstance() + .ipSecAllocateSpi( + resourceId, + direction, + localAddress, + remoteAddress, + requestedSpi); + Log.d(TAG, "Allocated SPI " + spi); + retBundle.putInt(KEY_STATUS, IpSecManager.Status.OK); + retBundle.putInt(KEY_RESOURCE_ID, resourceId); + retBundle.putInt(KEY_SPI, spi); + synchronized (mSpiRecords) { + mSpiRecords.put( + resourceId, + new SpiRecord( + resourceId, direction, localAddress, remoteAddress, spi, binder)); + } + } catch (ServiceSpecificException e) { + // TODO: Add appropriate checks when other ServiceSpecificException types are supported + retBundle.putInt(KEY_STATUS, IpSecManager.Status.SPI_UNAVAILABLE); + retBundle.putInt(KEY_RESOURCE_ID, resourceId); + retBundle.putInt(KEY_SPI, spi); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return retBundle; + } + + /** Release a previously allocated SPI that has been registered with the system server */ + @Override + public void releaseSecurityParameterIndex(int resourceId) throws RemoteException {} + + /** + * Open a socket via the system server and bind it to the specified port (random if port=0). + * This will return a PFD to the user that represent a bound UDP socket. The system server will + * cache the socket and a record of its owner so that it can and must be freed when no longer + * needed. + */ + @Override + public Bundle openUdpEncapsulationSocket(int port, IBinder binder) throws RemoteException { + return null; + } + + /** close a socket that has been been allocated by and registered with the system server */ + @Override + public void closeUdpEncapsulationSocket(ParcelFileDescriptor socket) {} + + /** + * Create a transport mode transform, which represent two security associations (one in each + * direction) in the kernel. The transform will be cached by the system server and must be freed + * when no longer needed. It is possible to free one, deleting the SA from underneath sockets + * that are using it, which will result in all of those sockets becoming unable to send or + * receive data. + */ + @Override + public Bundle createTransportModeTransform(IpSecConfig c, IBinder binder) + throws RemoteException { + // TODO: Basic input validation here since it's coming over the Binder + int resourceId = mNextResourceId.getAndIncrement(); + for (int direction : DIRECTIONS) { + IpSecAlgorithm auth = c.getAuthentication(direction); + IpSecAlgorithm crypt = c.getEncryption(direction); + try { + int result = + getNetdInstance() + .ipSecAddSecurityAssociation( + resourceId, + c.getMode(), + direction, + (c.getLocalAddress() != null) + ? c.getLocalAddress().getHostAddress() + : "", + (c.getRemoteAddress() != null) + ? c.getRemoteAddress().getHostAddress() + : "", + (c.getNetwork() != null) + ? c.getNetwork().getNetworkHandle() + : 0, + c.getSpi(direction), + (auth != null) ? auth.getName() : "", + (auth != null) ? auth.getKey() : null, + (auth != null) ? auth.getTruncationLengthBits() : 0, + (crypt != null) ? crypt.getName() : "", + (crypt != null) ? crypt.getKey() : null, + (crypt != null) ? crypt.getTruncationLengthBits() : 0, + c.getEncapType(), + c.getEncapLocalPort(), + c.getEncapRemotePort()); + if (result != c.getSpi(direction)) { + // TODO: cleanup the first SA if creation of second SA fails + Bundle retBundle = new Bundle(2); + retBundle.putInt(KEY_STATUS, IpSecManager.Status.SPI_UNAVAILABLE); + retBundle.putInt(KEY_RESOURCE_ID, INVALID_RESOURCE_ID); + return retBundle; + } + } catch (ServiceSpecificException e) { + // FIXME: get the error code and throw is at an IOException from Errno Exception + } + } + synchronized (mTransformRecords) { + mTransformRecords.put(resourceId, new TransformRecord(c, resourceId, binder)); + } + + Bundle retBundle = new Bundle(2); + retBundle.putInt(KEY_STATUS, IpSecManager.Status.OK); + retBundle.putInt(KEY_RESOURCE_ID, resourceId); + return retBundle; + } + + /** + * Delete a transport mode transform that was previously allocated by + registered with the + * system server. If this is called on an inactive (or non-existent) transform, it will not + * return an error. It's safe to de-allocate transforms that may have already been deleted for + * other reasons. + */ + @Override + public void deleteTransportModeTransform(int resourceId) throws RemoteException { + synchronized (mTransformRecords) { + TransformRecord record; + // We want to non-destructively get so that we can check credentials before removing + // this from the records. + record = mTransformRecords.get(resourceId); + + if (record == null) { + throw new IllegalArgumentException( + "Transform " + resourceId + " is not available to be deleted"); + } + + if (record.pid != Binder.getCallingPid() || record.uid != Binder.getCallingUid()) { + throw new SecurityException("Only the owner of an IpSec Transform may delete it!"); + } + + // TODO: if releaseResources() throws RemoteException, we can try again to clean up on + // binder death. Need to make sure that path is actually functional. + record.releaseResources(); + mTransformRecords.remove(resourceId); + record.nullifyRecord(); + } + } + + /** + * Apply an active transport mode transform to a socket, which will apply the IPsec security + * association as a correspondent policy to the provided socket + */ + @Override + public void applyTransportModeTransform(ParcelFileDescriptor socket, int resourceId) + throws RemoteException { + + synchronized (mTransformRecords) { + TransformRecord info; + // FIXME: this code should be factored out into a security check + getter + info = mTransformRecords.get(resourceId); + + if (info == null) { + throw new IllegalArgumentException("Transform " + resourceId + " is not active"); + } + + // TODO: make this a function. + if (info.pid != getCallingPid() || info.uid != getCallingUid()) { + throw new SecurityException("Only the owner of an IpSec Transform may apply it!"); + } + + IpSecConfig c = info.getConfig(); + try { + for (int direction : DIRECTIONS) { + getNetdInstance() + .ipSecApplyTransportModeTransform( + socket.getFileDescriptor(), + resourceId, + direction, + (c.getLocalAddress() != null) + ? c.getLocalAddress().getHostAddress() + : "", + (c.getRemoteAddress() != null) + ? c.getRemoteAddress().getHostAddress() + : "", + c.getSpi(direction)); + } + } catch (ServiceSpecificException e) { + // FIXME: get the error code and throw is at an IOException from Errno Exception + } + } + } + /** + * Remove a transport mode transform from a socket, applying the default (empty) policy. This + * will ensure that NO IPsec policy is applied to the socket (would be the equivalent of + * applying a policy that performs no IPsec). Today the resourceId parameter is passed but not + * used: reserved for future improved input validation. + */ + @Override + public void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId) + throws RemoteException { + try { + getNetdInstance().ipSecRemoveTransportModeTransform(socket.getFileDescriptor()); + } catch (ServiceSpecificException e) { + // FIXME: get the error code and throw is at an IOException from Errno Exception + } + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + mContext.enforceCallingOrSelfPermission(DUMP, TAG); + + pw.println("IpSecService Log:"); + pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead")); + pw.println(); + } +} 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..8404025e6726 100644 --- a/services/core/java/com/android/server/NetworkScorerAppManager.java +++ b/services/core/java/com/android/server/NetworkScorerAppManager.java @@ -286,6 +286,57 @@ public class NetworkScorerAppManager { } } + /** + * Migrates the NETWORK_SCORER_APP Setting to the USE_OPEN_WIFI_PACKAGE Setting. + */ + @VisibleForTesting + public 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/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java index 06104646cd87..7f5befabc576 100644 --- a/services/core/java/com/android/server/SensorNotificationService.java +++ b/services/core/java/com/android/server/SensorNotificationService.java @@ -20,25 +20,46 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.hardware.GeomagneticField; import android.hardware.Sensor; +import android.hardware.SensorAdditionalInfo; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; -public class SensorNotificationService extends SystemService implements SensorEventListener { - //TODO: set DBG to false or remove Slog before release - private static final boolean DBG = true; +public class SensorNotificationService extends SystemService + implements SensorEventListener, LocationListener { + private static final boolean DBG = false; private static final String TAG = "SensorNotificationService"; - private Context mContext; + private static final long MINUTE_IN_MS = 60 * 1000; + private static final long KM_IN_M = 1000; + + private static final long LOCATION_MIN_TIME = 30 * MINUTE_IN_MS; + private static final long LOCATION_MIN_DISTANCE = 100 * KM_IN_M; + + private static final String PROPERTY_USE_MOCKED_LOCATION = + "sensor.notification.use_mocked"; // max key length is 32 + + private static final long MILLIS_2010_1_1 = 1262358000000l; + + private Context mContext; private SensorManager mSensorManager; + private LocationManager mLocationManager; private Sensor mMetaSensor; + // for rate limiting + private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME; + public SensorNotificationService(Context context) { super(context); mContext = context; @@ -50,7 +71,6 @@ public class SensorNotificationService extends SystemService implements SensorEv public void onBootPhase(int phase) { if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { - // start mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE); mMetaSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DYNAMIC_SENSOR_META); if (mMetaSensor == null) { @@ -60,13 +80,28 @@ public class SensorNotificationService extends SystemService implements SensorEv SensorManager.SENSOR_DELAY_FASTEST); } } + + if (phase == PHASE_BOOT_COMPLETED) { + // LocationManagerService is initialized after PHASE_THIRD_PARTY_APPS_CAN_START + mLocationManager = + (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); + if (mLocationManager == null) { + if (DBG) Slog.d(TAG, "Cannot obtain location service."); + } else { + mLocationManager.requestLocationUpdates( + LocationManager.PASSIVE_PROVIDER, + LOCATION_MIN_TIME, + LOCATION_MIN_DISTANCE, + this); + } + } } private void broadcastDynamicSensorChanged() { Intent i = new Intent(Intent.ACTION_DYNAMIC_SENSOR_CHANGED); i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); // avoid waking up manifest receivers mContext.sendBroadcastAsUser(i, UserHandle.ALL); - if (DBG) Slog.d(TAG, "DYNS sent dynamic sensor broadcast"); + if (DBG) Slog.d(TAG, "dynamic sensor broadcast sent"); } @Override @@ -77,8 +112,62 @@ public class SensorNotificationService extends SystemService implements SensorEv } @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { + public void onLocationChanged(Location location) { + if (DBG) Slog.d(TAG, String.format( + "Location is (%f, %f), h %f, acc %f, mocked %b", + location.getLatitude(), location.getLongitude(), + location.getAltitude(), location.getAccuracy(), + location.isFromMockProvider())); + + // lat long == 0 usually means invalid location + if (location.getLatitude() == 0 && location.getLongitude() == 0) { + return; + } + + // update too often, ignore + if (SystemClock.elapsedRealtime() - mLocalGeomagneticFieldUpdateTime < 10 * MINUTE_IN_MS) { + return; + } + + long time = System.currentTimeMillis(); + // Mocked location should not be used. Except in test, only use mocked location + // Wrong system clock also gives bad values so ignore as well. + if (useMockedLocation() == location.isFromMockProvider() || time < MILLIS_2010_1_1) { + return; + } + + GeomagneticField field = new GeomagneticField( + (float) location.getLatitude(), (float) location.getLongitude(), + (float) location.getAltitude(), time); + if (DBG) Slog.d(TAG, String.format( + "Nominal mag field, norm %fuT, decline %f deg, incline %f deg", + field.getFieldStrength() / 1000, field.getDeclination(), field.getInclination())); + + try { + SensorAdditionalInfo info = SensorAdditionalInfo.createLocalGeomagneticField( + field.getFieldStrength() / 1000, // convert from nT to uT + (float)(field.getDeclination() * Math.PI / 180), // from degree to rad + (float)(field.getInclination() * Math.PI / 180)); // from degree to rad + if (info != null) { + mSensorManager.setOperationParameter(info); + mLocalGeomagneticFieldUpdateTime = SystemClock.elapsedRealtime(); + } + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Invalid local geomagnetic field, ignore."); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) {} + @Override + public void onStatusChanged(String provider, int status, Bundle extras) {} + @Override + public void onProviderEnabled(String provider) {} + @Override + public void onProviderDisabled(String provider) {} + private boolean useMockedLocation() { + return "false".equals(System.getProperty(PROPERTY_USE_MOCKED_LOCATION, "false")); } } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 7c1a6093beda..dd4d9065f126 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -32,37 +32,39 @@ import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; -import android.telephony.CellLocation; -import android.telephony.Rlog; -import android.telephony.TelephonyManager; -import android.telephony.SubscriptionManager; -import android.telephony.PhoneStateListener; -import android.telephony.ServiceState; -import android.telephony.SignalStrength; import android.telephony.CellInfo; -import android.telephony.VoLteServiceState; +import android.telephony.CellLocation; import android.telephony.DisconnectCause; +import android.telephony.PhoneStateListener; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; import android.telephony.PreciseDisconnectCause; +import android.telephony.Rlog; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.telephony.VoLteServiceState; import android.text.TextUtils; -import android.text.format.Time; - -import java.util.ArrayList; -import java.util.List; -import java.io.FileDescriptor; -import java.io.PrintWriter; +import android.util.LocalLog; import com.android.internal.app.IBatteryStats; import com.android.internal.telephony.IOnSubscriptionsChangedListener; -import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.IPhoneStateListener; +import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.PhoneConstantConversions; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.DumpUtils; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.am.BatteryStatsService; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * Since phone process can be restarted, this class provides a centralized place * that applications can register and be called back from. @@ -159,8 +161,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private String[] mDataConnectionReason; - private String[] mDataConnectionApn; - private ArrayList<String>[] mConnectedApns; private LinkProperties[] mDataConnectionLinkProperties; @@ -191,6 +191,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private boolean mCarrierNetworkChangeState = false; + private final LocalLog mLocalLog = new LocalLog(100); + private PreciseDataConnectionState mPreciseDataConnectionState = new PreciseDataConnectionState(); @@ -310,7 +312,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mMessageWaiting = new boolean[numPhones]; mDataConnectionPossible = new boolean[numPhones]; mDataConnectionReason = new String[numPhones]; - mDataConnectionApn = new String[numPhones]; mCallForwarding = new boolean[numPhones]; mCellLocation = new Bundle[numPhones]; mDataConnectionLinkProperties = new LinkProperties[numPhones]; @@ -329,7 +330,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallForwarding[i] = false; mDataConnectionPossible[i] = false; mDataConnectionReason[i] = ""; - mDataConnectionApn[i] = ""; mCellLocation[i] = new Bundle(); mCellInfo.add(i, null); mConnectedApns[i] = new ArrayList<String>(); @@ -536,7 +536,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { if (DBG) { log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); } - if (VDBG) toStringLogSSC("listen"); if (notifyNow && validatePhoneId(phoneId)) { if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { try { @@ -780,14 +779,14 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } synchronized (mRecords) { + String str = "notifyServiceStateForSubscriber: subId=" + subId + " phoneId=" + phoneId + + " state=" + state; if (VDBG) { - log("notifyServiceStateForSubscriber: subId=" + subId + " phoneId=" + phoneId - + " state=" + state); + log(str); } + mLocalLog.log(str); if (validatePhoneId(phoneId)) { mServiceState[phoneId] = state; - logServiceStateChanged("notifyServiceStateForSubscriber", subId, phoneId, state); - if (VDBG) toStringLogSSC("notifyServiceStateForSubscriber"); for (Record r : mRecords) { if (VDBG) { @@ -885,7 +884,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { if (VDBG) { log("notifySignalStrengthForPhoneId: subId=" + subId +" phoneId=" + phoneId + " signalStrength=" + signalStrength); - toStringLogSSC("notifySignalStrengthForPhoneId"); } synchronized (mRecords) { @@ -1137,18 +1135,20 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { modified = true; } if (modified) { - if (DBG) { - log("onDataConnectionStateChanged(" + mDataConnectionState[phoneId] - + ", " + mDataConnectionNetworkType[phoneId] + ")"); - } + String str = "onDataConnectionStateChanged(" + mDataConnectionState[phoneId] + + ", " + mDataConnectionNetworkType[phoneId] + ")"; + log(str); + mLocalLog.log(str); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) && idMatch(r.subId, subId, phoneId)) { try { - log("Notify data connection state changed on sub: " + - subId); - r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], + if (DBG) { + log("Notify data connection state changed on sub: " + subId); + } + r.callback.onDataConnectionStateChanged( + mDataConnectionState[phoneId], mDataConnectionNetworkType[phoneId]); } catch (RemoteException ex) { mRemoveList.add(r.binder); @@ -1163,7 +1163,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) { try { - r.callback.onPreciseDataConnectionStateChanged(mPreciseDataConnectionState); + r.callback.onPreciseDataConnectionStateChanged( + mPreciseDataConnectionState); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -1391,36 +1392,58 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + synchronized (mRecords) { final int recordCount = mRecords.size(); pw.println("last known state:"); + pw.increaseIndent(); for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) { - pw.println(" Phone Id=" + i); - pw.println(" mCallState=" + mCallState[i]); - pw.println(" mCallIncomingNumber=" + mCallIncomingNumber[i]); - pw.println(" mServiceState=" + mServiceState[i]); - pw.println(" mVoiceActivationState= " + mVoiceActivationState[i]); - pw.println(" mDataActivationState= " + mDataActivationState[i]); - pw.println(" mSignalStrength=" + mSignalStrength[i]); - pw.println(" mMessageWaiting=" + mMessageWaiting[i]); - pw.println(" mCallForwarding=" + mCallForwarding[i]); - pw.println(" mDataActivity=" + mDataActivity[i]); - pw.println(" mDataConnectionState=" + mDataConnectionState[i]); - pw.println(" mDataConnectionPossible=" + mDataConnectionPossible[i]); - pw.println(" mDataConnectionReason=" + mDataConnectionReason[i]); - pw.println(" mDataConnectionApn=" + mDataConnectionApn[i]); - pw.println(" mDataConnectionLinkProperties=" + mDataConnectionLinkProperties[i]); - pw.println(" mDataConnectionNetworkCapabilities=" + + pw.println("Phone Id=" + i); + pw.increaseIndent(); + pw.println("mCallState=" + mCallState[i]); + pw.println("mCallIncomingNumber=" + mCallIncomingNumber[i]); + pw.println("mServiceState=" + mServiceState[i]); + pw.println("mVoiceActivationState= " + mVoiceActivationState[i]); + pw.println("mDataActivationState= " + mDataActivationState[i]); + pw.println("mSignalStrength=" + mSignalStrength[i]); + pw.println("mMessageWaiting=" + mMessageWaiting[i]); + pw.println("mCallForwarding=" + mCallForwarding[i]); + pw.println("mDataActivity=" + mDataActivity[i]); + pw.println("mDataConnectionState=" + mDataConnectionState[i]); + pw.println("mDataConnectionPossible=" + mDataConnectionPossible[i]); + pw.println("mDataConnectionReason=" + mDataConnectionReason[i]); + pw.println("mDataConnectionLinkProperties=" + mDataConnectionLinkProperties[i]); + pw.println("mDataConnectionNetworkCapabilities=" + mDataConnectionNetworkCapabilities[i]); - pw.println(" mCellLocation=" + mCellLocation[i]); - pw.println(" mCellInfo=" + mCellInfo.get(i)); + pw.println("mCellLocation=" + mCellLocation[i]); + pw.println("mCellInfo=" + mCellInfo.get(i)); + pw.decreaseIndent(); } + pw.println("mConnectedApns=" + Arrays.toString(mConnectedApns)); + pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState); + pw.println("mPreciseCallState=" + mPreciseCallState); + pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState); + pw.println("mRingingCallState=" + mRingingCallState); + pw.println("mForegroundCallState=" + mForegroundCallState); + pw.println("mBackgroundCallState=" + mBackgroundCallState); + pw.println("mVoLteServiceState=" + mVoLteServiceState); + + pw.decreaseIndent(); + + pw.println("local logs:"); + pw.increaseIndent(); + mLocalLog.dump(fd, pw, args); + pw.decreaseIndent(); pw.println("registrations: count=" + recordCount); + pw.increaseIndent(); for (Record r : mRecords) { - pw.println(" " + r); + pw.println(r); } + pw.decreaseIndent(); } } @@ -1705,63 +1728,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { Rlog.d(TAG, s); } - private static class LogSSC { - private Time mTime; - private String mS; - private int mSubId; - private int mPhoneId; - private ServiceState mState; - - public void set(Time t, String s, int subId, int phoneId, ServiceState state) { - mTime = t; mS = s; mSubId = subId; mPhoneId = phoneId; mState = state; - } - - @Override - public String toString() { - return mS + " Time " + mTime.toString() + " mSubId " + mSubId + " mPhoneId " - + mPhoneId + " mState " + mState; - } - } - - private LogSSC logSSC [] = new LogSSC[10]; - private int next = 0; - - private void logServiceStateChanged(String s, int subId, int phoneId, ServiceState state) { - if (logSSC == null || logSSC.length == 0) { - return; - } - if (logSSC[next] == null) { - logSSC[next] = new LogSSC(); - } - Time t = new Time(); - t.setToNow(); - logSSC[next].set(t, s, subId, phoneId, state); - if (++next >= logSSC.length) { - next = 0; - } - } - - private void toStringLogSSC(String prompt) { - if (logSSC == null || logSSC.length == 0 || (next == 0 && logSSC[next] == null)) { - log(prompt + ": logSSC is empty"); - } else { - // There is at least one element - log(prompt + ": logSSC.length=" + logSSC.length + " next=" + next); - int i = next; - if (logSSC[i] == null) { - // logSSC is not full so back to the beginning - i = 0; - } - do { - log(logSSC[i].toString()); - if (++i >= logSSC.length) { - i = 0; - } - } while (i != next); - log(prompt + ": ----------------"); - } - } - boolean idMatch(int rSubId, int subId, int phoneId) { if(subId < 0) { diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index e560d325e6dd..738365d5b36f 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -427,14 +427,13 @@ public class AccountManagerService public boolean addAccountExplicitlyWithVisibility(Account account, String password, Bundle extras, Map packageToVisibility) { Bundle.setDefusable(extras, true); - - final int callingUid = Binder.getCallingUid(); + int callingUid = Binder.getCallingUid(); + int userId = UserHandle.getCallingUserId(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "addAccountExplicitly: " + account + ", caller's uid " + callingUid + ", pid " + Binder.getCallingPid()); } Preconditions.checkNotNull(account, "account cannot be null"); - int userId = UserHandle.getCallingUserId(); if (!isAccountManagedByCaller(account.type, callingUid, userId)) { String msg = String.format("uid %s cannot explicitly add accounts of type: %s", callingUid, account.type); @@ -461,9 +460,9 @@ public class AccountManagerService public Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName, String accountType) { int callingUid = Binder.getCallingUid(); + int userId = UserHandle.getCallingUserId(); boolean isSystemUid = UserHandle.isSameApp(callingUid, Process.SYSTEM_UID); - List<String> managedTypes = - getTypesForCaller(callingUid, UserHandle.getUserId(callingUid), isSystemUid); + List<String> managedTypes = getTypesForCaller(callingUid, userId, isSystemUid); if ((accountType != null && !managedTypes.contains(accountType)) || (accountType == null && !isSystemUid)) { @@ -478,8 +477,9 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { + UserAccounts accounts = getUserAccounts(userId); return getAccountsAndVisibilityForPackage(packageName, managedTypes, callingUid, - getUserAccounts(UserHandle.getUserId(callingUid))); + accounts); } finally { restoreCallingIdentity(identityToken); } @@ -490,12 +490,8 @@ public class AccountManagerService */ private Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName, List<String> accountTypes, Integer callingUid, UserAccounts accounts) { - int uid = 0; - try { - uid = mPackageManager.getPackageUidAsUser(packageName, - UserHandle.getUserId(callingUid)); - } catch (NameNotFoundException e) { - Log.d(TAG, "Package not found " + e.getMessage()); + if (!packageExistsForUser(packageName, accounts.userId)) { + Log.d(TAG, "Package not found " + packageName); return new LinkedHashMap<>(); } @@ -520,19 +516,26 @@ public class AccountManagerService public Map<String, Integer> getPackagesAndVisibilityForAccount(Account account) { Preconditions.checkNotNull(account, "account cannot be null"); int callingUid = Binder.getCallingUid(); - int userId = UserHandle.getUserId(callingUid); - UserAccounts accounts = getUserAccounts(userId); + int userId = UserHandle.getCallingUserId(); if (!isAccountManagedByCaller(account.type, callingUid, userId) && !isSystemUid(callingUid)) { String msg = String.format("uid %s cannot get secrets for account %s", callingUid, account); throw new SecurityException(msg); } - synchronized (accounts.dbLock) { - synchronized (accounts.cacheLock) { - return getPackagesAndVisibilityForAccountLocked(account, accounts); + + long identityToken = clearCallingIdentity(); + try { + UserAccounts accounts = getUserAccounts(userId); + synchronized (accounts.dbLock) { + synchronized (accounts.cacheLock) { + return getPackagesAndVisibilityForAccountLocked(account, accounts); + } } + } finally { + restoreCallingIdentity(identityToken); } + } /** @@ -560,8 +563,8 @@ public class AccountManagerService Preconditions.checkNotNull(account, "account cannot be null"); Preconditions.checkNotNull(packageName, "packageName cannot be null"); int callingUid = Binder.getCallingUid(); - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid)); - if (!isAccountManagedByCaller(account.type, callingUid, accounts.userId) + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId) && !isSystemUid(callingUid)) { String msg = String.format( "uid %s cannot get secrets for accounts of type: %s", @@ -569,7 +572,13 @@ public class AccountManagerService account.type); throw new SecurityException(msg); } - return resolveAccountVisibility(account, packageName, accounts); + long identityToken = clearCallingIdentity(); + try { + UserAccounts accounts = getUserAccounts(userId); + return resolveAccountVisibility(account, packageName, accounts); + } finally { + restoreCallingIdentity(identityToken); + } } /** @@ -708,8 +717,8 @@ public class AccountManagerService Preconditions.checkNotNull(account, "account cannot be null"); Preconditions.checkNotNull(packageName, "packageName cannot be null"); int callingUid = Binder.getCallingUid(); - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid)); - if (!isAccountManagedByCaller(account.type, callingUid, accounts.userId) + int userId = UserHandle.getCallingUserId(); + if (!isAccountManagedByCaller(account.type, callingUid, userId) && !isSystemUid(callingUid)) { String msg = String.format( "uid %s cannot get secrets for accounts of type: %s", @@ -717,8 +726,14 @@ public class AccountManagerService account.type); throw new SecurityException(msg); } - return setAccountVisibility(account, packageName, newVisibility, true /* notify */, + long identityToken = clearCallingIdentity(); + try { + UserAccounts accounts = getUserAccounts(userId); + return setAccountVisibility(account, packageName, newVisibility, true /* notify */, accounts); + } finally { + restoreCallingIdentity(identityToken); + } } /** @@ -805,8 +820,15 @@ public class AccountManagerService public void registerAccountListener(String[] accountTypes, String opPackageName) { int callingUid = Binder.getCallingUid(); mAppOpsManager.checkPackage(callingUid, opPackageName); - registerAccountListener(accountTypes, opPackageName, - getUserAccounts(UserHandle.getUserId(callingUid))); + + int userId = UserHandle.getCallingUserId(); + long identityToken = clearCallingIdentity(); + try { + UserAccounts accounts = getUserAccounts(userId); + registerAccountListener(accountTypes, opPackageName, accounts); + } finally { + restoreCallingIdentity(identityToken); + } } private void registerAccountListener(String[] accountTypes, String opPackageName, @@ -832,7 +854,18 @@ public class AccountManagerService public void unregisterAccountListener(String[] accountTypes, String opPackageName) { int callingUid = Binder.getCallingUid(); mAppOpsManager.checkPackage(callingUid, opPackageName); - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid)); + int userId = UserHandle.getCallingUserId(); + long identityToken = clearCallingIdentity(); + try { + UserAccounts accounts = getUserAccounts(userId); + unregisterAccountListener(accountTypes, opPackageName, accounts); + } finally { + restoreCallingIdentity(identityToken); + } + } + + private void unregisterAccountListener(String[] accountTypes, String opPackageName, + UserAccounts accounts) { synchronized (accounts.mReceiversForType) { if (accountTypes == null) { // null for any type @@ -903,7 +936,7 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { mPackageManager.getPackageUidAsUser(packageName, userId); - return true; // package exist + return true; } finally { restoreCallingIdentity(identityToken); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 4cbfb275fd32..7dd75df5252a 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -31,8 +31,10 @@ import java.util.Set; import android.app.ActivityThread; import android.app.AppOpsManager; +import android.app.ServiceStartArgs; import android.content.IIntentSender; import android.content.IntentSender; +import android.content.pm.ParceledListSlice; import android.os.Build; import android.os.Bundle; import android.os.DeadObjectException; @@ -369,7 +371,8 @@ public final class ActiveServices { } // This app knows it is in the new model where this operation is not // allowed, so tell it what has happened. - return new ComponentName("?", "app is in background"); + UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid); + return new ComponentName("?", "app is in background uid " + uidRec); } } finally { Binder.restoreCallingIdentity(token); @@ -1956,78 +1959,86 @@ public final class ActiveServices { return; } + ArrayList<ServiceStartArgs> args = new ArrayList<>(); + while (r.pendingStarts.size() > 0) { - Exception caughtException = null; - ServiceRecord.StartItem si = null; - try { - si = r.pendingStarts.remove(0); - if (DEBUG_SERVICE) { - Slog.v(TAG_SERVICE, "Sending arguments to: " - + r + " " + r.intent + " args=" + si.intent); - } - if (si.intent == null && N > 1) { - // If somehow we got a dummy null intent in the middle, - // then skip it. DO NOT skip a null intent when it is - // the only one in the list -- this is to support the - // onStartCommand(null) case. - continue; - } - si.deliveredTime = SystemClock.uptimeMillis(); - r.deliveredStarts.add(si); - si.deliveryCount++; - if (si.neededGrants != null) { - mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants, - si.getUriPermissionsLocked()); - } - mAm.grantEphemeralAccessLocked(r.userId, si.intent, - r.appInfo.uid, UserHandle.getAppId(si.callingId)); - bumpServiceExecutingLocked(r, execInFg, "start"); - if (!oomAdjusted) { - oomAdjusted = true; - mAm.updateOomAdjLocked(r.app); - } - if (r.fgRequired && !r.fgWaiting) { - if (!r.isForeground) { - if (DEBUG_BACKGROUND_CHECK) { - Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r); - } - scheduleServiceForegroundTransitionTimeoutLocked(r); - } else { - if (DEBUG_BACKGROUND_CHECK) { - Slog.i(TAG, "Service already foreground; no new timeout: " + r); - } - r.fgRequired = false; + ServiceRecord.StartItem si = r.pendingStarts.remove(0); + if (DEBUG_SERVICE) { + Slog.v(TAG_SERVICE, "Sending arguments to: " + + r + " " + r.intent + " args=" + si.intent); + } + if (si.intent == null && N > 1) { + // If somehow we got a dummy null intent in the middle, + // then skip it. DO NOT skip a null intent when it is + // the only one in the list -- this is to support the + // onStartCommand(null) case. + continue; + } + si.deliveredTime = SystemClock.uptimeMillis(); + r.deliveredStarts.add(si); + si.deliveryCount++; + if (si.neededGrants != null) { + mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants, + si.getUriPermissionsLocked()); + } + mAm.grantEphemeralAccessLocked(r.userId, si.intent, + r.appInfo.uid, UserHandle.getAppId(si.callingId)); + bumpServiceExecutingLocked(r, execInFg, "start"); + if (!oomAdjusted) { + oomAdjusted = true; + mAm.updateOomAdjLocked(r.app); + } + if (r.fgRequired && !r.fgWaiting) { + if (!r.isForeground) { + if (DEBUG_BACKGROUND_CHECK) { + Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r); } + scheduleServiceForegroundTransitionTimeoutLocked(r); + } else { + if (DEBUG_BACKGROUND_CHECK) { + Slog.i(TAG, "Service already foreground; no new timeout: " + r); + } + r.fgRequired = false; } - int flags = 0; - if (si.deliveryCount > 1) { - flags |= Service.START_FLAG_RETRY; - } - if (si.doneExecutingCount > 0) { - flags |= Service.START_FLAG_REDELIVERY; - } - r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent); - } catch (TransactionTooLargeException e) { - if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large: intent=" - + si.intent); - caughtException = e; - } catch (RemoteException e) { - // Remote process gone... we'll let the normal cleanup take care of this. - if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r); - caughtException = e; - } catch (Exception e) { - Slog.w(TAG, "Unexpected exception", e); - caughtException = e; } + int flags = 0; + if (si.deliveryCount > 1) { + flags |= Service.START_FLAG_RETRY; + } + if (si.doneExecutingCount > 0) { + flags |= Service.START_FLAG_REDELIVERY; + } + args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent)); + } - if (caughtException != null) { - // Keep nesting count correct - final boolean inDestroying = mDestroyingServices.contains(r); + ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args); + slice.setInlineCountLimit(4); + Exception caughtException = null; + try { + r.app.thread.scheduleServiceArgs(r, slice); + } catch (TransactionTooLargeException e) { + if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size() + + " args, first: " + args.get(0).args); + Slog.w(TAG, "Failed delivering service starts", e); + caughtException = e; + } catch (RemoteException e) { + // Remote process gone... we'll let the normal cleanup take care of this. + if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r); + Slog.w(TAG, "Failed delivering service starts", e); + caughtException = e; + } catch (Exception e) { + Slog.w(TAG, "Unexpected exception", e); + caughtException = e; + } + + if (caughtException != null) { + // Keep nesting count correct + final boolean inDestroying = mDestroyingServices.contains(r); + for (int i = 0; i < args.size(); i++) { serviceDoneExecutingLocked(r, inDestroying, inDestroying); - if (caughtException instanceof TransactionTooLargeException) { - throw (TransactionTooLargeException)caughtException; - } - break; + } + if (caughtException instanceof TransactionTooLargeException) { + throw (TransactionTooLargeException)caughtException; } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 19fc2b876c3e..ee2fdba71576 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -12143,6 +12143,24 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } + @Override + public void backgroundWhitelistUid(final int uid) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the OS may call backgroundWhitelistUid()"); + } + + if (DEBUG_BACKGROUND_CHECK) { + Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist"); + } + synchronized (this) { + final int N = mBackgroundUidWhitelist.length; + int[] newList = new int[N+1]; + System.arraycopy(mBackgroundUidWhitelist, 0, newList, 0, N); + newList[N] = uid; + mBackgroundUidWhitelist = newList; + } + } + final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, String abiOverride) { ProcessRecord app; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index b72cd73e85cf..3e3fee54bdd0 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -745,7 +745,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * Returns a {@link TaskRecord} for the input id if available. Null otherwise. + * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise. * @param id Id of the task we would like returned. * @param matchMode The mode to match the given task id in. * @param stackId The stack to restore the task to (default launch stack will be used if @@ -765,7 +765,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { ActivityStack stack = stacks.get(stackNdx); - TaskRecord task = stack.taskForIdLocked(id); + final TaskRecord task = stack.taskForIdLocked(id); if (task != null) { return task; } @@ -780,11 +780,17 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Otherwise, check the recent tasks and return if we find it there and we are not restoring // the task from recents if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents"); - TaskRecord task = mRecentTasks.taskForIdLocked(id); - if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) { - if (DEBUG_RECENTS && task == null) { + final TaskRecord task = mRecentTasks.taskForIdLocked(id); + + if (task == null) { + if (DEBUG_RECENTS) { Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents"); } + + return null; + } + + if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) { return task; } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index e5b2ecad2842..983c97510687 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -1021,32 +1021,24 @@ public final class BatteryStatsService extends IBatteryStats.Stub mHandler.post(new Runnable() { @Override public void run() { - Slog.d(TAG, "begin setBatteryStateLocked"); - try { - synchronized (mStats) { - final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE; - if (mStats.isOnBattery() == onBattery) { - // The battery state has not changed, so we don't need to sync external - // stats immediately. - mStats.setBatteryStateLocked(status, health, plugType, level, temp, - volt, - chargeUAh, chargeFullUAh); - return; - } + synchronized (mStats) { + final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE; + if (mStats.isOnBattery() == onBattery) { + // The battery state has not changed, so we don't need to sync external + // stats immediately. + mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt, + chargeUAh, chargeFullUAh); + return; } - } finally { - Slog.d(TAG, "end setBatteryStateLocked"); } // Sync external stats first as the battery has changed states. If we don't sync // immediately here, we may not collect the relevant data later. updateExternalStatsSync("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); - Slog.d(TAG, "begin setBatteryStateLocked"); synchronized (mStats) { mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt, chargeUAh, chargeFullUAh); } - Slog.d(TAG, "end setBatteryStateLocked"); } }); } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 728476aebf3e..3b5e5bcce7ae 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -670,7 +670,7 @@ final class UserController { } mInjector.systemServiceManagerCleanupUser(userId); synchronized (mLock) { - mInjector.stackSupervisorRemoveUserLocked(userId); + mInjector.getActivityStackSupervisor().removeUserLocked(userId); } // Remove the user if it is ephemeral. if (getUserInfo(userId).isEphemeral()) { @@ -823,8 +823,10 @@ final class UserController { return true; } - mInjector.stackSupervisorSetLockTaskModeLocked(null, - ActivityManager.LOCK_TASK_MODE_NONE, "startUser", false); + if (foreground) { + mInjector.getActivityStackSupervisor().setLockTaskModeLocked( + null, ActivityManager.LOCK_TASK_MODE_NONE, "startUser", false); + } final UserInfo userInfo = getUserInfo(userId); if (userInfo == null) { @@ -1202,11 +1204,12 @@ final class UserController { } void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) { - boolean homeInFront = mInjector.stackSupervisorSwitchUserLocked(newUserId, uss); + boolean homeInFront = + mInjector.getActivityStackSupervisor().switchUserLocked(newUserId, uss); if (homeInFront) { mInjector.startHomeActivityLocked(newUserId, "moveUserToForeground"); } else { - mInjector.stackSupervisorResumeFocusedStackTopActivityLocked(); + mInjector.getActivityStackSupervisor().resumeFocusedStackTopActivityLocked(); } EventLogTags.writeAmSwitchUser(newUserId); sendUserSwitchBroadcastsLocked(oldUserId, newUserId); @@ -1680,10 +1683,6 @@ final class UserController { mService.mSystemServiceManager.cleanupUser(userId); } - void stackSupervisorRemoveUserLocked(int userId) { - mService.mStackSupervisor.removeUserLocked(userId); - } - protected UserManagerService getUserManager() { if (mUserManager == null) { IBinder b = ServiceManager.getService(Context.USER_SERVICE); @@ -1743,24 +1742,10 @@ final class UserController { return mService.checkComponentPermission(permission, pid, uid, owningUid, exported); } - boolean stackSupervisorSwitchUserLocked(int userId, UserState uss) { - return mService.mStackSupervisor.switchUserLocked(userId, uss); - } - void startHomeActivityLocked(int userId, String reason) { mService.startHomeActivityLocked(userId, reason); } - void stackSupervisorResumeFocusedStackTopActivityLocked() { - mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); - } - - void stackSupervisorSetLockTaskModeLocked(TaskRecord task, int lockTaskModeState, - String reason, boolean andResume) { - mService.mStackSupervisor.setLockTaskModeLocked(task, lockTaskModeState, reason, - andResume); - } - void updateUserConfigurationLocked() { mService.updateUserConfigurationLocked(); } @@ -1778,5 +1763,9 @@ final class UserController { true /* above system */); d.show(); } + + ActivityStackSupervisor getActivityStackSupervisor() { + return mService.mStackSupervisor; + } } } diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 82a0ff690b62..51aa4f8a834a 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -103,6 +103,13 @@ public final class PlaybackActivityMonitor apc.init(); synchronized(mPlayerLock) { mPlayers.put(newPiid, apc); + if (mDuckedUids.contains(new Integer(apc.getClientUid()))) { + if (DEBUG) { Log.v(TAG, " > trackPlayer() piid=" + newPiid + " must be ducked"); } + mDuckedPlayers.add(new Integer(newPiid)); + // FIXME here the player needs to be put in a state that is the same as if it + // had been ducked as it starts. At the moment, this works already for linked + // players, as is the case in gapless playback. + } } return newPiid; } @@ -141,6 +148,13 @@ public final class PlaybackActivityMonitor Log.e(TAG, "Error handling event " + event); change = false; } + if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED + && mDuckedUids.contains(new Integer(apc.getClientUid()))) { + if (DEBUG) { Log.v(TAG, " > playerEvent() piid=" + piid + " must be ducked"); } + if (!mDuckedPlayers.contains(new Integer(piid))) { + mDuckedPlayers.add(new Integer(piid)); + } + } } if (change) { dispatchPlaybackChange(); @@ -273,13 +287,20 @@ public final class PlaybackActivityMonitor // PlayerFocusEnforcer implementation private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>(); private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>(); + // size of 2 for typical cases of double-ducking, not expected to grow beyond that, but can + private final ArrayList<Integer> mDuckedUids = new ArrayList<Integer>(2); @Override public boolean duckPlayers(FocusRequester winner, FocusRequester loser) { if (DEBUG) { Log.v(TAG, String.format("duckPlayers: uids winner=%d loser=%d", - winner.getClientUid(), loser.getClientUid())); } + winner.getClientUid(), loser.getClientUid())); + } synchronized (mPlayerLock) { + final Integer loserUid = new Integer(loser.getClientUid()); + if (!mDuckedUids.contains(loserUid)) { + mDuckedUids.add(loserUid); + } if (mPlayers.isEmpty()) { return true; } @@ -296,7 +317,7 @@ public final class PlaybackActivityMonitor && loser.hasSameUid(apc.getClientUid()) && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { - if (mDuckedPlayers.contains(piid)) { + if (mDuckedPlayers.contains(new Integer(piid))) { if (DEBUG) { Log.v(TAG, "player " + piid + " already ducked"); } } else if (apc.getAudioAttributes().getContentType() == AudioAttributes.CONTENT_TYPE_SPEECH) { @@ -313,7 +334,7 @@ public final class PlaybackActivityMonitor apc.getPlayerProxy().applyVolumeShaper( DUCK_VSHAPE, PLAY_CREATE_IF_NEEDED); - mDuckedPlayers.add(piid); + mDuckedPlayers.add(new Integer(piid)); } catch (Exception e) { Log.e(TAG, "Error ducking player " + piid, e); // something went wrong trying to duck, so let the app handle it @@ -332,25 +353,36 @@ public final class PlaybackActivityMonitor if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); } synchronized (mPlayerLock) { if (mDuckedPlayers.isEmpty()) { + mDuckedUids.remove(new Integer(winner.getClientUid())); return; } + final ArrayList<Integer> playersToRemove = + new ArrayList<Integer>(mDuckedPlayers.size()); for (int piid : mDuckedPlayers) { final AudioPlaybackConfiguration apc = mPlayers.get(piid); - if (apc != null - && winner.hasSameUid(apc.getClientUid())) { - try { - Log.v(TAG, "unducking player" + piid); - mDuckedPlayers.remove(new Integer(piid)); - apc.getPlayerProxy().applyVolumeShaper( - DUCK_ID, - VolumeShaper.Operation.REVERSE); - } catch (Exception e) { - Log.e(TAG, "Error unducking player " + piid, e); + if (apc != null) { + if (winner.hasSameUid(apc.getClientUid())) { + try { + Log.v(TAG, "unducking player " + piid); + apc.getPlayerProxy().applyVolumeShaper( + DUCK_ID, + VolumeShaper.Operation.REVERSE); + } catch (Exception e) { + Log.e(TAG, "Error unducking player " + piid, e); + } finally { + playersToRemove.add(piid); + } } } else { - Log.e(TAG, "Error unducking player " + piid + ", player not found"); + // this piid was in the list of ducked players, but wasn't found, discard it + Log.v(TAG, "Error unducking player " + piid + ", player not found"); + playersToRemove.add(piid); } } + for (int piid : playersToRemove) { + mDuckedPlayers.remove(new Integer(piid)); + } + mDuckedUids.remove(new Integer(winner.getClientUid())); } } @@ -383,7 +415,7 @@ public final class PlaybackActivityMonitor try { Log.v(TAG, "call: muting player" + piid); apc.getPlayerProxy().setVolume(0.0f); - mMutedPlayers.add(piid); + mMutedPlayers.add(new Integer(piid)); } catch (Exception e) { Log.e(TAG, "call: error muting player " + piid, e); } diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java index 0a6d8a4e2474..8ad1beace643 100644 --- a/services/core/java/com/android/server/job/JobPackageTracker.java +++ b/services/core/java/com/android/server/job/JobPackageTracker.java @@ -345,6 +345,7 @@ public final class JobPackageTracker { public void notePending(JobStatus job) { final long now = SystemClock.uptimeMillis(); + job.madePending = now; rebatchIfNeeded(now); mCurDataSet.incPending(job.getSourceUid(), job.getSourcePackageName(), now); } @@ -357,6 +358,7 @@ public final class JobPackageTracker { public void noteActive(JobStatus job) { final long now = SystemClock.uptimeMillis(); + job.madeActive = now; rebatchIfNeeded(now); if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { mCurDataSet.incActiveTop(job.getSourceUid(), job.getSourcePackageName(), now); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 7c231ff45434..cd3ba4c8b1f3 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -2035,27 +2035,35 @@ public final class JobSchedulerService extends com.android.server.SystemService pw.print(" Evaluated priority: "); pw.println(priority); } pw.print(" Tag: "); pw.println(job.getTag()); + pw.print(" Enq: "); + TimeUtils.formatDuration(now - job.madePending, pw); + pw.println(" ago"); } pw.println(); pw.println("Active jobs:"); for (int i=0; i<mActiveServices.size(); i++) { JobServiceContext jsc = mActiveServices.get(i); pw.print(" Slot #"); pw.print(i); pw.print(": "); - if (jsc.getRunningJob() == null) { + final JobStatus job = jsc.getRunningJob(); + if (job == null) { pw.println("inactive"); continue; } else { - pw.println(jsc.getRunningJob().toShortString()); + pw.println(job.toShortString()); pw.print(" Running for: "); TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw); pw.print(", timeout at: "); TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw); pw.println(); - jsc.getRunningJob().dump(pw, " ", false); + job.dump(pw, " ", false); int priority = evaluateJobPriorityLocked(jsc.getRunningJob()); if (priority != JobInfo.PRIORITY_DEFAULT) { pw.print(" Evaluated priority: "); pw.println(priority); } + pw.print(" Active at "); pw.println(job.madeActive); + pw.print(" Pending for "); + TimeUtils.formatDuration(job.madeActive - job.madePending, pw); + pw.println(); } } if (filterUid == -1) { diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java index f41e1876e721..0e04d2441bf8 100644 --- a/services/core/java/com/android/server/job/controllers/IdleController.java +++ b/services/core/java/com/android/server/job/controllers/IdleController.java @@ -147,7 +147,6 @@ public class IdleController extends StateController { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); - if (action.equals(Intent.ACTION_SCREEN_ON) || action.equals(Intent.ACTION_DREAMING_STOPPED)) { if (DEBUG) { @@ -183,6 +182,11 @@ public class IdleController extends StateController { } mIdle = true; reportNewIdleState(mIdle); + } else { + if (DEBUG) { + Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle=" + + mIdle + " screen=" + mScreenOn); + } } } } @@ -191,7 +195,7 @@ public class IdleController extends StateController { @Override public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { pw.print("Idle: "); - pw.println(mIdleTracker.isIdle() ? "true" : "false"); + pw.println(mIdleTracker.isIdle()); pw.print("Tracking "); pw.print(mTrackedTasks.size()); pw.println(":"); diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 47630d0d734b..d27d0e582cc5 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -129,6 +129,10 @@ public final class JobStatus { // Used by shell commands public int overrideState = 0; + // Metrics about queue latency + public long madePending; + public long madeActive; + /** * For use only by ContentObserverController: state it is maintaining about content URIs * being observed. diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 44c715ba706e..64ee1e976378 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1294,19 +1294,14 @@ public class NotificationManagerService extends SystemService { sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED); } - private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel, - boolean fromAssistant) { + private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel) { if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) { // cancel cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true, UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null); } - if (fromAssistant) { - mRankingHelper.updateNotificationChannelFromAssistant(pkg, uid, channel); - } else { - mRankingHelper.updateNotificationChannel(pkg, uid, channel); - } + mRankingHelper.updateNotificationChannel(pkg, uid, channel); synchronized (mNotificationLock) { final int N = mNotificationList.size(); @@ -1709,7 +1704,7 @@ public class NotificationManagerService extends SystemService { NotificationChannel channel) { enforceSystemOrSystemUI("Caller not system or systemui"); Preconditions.checkNotNull(channel); - updateNotificationChannelInt(pkg, uid, channel, false); + updateNotificationChannelInt(pkg, uid, channel); } @Override @@ -2646,47 +2641,6 @@ public class NotificationManagerService extends SystemService { Binder.restoreCallingIdentity(identity); } } - - @Override - public void createNotificationChannelFromAssistant(INotificationListener token, String pkg, - NotificationChannel channel) throws RemoteException { - ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token); - int uid = mPackageManager.getPackageUid(pkg, 0, info.userid); - mRankingHelper.createNotificationChannel(pkg, uid, channel, false /* fromTargetApp */); - savePolicyFile(); - } - - @Override - public void deleteNotificationChannelFromAssistant(INotificationListener token, String pkg, - String channelId) throws RemoteException { - ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token); - if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { - throw new IllegalArgumentException("Cannot delete default channel"); - } - - int uid = mPackageManager.getPackageUid(pkg, 0, info.userid); - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true, - info.userid, REASON_CHANNEL_BANNED, null); - mRankingHelper.deleteNotificationChannel(pkg, uid, channelId); - savePolicyFile(); - } - - @Override - public void updateNotificationChannelFromAssistant(INotificationListener token, String pkg, - NotificationChannel channel) throws RemoteException { - ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token); - Preconditions.checkNotNull(channel); - int uid = mPackageManager.getPackageUid(pkg, 0, info.userid); - updateNotificationChannelInt(pkg, uid, channel, true); - } - - @Override - public ParceledListSlice<NotificationChannel> getNotificationChannelsFromAssistant( - INotificationListener token, String pkg) throws RemoteException { - ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token); - int uid = mPackageManager.getPackageUid(pkg, 0, info.userid); - return mRankingHelper.getNotificationChannels(pkg, uid, false /* includeDeleted */); - } }; private void applyAdjustment(NotificationRecord n, Adjustment adjustment) { @@ -2695,17 +2649,10 @@ public class NotificationManagerService extends SystemService { } if (adjustment.getSignals() != null) { Bundle.setDefusable(adjustment.getSignals(), true); - final String overrideChannelId = - adjustment.getSignals().getString(Adjustment.KEY_CHANNEL_ID, null); final ArrayList<String> people = adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE); final ArrayList<SnoozeCriterion> snoozeCriterionList = adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA); - if (!TextUtils.isEmpty(overrideChannelId)) { - n.updateNotificationChannel(mRankingHelper.getNotificationChannel( - n.sbn.getPackageName(), n.sbn.getUid(), overrideChannelId, - false /* includeDeleted */)); - } n.setPeopleOverride(people); n.setSnoozeCriteria(snoozeCriterionList); } diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index e13df192acb6..4d19b52c327a 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -37,7 +37,6 @@ public interface RankingConfig { void createNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromTargetApp); void updateNotificationChannel(String pkg, int uid, NotificationChannel channel); - void updateNotificationChannelFromAssistant(String pkg, int uid, NotificationChannel channel); NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted); void deleteNotificationChannel(String pkg, int uid, String channelId); void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId); diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 65aaee0f8ce8..b63b05f1f59b 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -609,55 +609,6 @@ public class RankingHelper implements RankingConfig { } @Override - public void updateNotificationChannelFromAssistant(String pkg, int uid, - NotificationChannel updatedChannel) { - Record r = getOrCreateRecord(pkg, uid); - if (r == null) { - throw new IllegalArgumentException("Invalid package"); - } - NotificationChannel channel = r.channels.get(updatedChannel.getId()); - if (channel == null || channel.isDeleted()) { - throw new IllegalArgumentException("Channel does not exist"); - } - - if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0) { - channel.setImportance(updatedChannel.getImportance()); - } - if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_LIGHTS) == 0) { - channel.enableLights(updatedChannel.shouldShowLights()); - channel.setLightColor(updatedChannel.getLightColor()); - } - if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_PRIORITY) == 0) { - channel.setBypassDnd(updatedChannel.canBypassDnd()); - } - if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_SOUND) == 0) { - channel.setSound(updatedChannel.getSound(), updatedChannel.getAudioAttributes()); - } - if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_VIBRATION) == 0) { - channel.enableVibration(updatedChannel.shouldVibrate()); - channel.setVibrationPattern(updatedChannel.getVibrationPattern()); - } - if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_VISIBILITY) == 0) { - if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) { - channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE); - } else { - channel.setLockscreenVisibility(updatedChannel.getLockscreenVisibility()); - } - } - if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_SHOW_BADGE) == 0) { - channel.setShowBadge(updatedChannel.canShowBadge()); - } - if (updatedChannel.isDeleted()) { - channel.setDeleted(true); - } - // Assistant cannot change the group - - MetricsLogger.action(getChannelLog(channel, pkg)); - r.channels.put(channel.getId(), channel); - updateConfig(); - } - - @Override public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted) { Preconditions.checkNotNull(pkg); 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..c4cc4fbf3cf0 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; @@ -63,9 +64,10 @@ final class EphemeralResolverConnection { private volatile boolean mBindRequested; private IInstantAppResolver mRemoteInstance; - public EphemeralResolverConnection(Context context, ComponentName componentName) { + public EphemeralResolverConnection( + Context context, ComponentName componentName, String action) { mContext = context; - mIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE).setComponent(componentName); + mIntent = new Intent(action).setComponent(componentName); } public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[], @@ -171,6 +173,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 +194,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/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java index 59f8a2d0da20..6f593b08e258 100644 --- a/services/core/java/com/android/server/pm/InstantAppResolver.java +++ b/services/core/java/com/android/server/pm/InstantAppResolver.java @@ -86,22 +86,18 @@ public abstract class InstantAppResolver { final List<InstantAppResolveInfo> instantAppResolveInfoList = connection.getInstantAppResolveInfoList(shaPrefix, token); - final AuxiliaryResolveInfo resolveInfo; if (instantAppResolveInfoList == null || instantAppResolveInfoList.size() == 0) { // No hash prefix match; there are no instant apps for this domain. if (DEBUG_EPHEMERAL) { Log.d(TAG, "No results returned"); } - resolveInfo = null; - } else { - resolveInfo = InstantAppResolver.filterInstantAppIntent(instantAppResolveInfoList, - intent, requestObj.resolvedType, requestObj.userId, - intent.getPackage(), digest, token); + return null; } - + final AuxiliaryResolveInfo resolveInfo = InstantAppResolver.filterInstantAppIntent( + instantAppResolveInfoList, intent, requestObj.resolvedType, requestObj.userId, + intent.getPackage(), digest, token); logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token, - resolveInfo != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE); - + RESOLUTION_SUCCESS); return resolveInfo; } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index d9ea7284616d..781be34828ad 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -22,6 +22,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageParser; import android.os.Environment; +import android.os.FileUtils; import android.os.PowerManager; import android.os.UserHandle; import android.os.WorkSource; @@ -151,6 +152,7 @@ public class PackageDexOptimizer { // TODO(calin,jeffhao): shared library paths should be adjusted to include previous code // paths (b/34169257). final String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries); + // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags. final int dexoptFlags = getDexFlags(pkg, compilerFilter); int result = DEX_OPT_SKIPPED; @@ -254,17 +256,20 @@ public class PackageDexOptimizer { @GuardedBy("mInstallLock") private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, Set<String> isas, String compilerFilter, boolean isUsedByOtherApps) { + compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps); + // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags. int dexoptFlags = getDexFlags(info, compilerFilter) | DEXOPT_SECONDARY_DEX; // Check the app storage and add the appropriate flags. - if (info.dataDir.equals(info.deviceProtectedDataDir)) { + if (info.deviceProtectedDataDir != null && + FileUtils.contains(info.deviceProtectedDataDir, path)) { dexoptFlags |= DEXOPT_STORAGE_DE; - } else if (info.dataDir.equals(info.credentialProtectedDataDir)) { + } else if (info.credentialProtectedDataDir != null && + FileUtils.contains(info.credentialProtectedDataDir, path)) { dexoptFlags |= DEXOPT_STORAGE_CE; } else { Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName); return DEX_OPT_FAILED; } - compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps); Log.d(TAG, "Running dexopt on: " + path + " pkg=" + info.packageName + " isa=" + isas + " dexoptFlags=" + printDexoptFlags(dexoptFlags) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 965e28ec234c..01847aeb919b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -608,6 +608,10 @@ public class PackageManagerService extends IPackageManager.Stub { final boolean mIsPreNUpgrade; final boolean mIsPreNMR1Upgrade; + // Have we told the Activity Manager to whitelist the default container service by uid yet? + @GuardedBy("mPackages") + boolean mDefaultContainerWhitelisted = false; + @GuardedBy("mPackages") private boolean mDexOptDialogShown; @@ -842,11 +846,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(); @@ -2718,6 +2722,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; } @@ -2801,18 +2814,22 @@ public class PackageManagerService extends IPackageManager.Stub { } mInstallerService = new PackageInstallerService(context, this); - final ComponentName ephemeralResolverComponent = getEphemeralResolverLPr(); - if (ephemeralResolverComponent != null) { + final Pair<ComponentName, String> instantAppResolverComponent = + getInstantAppResolverLPr(); + if (instantAppResolverComponent != null) { if (DEBUG_EPHEMERAL) { - Slog.d(TAG, "Set ephemeral resolver: " + ephemeralResolverComponent); + Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent); } - mInstantAppResolverConnection = - new EphemeralResolverConnection(mContext, ephemeralResolverComponent); + mInstantAppResolverConnection = new EphemeralResolverConnection( + mContext, instantAppResolverComponent.first, + instantAppResolverComponent.second); + mInstantAppResolverSettingsComponent = + getInstantAppResolverSettingsLPr(instantAppResolverComponent.first); } 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 @@ -2854,7 +2871,7 @@ public class PackageManagerService extends IPackageManager.Stub { private void updateInstantAppInstallerLocked() { final ComponentName oldInstantAppInstallerComponent = mInstantAppInstallerComponent; - final ActivityInfo newInstantAppInstaller = getEphemeralInstallerLPr(); + final ActivityInfo newInstantAppInstaller = getInstantAppInstallerLPr(); ComponentName newInstantAppInstallerComponent = newInstantAppInstaller == null ? null : newInstantAppInstaller.getComponentName(); @@ -3031,7 +3048,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private @Nullable ComponentName getEphemeralResolverLPr() { + private @Nullable Pair<ComponentName, String> getInstantAppResolverLPr() { final String[] packageArray = mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage); if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) { @@ -3046,10 +3063,20 @@ 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 List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null, + String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE; + final Intent resolverIntent = new Intent(actionName); + List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null, resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); - + // temporarily look for the old action + if (resolvers.size() == 0) { + if (DEBUG_EPHEMERAL) { + Slog.d(TAG, "Ephemeral resolver not found with new action; try old one"); + } + actionName = Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE; + resolverIntent.setAction(actionName); + resolvers = queryIntentServicesInternal(resolverIntent, null, + resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); + } final int N = resolvers.size(); if (N == 0) { if (DEBUG_EPHEMERAL) { @@ -3079,7 +3106,7 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.v(TAG, "Ephemeral resolver found;" + " pkg: " + packageName + ", info:" + info); } - return new ComponentName(packageName, info.serviceInfo.name); + return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName); } if (DEBUG_EPHEMERAL) { Slog.v(TAG, "Ephemeral resolver NOT found"); @@ -3087,8 +3114,8 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } - private @Nullable ActivityInfo getEphemeralInstallerLPr() { - final Intent intent = new Intent(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE); + private @Nullable ActivityInfo getInstantAppInstallerLPr() { + 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); @@ -3096,8 +3123,17 @@ public class PackageManagerService extends IPackageManager.Stub { MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); - final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, + List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, resolveFlags, UserHandle.USER_SYSTEM); + // temporarily look for the old action + if (matches.isEmpty()) { + if (DEBUG_EPHEMERAL) { + Slog.d(TAG, "Ephemeral installer not found with new action; try old one"); + } + intent.setAction(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE); + matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, + resolveFlags, UserHandle.USER_SYSTEM); + } Iterator<ResolveInfo> iter = matches.iterator(); while (iter.hasNext()) { final ResolveInfo rInfo = iter.next(); @@ -3120,35 +3156,27 @@ 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; - } + private @Nullable ComponentName getInstantAppResolverSettingsLPr( + @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; + List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags, + UserHandle.USER_SYSTEM); + // temporarily look for the old action + if (matches.isEmpty()) { + if (DEBUG_EPHEMERAL) { + Slog.d(TAG, "Ephemeral resolver settings not found with new action; try old one"); } - iter.remove(); + intent.setAction(Intent.ACTION_EPHEMERAL_RESOLVER_SETTINGS); + matches = queryIntentActivitiesInternal(intent, null, resolveFlags, + UserHandle.USER_SYSTEM); } - if (matches.size() == 0) { + 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) { @@ -5730,20 +5758,23 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { final int count = (resolvedActivities == null ? 0 : resolvedActivities.size()); for (int n = 0; n < count; n++) { - ResolveInfo info = resolvedActivities.get(n); - String packageName = info.activityInfo.packageName; - PackageSetting ps = mSettings.mPackages.get(packageName); + final ResolveInfo info = resolvedActivities.get(n); + final String packageName = info.activityInfo.packageName; + final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { - // Try to get the status from User settings first - long packedStatus = getDomainVerificationStatusLPr(ps, userId); - int status = (int) (packedStatus >> 32); - if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS + // only check domain verification status if the app is not a browser + if (!info.handleAllWebDataURI) { + // Try to get the status from User settings first + final long packedStatus = getDomainVerificationStatusLPr(ps, userId); + final int status = (int) (packedStatus >> 32); + if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { - if (DEBUG_EPHEMERAL) { - Slog.v(TAG, "DENY ephemeral apps;" - + " pkg: " + packageName + ", status: " + status); + if (DEBUG_EPHEMERAL) { + Slog.v(TAG, "DENY instant app;" + + " pkg: " + packageName + ", status: " + status); + } + return false; } - return false; } if (ps.getInstantApp(userId)) { if (DEBUG_EPHEMERAL) { @@ -13037,7 +13068,18 @@ public class PackageManagerService extends IPackageManager.Stub { intent.setComponent(DEFAULT_CONTAINER_COMPONENT); IActivityManager am = ActivityManager.getService(); if (am != null) { + int dcsUid = -1; + synchronized (mPackages) { + if (!mDefaultContainerWhitelisted) { + mDefaultContainerWhitelisted = true; + PackageSetting ps = mSettings.mPackages.get(DEFAULT_CONTAINER_PACKAGE); + dcsUid = UserHandle.getUid(UserHandle.USER_SYSTEM, ps.appId); + } + } try { + if (dcsUid > 0) { + am.backgroundWhitelistUid(dcsUid); + } am.startService(null, intent, null, -1, null, false, mContext.getOpPackageName(), UserHandle.USER_SYSTEM); } catch (RemoteException e) { @@ -16972,6 +17014,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. @@ -17020,6 +17071,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) { @@ -17646,6 +17698,7 @@ public class PackageManagerService extends IPackageManager.Stub { int removedAppId = -1; int[] origUsers; int[] removedUsers = null; + int[] broadcastUsers = null; SparseArray<Integer> installReasons; boolean isRemovedPackageSystemUpdate = false; boolean isUpdate; @@ -17719,16 +17772,16 @@ public class PackageManagerService extends IPackageManager.Stub { extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers); if (removedPackage != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, - extras, 0, null, null, removedUsers); + extras, 0, null, null, broadcastUsers); if (dataRemoved && !isRemovedPackageSystemUpdate) { sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, - null, null, removedUsers); + null, null, broadcastUsers); } } if (removedAppId >= 0) { sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, 0, null, null, - removedUsers); + broadcastUsers); } } } @@ -17757,6 +17810,20 @@ public class PackageManagerService extends IPackageManager.Stub { outInfo.removedUsers = deletedPs != null ? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true) : null; + if (outInfo.removedUsers == null) { + outInfo.broadcastUsers = null; + } else { + outInfo.broadcastUsers = EMPTY_INT_ARRAY; + int[] allUsers = outInfo.removedUsers; + for (int i = allUsers.length - 1; i >= 0; --i) { + final int userId = allUsers[i]; + if (deletedPs.getInstantApp(userId)) { + continue; + } + outInfo.broadcastUsers = + ArrayUtils.appendInt(outInfo.broadcastUsers, userId); + } + } } } @@ -20865,13 +20932,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. @@ -20942,6 +21002,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/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index c693a475ed59..3d7cedce522a 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -19,7 +19,7 @@ package com.android.server.pm.dex; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; -import android.content.pm.PackageParser; +import android.os.FileUtils; import android.os.RemoteException; import android.os.storage.StorageManager; import android.os.UserHandle; @@ -93,7 +93,7 @@ public class DexManager { * Note that this method is invoked when apps load dex files and it should * return as fast as possible. * - * @param loadingPackage the package performing the load + * @param loadingAppInfo the package performing the load * @param dexPaths the list of dex files being loaded * @param loaderIsa the ISA of the app loading the dex files * @param loaderUserId the user id which runs the code loading the dex files @@ -191,8 +191,7 @@ public class DexManager { throw new IllegalArgumentException( "notifyPackageInstalled called with USER_ALL"); } - cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir, - pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId); + cachePackageInfo(pi, userId); } /** @@ -231,13 +230,32 @@ public class DexManager { } } - public void cachePackageCodeLocation(String packageName, String baseCodePath, - String[] splitCodePaths, String dataDir, int userId) { + /** + * Caches the code location from the given package info. + */ + private void cachePackageInfo(PackageInfo pi, int userId) { + ApplicationInfo ai = pi.applicationInfo; + String[] dataDirs = new String[] {ai.dataDir, ai.deviceProtectedDataDir, + ai.credentialProtectedDataDir}; + cachePackageCodeLocation(pi.packageName, ai.sourceDir, ai.splitSourceDirs, + dataDirs, userId); + } + + private void cachePackageCodeLocation(String packageName, String baseCodePath, + String[] splitCodePaths, String[] dataDirs, int userId) { PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName, new PackageCodeLocations(packageName, baseCodePath, splitCodePaths)); pcl.updateCodeLocation(baseCodePath, splitCodePaths); - if (dataDir != null) { - pcl.mergeAppDataDirs(dataDir, userId); + if (dataDirs != null) { + for (String dataDir : dataDirs) { + // The set of data dirs includes deviceProtectedDataDir and + // credentialProtectedDataDir which might be null for shared + // libraries. Currently we don't track these but be lenient + // and check in case we ever decide to store their usage data. + if (dataDir != null) { + pcl.mergeAppDataDirs(dataDir, userId); + } + } } } @@ -250,8 +268,7 @@ public class DexManager { int userId = entry.getKey(); for (PackageInfo pi : packageInfoList) { // Cache the code locations. - cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir, - pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId); + cachePackageInfo(pi, userId); // Cache a map from package name to the set of user ids who installed the package. // We will use it to sync the data and remove obsolete entries from @@ -329,6 +346,7 @@ public class DexManager { mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId()); continue; } + int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath, dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps()); success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED); @@ -350,7 +368,7 @@ public class DexManager { // Nothing to reconcile. return; } - Set<String> dexFilesToRemove = new HashSet<>(); + boolean updated = false; for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) { String dexPath = entry.getKey(); @@ -378,14 +396,16 @@ public class DexManager { } ApplicationInfo info = pkg.applicationInfo; int flags = 0; - if (info.dataDir.equals(info.deviceProtectedDataDir)) { + if (info.deviceProtectedDataDir != null && + FileUtils.contains(info.deviceProtectedDataDir, dexPath)) { flags |= StorageManager.FLAG_STORAGE_DE; - } else if (info.dataDir.equals(info.credentialProtectedDataDir)) { + } else if (info.credentialProtectedDataDir!= null && + FileUtils.contains(info.credentialProtectedDataDir, dexPath)) { flags |= StorageManager.FLAG_STORAGE_CE; } else { - Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName); - updated = mPackageDexUsage.removeUserPackage( - packageName, dexUseInfo.getOwnerUserId()) || updated; + Slog.e(TAG, "Could not infer CE/DE storage for path " + dexPath); + updated = mPackageDexUsage.removeDexFile( + packageName, dexPath, dexUseInfo.getOwnerUserId()) || updated; continue; } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 218d21826d93..32871bb0ba2c 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -763,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); } @@ -780,11 +782,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub { try { mHandler.post(() -> { // ShutdownThread displays UI, so give it a UI context. - Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext(); if (safeMode) { - ShutdownThread.rebootSafeMode(uiContext, false); + ShutdownThread.rebootSafeMode(getUiContext(), false); } else { - ShutdownThread.reboot(uiContext, PowerManager.SHUTDOWN_USER_REQUESTED, false); + ShutdownThread.reboot(getUiContext(), + PowerManager.SHUTDOWN_USER_REQUESTED, false); } }); } finally { @@ -1018,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/vr/CompatibilityDisplay.java b/services/core/java/com/android/server/vr/CompatibilityDisplay.java index ae1d50ff1f71..772fc2653e77 100644 --- a/services/core/java/com/android/server/vr/CompatibilityDisplay.java +++ b/services/core/java/com/android/server/vr/CompatibilityDisplay.java @@ -95,20 +95,15 @@ class CompatibilityDisplay { * Creates and Destroys the virtual display depending on the current state of VrMode. */ private void updateVirtualDisplay() { - boolean createVirtualDisplay = "true".equals(SystemProperties.get("vr_virtualdisplay")); if (DEBUG) { - Log.i(TAG, "isVrMode: " + mIsVrModeEnabled + ", createVD: " + createVirtualDisplay + - ", override: " + mIsVrModeOverrideEnabled); + Log.i(TAG, "isVrMode: " + mIsVrModeEnabled + ", override: " + mIsVrModeOverrideEnabled); } - if (mIsVrModeEnabled || (createVirtualDisplay && mIsVrModeOverrideEnabled)) { + if (mIsVrModeEnabled || mIsVrModeOverrideEnabled) { // TODO: Consider not creating the display until ActivityManager needs one on // which to display a 2D application. - // TODO: STOPSHIP Remove createVirtualDisplay conditional before launching. - if (createVirtualDisplay) { - startVirtualDisplay(); - startImageReader(); - } + startVirtualDisplay(); + startImageReader(); } else { // Stop virtual display to test exit condition stopVirtualDisplay(); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 1decf4e19e9f..a8664a5727ab 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -505,6 +505,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree getController().removeStartingWindow(); } + // If this window was animating, then we need to ensure that the app transition notifies + // that animations have completed in WMS.handleAnimatingStoppedAndTransitionLocked(), so + // add to that list now + if (mAppAnimator.animating) { + mService.mNoAnimationNotifyOnTransitionFinished.add(token); + } + final TaskStack stack = getTask().mStack; if (delayed && !isEmpty()) { // set the token aside because it has an active animation to be finished diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index 9f0ed2100462..7b8057cafbb4 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -61,17 +61,21 @@ public class BoundsAnimationController { extends WindowManagerInternal.AppTransitionListener implements Runnable { public void onAppTransitionCancelledLocked() { + if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:" + + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition); animationFinished(); } public void onAppTransitionFinishedLocked(IBinder token) { + if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:" + + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition); animationFinished(); } private void animationFinished() { if (mFinishAnimationAfterTransition) { mHandler.removeCallbacks(this); - // This might end up calling into activity manager which will be bad since we have the - // window manager lock held at this point. Post a message to take care of the processing - // so we don't deadlock. + // This might end up calling into activity manager which will be bad since we have + // the window manager lock held at this point. Post a message to take care of the + // processing so we don't deadlock. mHandler.post(this); } } @@ -195,6 +199,7 @@ public class BoundsAnimationController { if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) { // Whoops, the target doesn't feel like animating anymore. Let's immediately finish // any further animation. + if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled"); animation.cancel(); } } @@ -203,7 +208,9 @@ public class BoundsAnimationController { public void onAnimationEnd(Animator animation) { if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget + " mMoveToFullScreen=" + mMoveToFullScreen - + " mSkipAnimationEnd=" + mSkipAnimationEnd); + + " mSkipAnimationEnd=" + mSkipAnimationEnd + + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition + + " mAppTransitionIsRunning=" + mAppTransition.isRunning()); // There could be another animation running. For example in the // move to fullscreen case, recents will also be closing while the diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 3cb96a1145fc..ee2d5de710c1 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -441,6 +441,8 @@ class WindowSurfacePlacer { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); appAnimator.clearThumbnail(); appAnimator.setNullAnimation(); + // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not + // animating? wtoken.setVisibility(animLp, false, transit, false, voiceInteraction); wtoken.updateReportedVisibilityLocked(); // Force the allDrawn flag, because we want to start diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index a5eac46281f3..63af2da93a86 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -668,6 +668,7 @@ public final class SystemServer { VibratorService vibrator = null; IStorageManager storageManager = null; NetworkManagementService networkManagement = null; + IpSecService ipSecService = null; NetworkStatsService networkStats = null; NetworkPolicyManagerService networkPolicy = null; ConnectivityService connectivity = null; @@ -1015,6 +1016,15 @@ public final class SystemServer { reportWtf("starting NetworkManagement Service", e); } traceEnd(); + + traceBeginAndSlog("StartIpSecService"); + try { + ipSecService = IpSecService.create(context); + ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService); + } catch (Throwable e) { + reportWtf("starting IpSec Service", e); + } + traceEnd(); } if (!disableNonCoreServices && !disableTextServices) { @@ -1628,6 +1638,7 @@ public final class SystemServer { final TelephonyRegistry telephonyRegistryF = telephonyRegistry; final MediaRouterService mediaRouterF = mediaRouter; final MmsServiceBroker mmsServiceF = mmsService; + final IpSecService ipSecServiceF = ipSecService; // We now tell the activity manager it is okay to run third party // code. It will call back into us once it has gotten to the state @@ -1691,6 +1702,13 @@ public final class SystemServer { .networkScoreAndNetworkManagementServiceReady(); } traceEnd(); + traceBeginAndSlog("MakeIpSecServiceReady"); + try { + if (ipSecServiceF != null) ipSecServiceF.systemReady(); + } catch (Throwable e) { + reportWtf("making IpSec Service ready", e); + } + traceEnd(); traceBeginAndSlog("MakeNetworkStatsServiceReady"); try { if (networkStatsF != null) networkStatsF.systemReady(); diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index 590bce186430..61a929404131 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -23,7 +23,6 @@ import android.content.Context; import android.net.apf.ApfCapabilities; import android.net.apf.ApfFilter; import android.net.DhcpResults; -import android.net.INetd; import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; @@ -35,12 +34,10 @@ import android.net.dhcp.DhcpClient; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpManagerEvent; import android.net.util.MultinetworkPolicyTracker; -import android.net.util.NetdService; import android.os.INetworkManagementService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.ServiceSpecificException; import android.os.SystemClock; import android.text.TextUtils; import android.util.LocalLog; @@ -1030,16 +1027,14 @@ public class IpManager extends StateMachine { private boolean startIPv6() { // Set privacy extensions. - final String PREFER_TEMPADDRS = "2"; try { - NetdService.run((INetd netd) -> { - netd.setProcSysNet( - INetd.IPV6, INetd.CONF, mInterfaceName, "use_tempaddr", - PREFER_TEMPADDRS); - }); + mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); mNwService.enableIpv6(mInterfaceName); - } catch (IllegalStateException|RemoteException|ServiceSpecificException e) { - logError("Unable to change interface settings: %s", e); + } catch (RemoteException re) { + logError("Unable to change interface settings: %s", re); + return false; + } catch (IllegalStateException ie) { + logError("Unable to change interface settings: %s", ie); return false; } diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java index 40af2f89c93a..ad593be199b7 100644 --- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java @@ -497,149 +497,6 @@ public class RankingHelperTest { new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true); } - @Test - public void testUpdate_userLockedImportance() throws Exception { - // all fields locked by user - final NotificationChannel channel = - new NotificationChannel("id2", "name2", IMPORTANCE_LOW); - channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); - - mHelper.createNotificationChannel(PKG, UID, channel, false); - - // same id, try to update - final NotificationChannel channel2 = - new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); - - mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2); - - // no fields should be changed - assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false)); - } - - @Test - public void testUpdate_userLockedVisibility() throws Exception { - // all fields locked by user - final NotificationChannel channel = - new NotificationChannel("id2", "name2", IMPORTANCE_LOW); - channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); - channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY); - - mHelper.createNotificationChannel(PKG, UID, channel, false); - - // same id, try to update - final NotificationChannel channel2 = - new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); - channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); - - mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2); - - // no fields should be changed - assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false)); - } - - @Test - public void testUpdate_userLockedVibration() throws Exception { - // all fields locked by user - final NotificationChannel channel = - new NotificationChannel("id2", "name2", IMPORTANCE_LOW); - channel.enableLights(false); - channel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION); - - mHelper.createNotificationChannel(PKG, UID, channel, false); - - // same id, try to update - final NotificationChannel channel2 = - new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); - channel2.enableVibration(true); - channel2.setVibrationPattern(new long[]{100}); - - mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2); - - // no fields should be changed - assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false)); - } - - @Test - public void testUpdate_userLockedLights() throws Exception { - // all fields locked by user - final NotificationChannel channel = - new NotificationChannel("id2", "name2", IMPORTANCE_LOW); - channel.enableLights(false); - channel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS); - - mHelper.createNotificationChannel(PKG, UID, channel, false); - - // same id, try to update - final NotificationChannel channel2 = - new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); - channel2.enableLights(true); - - mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2); - - // no fields should be changed - assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false)); - } - - @Test - public void testUpdate_userLockedPriority() throws Exception { - // all fields locked by user - final NotificationChannel channel = - new NotificationChannel("id2", "name2", IMPORTANCE_LOW); - channel.setBypassDnd(true); - channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); - - mHelper.createNotificationChannel(PKG, UID, channel, false); - - // same id, try to update all fields - final NotificationChannel channel2 = - new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); - channel2.setBypassDnd(false); - - mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2); - - // no fields should be changed - assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false)); - } - - @Test - public void testUpdate_userLockedRingtone() throws Exception { - // all fields locked by user - final NotificationChannel channel = - new NotificationChannel("id2", "name2", IMPORTANCE_LOW); - channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); - channel.lockFields(NotificationChannel.USER_LOCKED_SOUND); - - mHelper.createNotificationChannel(PKG, UID, channel, false); - - // same id, try to update all fields - final NotificationChannel channel2 = - new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); - channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes); - - mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2); - - // no fields should be changed - assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false)); - } - - @Test - public void testUpdate_userLockedBadge() throws Exception { - final NotificationChannel channel = - new NotificationChannel("id2", "name2", IMPORTANCE_LOW); - channel.setShowBadge(true); - channel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE); - - mHelper.createNotificationChannel(PKG, UID, channel, false); - - final NotificationChannel channel2 = - new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); - channel2.setShowBadge(false); - - mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2); - - // no fields should be changed - assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false)); - } @Test public void testUpdate() throws Exception { @@ -816,30 +673,6 @@ public class RankingHelperTest { } @Test - public void testUpdateDeletedChannels() throws Exception { - NotificationChannel channel = - new NotificationChannel("id2", "name2", IMPORTANCE_LOW); - mHelper.createNotificationChannel(PKG, UID, channel, true); - - mHelper.deleteNotificationChannel(PKG, UID, channel.getId()); - - channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); - try { - mHelper.updateNotificationChannel(PKG, UID, channel); - fail("Updated deleted channel"); - } catch (IllegalArgumentException e) { - // :) - } - - try { - mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel); - fail("Updated deleted channel"); - } catch (IllegalArgumentException e) { - // :) - } - } - - @Test public void testCreateDeletedChannel() throws Exception { long[] vibration = new long[]{100, 67, 145, 156}; NotificationChannel channel = diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 6c7f146ed680..205c8dea18b0 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -52,20 +52,6 @@ <application> <uses-library android:name="android.test.runner" /> - <service android:name="com.android.server.AccessibilityManagerServiceTest$MyFirstMockAccessibilityService" - android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> - <intent-filter> - <action android:name="android.accessibilityservice.AccessibilityService"/> - </intent-filter> - </service> - - <service android:name="com.android.server.AccessibilityManagerServiceTest$MySecondMockAccessibilityService" - android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> - <intent-filter> - <action android:name="android.accessibilityservice.AccessibilityService"/> - </intent-filter> - </service> - <service android:name="com.android.server.accounts.TestAccountType1AuthenticatorService" android:exported="false"> <intent-filter> diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java deleted file mode 100644 index 340c62405814..000000000000 --- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java +++ /dev/null @@ -1,762 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.accessibilityservice.AccessibilityService; -import android.accessibilityservice.AccessibilityServiceInfo; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.ServiceInfo; -import android.os.IBinder; -import android.os.Message; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.os.UserHandle; -import android.provider.Settings; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.IAccessibilityManager; -import android.view.accessibility.IAccessibilityManagerClient; - -import com.android.internal.util.IntPair; - -/** - * This test exercises the - * {@link com.android.server.accessibility.AccessibilityManagerService} by mocking the - * {@link android.view.accessibility.AccessibilityManager} which talks to to the - * service. The service itself is interacting with the platform. Note: Testing - * the service in full isolation would require significant amount of work for - * mocking all system interactions. It would also require a lot of mocking code. - */ -public class AccessibilityManagerServiceTest extends AndroidTestCase { - - /** - * Timeout required for pending Binder calls or event processing to - * complete. - */ - private static final long TIMEOUT_BINDER_CALL = 100; - - /** - * Timeout in which we are waiting for the system to start the mock - * accessibility services. - */ - private static final long TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES = 1000; - - /** - * Timeout used for testing that a service is notified only upon a - * notification timeout. - */ - private static final long TIMEOUT_TEST_NOTIFICATION_TIMEOUT = 300; - - /** - * The interface used to talk to the tested service. - */ - private IAccessibilityManager mManagerService; - - @Override - protected void setUp() throws Exception { - // Reset the state. - ensureOnlyMockServicesEnabled(getContext(), false, false); - } - - @Override - public void setContext(Context context) { - super.setContext(context); - if (MyFirstMockAccessibilityService.sComponentName == null) { - MyFirstMockAccessibilityService.sComponentName = new ComponentName( - context.getPackageName(), MyFirstMockAccessibilityService.class.getName()) - .flattenToShortString(); - } - if (MySecondMockAccessibilityService.sComponentName == null) { - MySecondMockAccessibilityService.sComponentName = new ComponentName( - context.getPackageName(), MySecondMockAccessibilityService.class.getName()) - .flattenToShortString(); - } - } - - /** - * Creates a new instance. - */ - public AccessibilityManagerServiceTest() { - IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); - mManagerService = IAccessibilityManager.Stub.asInterface(iBinder); - } - - @LargeTest - public void testAddClient_AccessibilityDisabledThenEnabled() throws Exception { - // at least some service must be enabled, otherwise accessibility will always be disabled. - ensureOnlyMockServicesEnabled(mContext, true, false); - - // make sure accessibility is disabled - ensureAccessibilityEnabled(mContext, false); - - // create a client mock instance - MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient(); - - // invoke the method under test - final int stateFlagsDisabled = - IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT)); - boolean enabledAccessibilityDisabled = - (stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; - - // check expected result - assertFalse("The client must be disabled since accessibility is disabled.", - enabledAccessibilityDisabled); - - // enable accessibility - ensureAccessibilityEnabled(mContext, true); - - // invoke the method under test - final int stateFlagsEnabled = - IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT)); - boolean enabledAccessibilityEnabled = - (stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; - - // check expected result - assertTrue("The client must be enabled since accessibility is enabled.", - enabledAccessibilityEnabled); - } - - @LargeTest - public void testAddClient_AccessibilityEnabledThenDisabled() throws Exception { - // at least some service must be enabled, otherwise accessibility will always be disabled. - ensureOnlyMockServicesEnabled(mContext, true, false); - - // enable accessibility before registering the client - ensureAccessibilityEnabled(mContext, true); - - // create a client mock instance - MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient(); - - // invoke the method under test - final int stateFlagsEnabled = - IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT)); - boolean enabledAccessibilityEnabled = - (stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; - - // check expected result - assertTrue("The client must be enabled since accessibility is enabled.", - enabledAccessibilityEnabled); - - // disable accessibility - ensureAccessibilityEnabled(mContext, false); - - // invoke the method under test - final int stateFlagsDisabled = - IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT)); - boolean enabledAccessibilityDisabled = - (stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; - - // check expected result - assertFalse("The client must be disabled since accessibility is disabled.", - enabledAccessibilityDisabled); - } - - @LargeTest - public void testGetAccessibilityServicesList() throws Exception { - boolean firstMockServiceInstalled = false; - boolean secondMockServiceInstalled = false; - - String packageName = getContext().getPackageName(); - String firstMockServiceClassName = MyFirstMockAccessibilityService.class.getName(); - String secondMockServiceClassName = MySecondMockAccessibilityService.class.getName(); - - // look for the two mock services - for (AccessibilityServiceInfo info : mManagerService.getInstalledAccessibilityServiceList( - UserHandle.USER_CURRENT)) { - ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo; - if (packageName.equals(serviceInfo.packageName)) { - if (firstMockServiceClassName.equals(serviceInfo.name)) { - firstMockServiceInstalled = true; - } else if (secondMockServiceClassName.equals(serviceInfo.name)) { - secondMockServiceInstalled = true; - } - } - } - - // check expected result - assertTrue("First mock service must be installed", firstMockServiceInstalled); - assertTrue("Second mock service must be installed", secondMockServiceInstalled); - } - - @LargeTest - public void testSendAccessibilityEvent_OneService_MatchingPackageAndEventType() - throws Exception { - // enable the mock accessibility service - ensureOnlyMockServicesEnabled(mContext, true, false); - - // set the accessibility setting value - ensureAccessibilityEnabled(mContext, true); - - // configure the mock service - MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance; - service.setServiceInfo(MockAccessibilityService.createDefaultInfo()); - - // wait for the binder call to #setService to complete - Thread.sleep(TIMEOUT_BINDER_CALL); - - // create and populate an event to be sent - AccessibilityEvent sentEvent = AccessibilityEvent.obtain(); - fullyPopulateDefaultAccessibilityEvent(sentEvent); - - // set expectations - service.expectEvent(sentEvent); - service.replay(); - - // send the event - mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT); - - // verify if all expected methods have been called - assertMockServiceVerifiedWithinTimeout(service); - } - - @LargeTest - public void testSendAccessibilityEvent_OneService_NotMatchingPackage() throws Exception { - // enable the mock accessibility service - ensureOnlyMockServicesEnabled(mContext, true, false); - - // set the accessibility setting value - ensureAccessibilityEnabled(mContext, true); - - // configure the mock service - MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance; - service.setServiceInfo(MockAccessibilityService.createDefaultInfo()); - - // wait for the binder call to #setService to complete - Thread.sleep(TIMEOUT_BINDER_CALL); - - // create and populate an event to be sent - AccessibilityEvent sentEvent = AccessibilityEvent.obtain(); - fullyPopulateDefaultAccessibilityEvent(sentEvent); - sentEvent.setPackageName("no.service.registered.for.this.package"); - - // set expectations - service.replay(); - - // send the event - mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT); - - // verify if all expected methods have been called - assertMockServiceVerifiedWithinTimeout(service); - } - - @LargeTest - public void testSendAccessibilityEvent_OneService_NotMatchingEventType() throws Exception { - // enable the mock accessibility service - ensureOnlyMockServicesEnabled(mContext, true, false); - - // set the accessibility setting value - ensureAccessibilityEnabled(mContext, true); - - // configure the mock service - MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance; - service.setServiceInfo(MockAccessibilityService.createDefaultInfo()); - - // wait for the binder call to #setService to complete - Thread.sleep(TIMEOUT_BINDER_CALL); - - // create and populate an event to be sent - AccessibilityEvent sentEvent = AccessibilityEvent.obtain(); - fullyPopulateDefaultAccessibilityEvent(sentEvent); - sentEvent.setEventType(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); - - // set expectations - service.replay(); - - // send the event - mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT); - - // verify if all expected methods have been called - assertMockServiceVerifiedWithinTimeout(service); - } - - @LargeTest - public void testSendAccessibilityEvent_OneService_NotificationAfterTimeout() throws Exception { - // enable the mock accessibility service - ensureOnlyMockServicesEnabled(mContext, true, false); - - // set the accessibility setting value - ensureAccessibilityEnabled(mContext, true); - - // configure the mock service - MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance; - AccessibilityServiceInfo info = MockAccessibilityService.createDefaultInfo(); - info.notificationTimeout = TIMEOUT_TEST_NOTIFICATION_TIMEOUT; - service.setServiceInfo(info); - - // wait for the binder call to #setService to complete - Thread.sleep(TIMEOUT_BINDER_CALL); - - // create and populate the first event to be sent - AccessibilityEvent firstEvent = AccessibilityEvent.obtain(); - fullyPopulateDefaultAccessibilityEvent(firstEvent); - - // create and populate the second event to be sent - AccessibilityEvent secondEvent = AccessibilityEvent.obtain(); - fullyPopulateDefaultAccessibilityEvent(secondEvent); - - // set expectations - service.expectEvent(secondEvent); - service.replay(); - - // send the events - mManagerService.sendAccessibilityEvent(firstEvent, UserHandle.USER_CURRENT); - mManagerService.sendAccessibilityEvent(secondEvent, UserHandle.USER_CURRENT); - - // wait for #sendAccessibilityEvent to reach the backing service - Thread.sleep(TIMEOUT_BINDER_CALL); - - try { - service.verify(); - fail("No events must be dispatched before the expiration of the notification timeout."); - } catch (IllegalStateException ise) { - /* expected */ - } - - // wait for the configured notification timeout to expire - Thread.sleep(TIMEOUT_TEST_NOTIFICATION_TIMEOUT); - - // verify if all expected methods have been called - assertMockServiceVerifiedWithinTimeout(service); - } - - @LargeTest - public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_DiffFeedback() - throws Exception { - // enable the mock accessibility services - ensureOnlyMockServicesEnabled(mContext, true, true); - - // set the accessibility setting value - ensureAccessibilityEnabled(mContext, true); - - // configure the first mock service - MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance; - AccessibilityServiceInfo firstInfo = MockAccessibilityService.createDefaultInfo(); - firstInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE; - firstService.setServiceInfo(firstInfo); - - // configure the second mock service - MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance; - AccessibilityServiceInfo secondInfo = MockAccessibilityService.createDefaultInfo(); - secondInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_HAPTIC; - secondService.setServiceInfo(secondInfo); - - // wait for the binder calls to #setService to complete - Thread.sleep(TIMEOUT_BINDER_CALL); - - // create and populate an event to be sent - AccessibilityEvent sentEvent = AccessibilityEvent.obtain(); - fullyPopulateDefaultAccessibilityEvent(sentEvent); - - // set expectations for the first mock service - firstService.expectEvent(sentEvent); - firstService.replay(); - - // set expectations for the second mock service - secondService.expectEvent(sentEvent); - secondService.replay(); - - // send the event - mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT); - - // verify if all expected methods have been called - assertMockServiceVerifiedWithinTimeout(firstService); - assertMockServiceVerifiedWithinTimeout(secondService); - } - - @LargeTest - public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType() - throws Exception { - // enable the mock accessibility services - ensureOnlyMockServicesEnabled(mContext, true, true); - - // set the accessibility setting value - ensureAccessibilityEnabled(mContext, true); - - // configure the first mock service - MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance; - firstService.setServiceInfo(MockAccessibilityService.createDefaultInfo()); - - // configure the second mock service - MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance; - secondService.setServiceInfo(MockAccessibilityService.createDefaultInfo()); - - // wait for the binder calls to #setService to complete - Thread.sleep(TIMEOUT_BINDER_CALL); - - // create and populate an event to be sent - AccessibilityEvent sentEvent = AccessibilityEvent.obtain(); - fullyPopulateDefaultAccessibilityEvent(sentEvent); - - // set expectations for the first mock service - firstService.expectEvent(sentEvent); - firstService.replay(); - - // set expectations for the second mock service - secondService.replay(); - - // send the event - mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT); - - // verify if all expected methods have been called - assertMockServiceVerifiedWithinTimeout(firstService); - assertMockServiceVerifiedWithinTimeout(secondService); - } - - @LargeTest - public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_OneDefault() - throws Exception { - // enable the mock accessibility services - ensureOnlyMockServicesEnabled(mContext, true, true); - - // set the accessibility setting value - ensureAccessibilityEnabled(mContext, true); - - // configure the first mock service - MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance; - AccessibilityServiceInfo firstInfo = MyFirstMockAccessibilityService.createDefaultInfo(); - firstInfo.flags = AccessibilityServiceInfo.DEFAULT; - firstService.setServiceInfo(firstInfo); - - // configure the second mock service - MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance; - secondService.setServiceInfo(MySecondMockAccessibilityService.createDefaultInfo()); - - // wait for the binder calls to #setService to complete - Thread.sleep(TIMEOUT_BINDER_CALL); - - // create and populate an event to be sent - AccessibilityEvent sentEvent = AccessibilityEvent.obtain(); - fullyPopulateDefaultAccessibilityEvent(sentEvent); - - // set expectations for the first mock service - firstService.replay(); - - // set expectations for the second mock service - secondService.expectEvent(sentEvent); - secondService.replay(); - - // send the event - mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT); - - // verify if all expected methods have been called - assertMockServiceVerifiedWithinTimeout(firstService); - assertMockServiceVerifiedWithinTimeout(secondService); - } - - @LargeTest - public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_TwoDefault() - throws Exception { - // enable the mock accessibility services - ensureOnlyMockServicesEnabled(mContext, true, true); - - // set the accessibility setting value - ensureAccessibilityEnabled(mContext, true); - - // configure the first mock service - MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance; - AccessibilityServiceInfo firstInfo = MyFirstMockAccessibilityService.createDefaultInfo(); - firstInfo.flags = AccessibilityServiceInfo.DEFAULT; - firstService.setServiceInfo(firstInfo); - - // configure the second mock service - MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance; - AccessibilityServiceInfo secondInfo = MyFirstMockAccessibilityService.createDefaultInfo(); - secondInfo.flags = AccessibilityServiceInfo.DEFAULT; - secondService.setServiceInfo(firstInfo); - - // wait for the binder calls to #setService to complete - Thread.sleep(TIMEOUT_BINDER_CALL); - - // create and populate an event to be sent - AccessibilityEvent sentEvent = AccessibilityEvent.obtain(); - fullyPopulateDefaultAccessibilityEvent(sentEvent); - - // set expectations for the first mock service - firstService.expectEvent(sentEvent); - firstService.replay(); - - // set expectations for the second mock service - secondService.replay(); - - // send the event - mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT); - - // verify if all expected methods have been called - assertMockServiceVerifiedWithinTimeout(firstService); - assertMockServiceVerifiedWithinTimeout(secondService); - } - - @LargeTest - public void testInterrupt() throws Exception { - // enable the mock accessibility services - ensureOnlyMockServicesEnabled(mContext, true, true); - - // set the accessibility setting value - ensureAccessibilityEnabled(mContext, true); - - // configure the first mock service - MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance; - firstService.setServiceInfo(MockAccessibilityService.createDefaultInfo()); - - // configure the second mock service - MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance; - secondService.setServiceInfo(MockAccessibilityService.createDefaultInfo()); - - // wait for the binder calls to #setService to complete - Thread.sleep(TIMEOUT_BINDER_CALL); - - // set expectations for the first mock service - firstService.expectInterrupt(); - firstService.replay(); - - // set expectations for the second mock service - secondService.expectInterrupt(); - secondService.replay(); - - // call the method under test - mManagerService.interrupt(UserHandle.USER_CURRENT); - - // verify if all expected methods have been called - assertMockServiceVerifiedWithinTimeout(firstService); - assertMockServiceVerifiedWithinTimeout(secondService); - } - - /** - * Fully populates the {@link AccessibilityEvent} to marshal. - * - * @param sentEvent The event to populate. - */ - private void fullyPopulateDefaultAccessibilityEvent(AccessibilityEvent sentEvent) { - sentEvent.setAddedCount(1); - sentEvent.setBeforeText("BeforeText"); - sentEvent.setChecked(true); - sentEvent.setClassName("foo.bar.baz.Class"); - sentEvent.setContentDescription("ContentDescription"); - sentEvent.setCurrentItemIndex(1); - sentEvent.setEnabled(true); - sentEvent.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT); - sentEvent.setEventTime(1000); - sentEvent.setFromIndex(1); - sentEvent.setFullScreen(true); - sentEvent.setItemCount(1); - sentEvent.setPackageName("foo.bar.baz"); - sentEvent.setParcelableData(Message.obtain(null, 1, null)); - sentEvent.setPassword(true); - sentEvent.setRemovedCount(1); - } - - /** - * This class is a mock {@link IAccessibilityManagerClient}. - */ - public class MyMockAccessibilityManagerClient extends IAccessibilityManagerClient.Stub { - int mState; - - public void setState(int state) { - mState = state; - } - - public void notifyServicesStateChanged() {} - - public void setRelevantEventTypes(int eventTypes) {} - - public void setTouchExplorationEnabled(boolean enabled) {} - } - - /** - * Ensures accessibility is in a given state by writing the state to the - * settings and waiting until the accessibility manager service pick it up. - * - * @param context A context handle to access the settings. - * @param enabled The accessibility state to write to the settings. - * @throws Exception If any error occurs. - */ - private void ensureAccessibilityEnabled(Context context, boolean enabled) throws Exception { - boolean isEnabled = Settings.Secure.getInt(context.getContentResolver(), - Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; - - if (isEnabled == enabled) { - return; - } - - Settings.Secure.putInt(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, - enabled ? 1 : 0); - - // wait the accessibility manager service to pick the change up - Thread.sleep(TIMEOUT_BINDER_CALL); - } - - /** - * Ensures the only {@link MockAccessibilityService}s with given component - * names are enabled by writing to the system settings and waiting until the - * accessibility manager service picks that up or the - * {@link #TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES} is exceeded. - * - * @param context A context handle to access the settings. - * @param firstMockServiceEnabled If the first mock accessibility service is enabled. - * @param secondMockServiceEnabled If the second mock accessibility service is enabled. - * @throws IllegalStateException If some of the requested for enabling mock services - * is not properly started. - * @throws Exception Exception If any error occurs. - */ - private void ensureOnlyMockServicesEnabled(Context context, boolean firstMockServiceEnabled, - boolean secondMockServiceEnabled) throws Exception { - String enabledServices = Settings.Secure.getString(context.getContentResolver(), - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); - - StringBuilder servicesToEnable = new StringBuilder(); - if (firstMockServiceEnabled) { - servicesToEnable.append(MyFirstMockAccessibilityService.sComponentName).append(":"); - } - if (secondMockServiceEnabled) { - servicesToEnable.append(MySecondMockAccessibilityService.sComponentName).append(":"); - } - - Settings.Secure.putString(context.getContentResolver(), - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, servicesToEnable.toString()); - - // Optimization. If things will not change, we don't have to do anything. - if (servicesToEnable.equals(enabledServices)) { - return; - } - - // we have enabled the services of interest and need to wait until they - // are instantiated and started (if needed) and the system binds to them - boolean firstMockServiceOK = false; - boolean secondMockServiceOK = false; - long start = SystemClock.uptimeMillis(); - long pollingInterval = TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES / 6; - - while (SystemClock.uptimeMillis() - start < TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES) { - firstMockServiceOK = !firstMockServiceEnabled - || (MyFirstMockAccessibilityService.sInstance != null - && MyFirstMockAccessibilityService.sInstance.isSystemBoundAsClient()); - - secondMockServiceOK = !secondMockServiceEnabled - || (MySecondMockAccessibilityService.sInstance != null - && MySecondMockAccessibilityService.sInstance.isSystemBoundAsClient()); - - if (firstMockServiceOK && secondMockServiceOK) { - return; - } - - Thread.sleep(pollingInterval); - } - - StringBuilder message = new StringBuilder(); - message.append("Mock accessibility services not started or system not bound as a client: "); - if (!firstMockServiceOK) { - message.append(MyFirstMockAccessibilityService.sComponentName); - message.append(" "); - } - if (!secondMockServiceOK) { - message.append(MySecondMockAccessibilityService.sComponentName); - } - throw new IllegalStateException(message.toString()); - } - - /** - * Asserts the the mock accessibility service has been successfully verified - * (which is it has received the expected method calls with expected - * arguments) within the {@link #TIMEOUT_BINDER_CALL}. The verified state is - * checked by polling upon small intervals. - * - * @param service The service to verify. - * @throws Exception If the verification has failed with exception after the - * {@link #TIMEOUT_BINDER_CALL}. - */ - private void assertMockServiceVerifiedWithinTimeout(MockAccessibilityService service) - throws Exception { - Exception lastVerifyException = null; - long beginTime = SystemClock.uptimeMillis(); - long pollTimeout = TIMEOUT_BINDER_CALL / 5; - - // poll until the timeout has elapsed - while (SystemClock.uptimeMillis() - beginTime < TIMEOUT_BINDER_CALL) { - // sleep first since immediate call will always fail - try { - Thread.sleep(pollTimeout); - } catch (InterruptedException ie) { - /* ignore */ - } - // poll for verification and if this fails save the exception and - // keep polling - try { - service.verify(); - // reset so it does not accept more events - service.reset(); - return; - } catch (Exception e) { - lastVerifyException = e; - } - } - - // reset, we have already failed - service.reset(); - - // always not null - throw lastVerifyException; - } - - /** - * This class is the first mock {@link AccessibilityService}. - */ - public static class MyFirstMockAccessibilityService extends MockAccessibilityService { - - /** - * The service {@link ComponentName} flattened as a string. - */ - static String sComponentName; - - /** - * Handle to the service instance. - */ - static MyFirstMockAccessibilityService sInstance; - - /** - * Creates a new instance. - */ - public MyFirstMockAccessibilityService() { - sInstance = this; - } - } - - /** - * This class is the first mock {@link AccessibilityService}. - */ - public static class MySecondMockAccessibilityService extends MockAccessibilityService { - - /** - * The service {@link ComponentName} flattened as a string. - */ - static String sComponentName; - - /** - * Handle to the service instance. - */ - static MySecondMockAccessibilityService sInstance; - - /** - * Creates a new instance. - */ - public MySecondMockAccessibilityService() { - sInstance = this; - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java b/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java deleted file mode 100644 index e1c5cee752e1..000000000000 --- a/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.accessibilityservice.AccessibilityService; -import android.accessibilityservice.AccessibilityServiceInfo; -import android.content.Intent; -import android.os.Message; -import android.view.accessibility.AccessibilityEvent; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -import junit.framework.TestCase; - -/** - * This is the base class for mock {@link AccessibilityService}s. - */ -public abstract class MockAccessibilityService extends AccessibilityService { - - /** - * The event this service expects to receive. - */ - private final Queue<AccessibilityEvent> mExpectedEvents = new LinkedList<AccessibilityEvent>(); - - /** - * Interruption call this service expects to receive. - */ - private boolean mExpectedInterrupt; - - /** - * Flag if the mock is currently replaying. - */ - private boolean mReplaying; - - /** - * Flag if the system is bound as a client to this service. - */ - private boolean mIsSystemBoundAsClient; - - /** - * Creates an {@link AccessibilityServiceInfo} populated with default - * values. - * - * @return The default info. - */ - public static AccessibilityServiceInfo createDefaultInfo() { - AccessibilityServiceInfo defaultInfo = new AccessibilityServiceInfo(); - defaultInfo.eventTypes = AccessibilityEvent.TYPE_ANNOUNCEMENT; - defaultInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE; - defaultInfo.flags = 0; - defaultInfo.notificationTimeout = 0; - defaultInfo.packageNames = new String[] { - "foo.bar.baz" - }; - - return defaultInfo; - } - - /** - * Starts replaying the mock. - */ - public void replay() { - mReplaying = true; - } - - /** - * Verifies if all expected service methods have been called. - */ - public void verify() { - if (!mReplaying) { - throw new IllegalStateException("Did you forget to call replay()"); - } - - if (mExpectedInterrupt) { - throw new IllegalStateException("Expected call to #interrupt() not received"); - } - if (!mExpectedEvents.isEmpty()) { - throw new IllegalStateException("Expected a call to onAccessibilityEvent() for " - + "events \"" + mExpectedEvents + "\" not received"); - } - } - - /** - * Resets this instance so it can be reused. - */ - public void reset() { - mExpectedEvents.clear(); - mExpectedInterrupt = false; - mReplaying = false; - } - - /** - * Sets an expected call to - * {@link #onAccessibilityEvent(AccessibilityEvent)} with given event as - * argument. - * - * @param expectedEvent The expected event argument. - */ - public void expectEvent(AccessibilityEvent expectedEvent) { - mExpectedEvents.add(expectedEvent); - } - - /** - * Sets an expected call of {@link #onInterrupt()}. - */ - public void expectInterrupt() { - mExpectedInterrupt = true; - } - - @Override - public void onAccessibilityEvent(AccessibilityEvent receivedEvent) { - if (!mReplaying) { - return; - } - - if (mExpectedEvents.isEmpty()) { - throw new IllegalStateException("Unexpected event: " + receivedEvent); - } - - AccessibilityEvent expectedEvent = mExpectedEvents.poll(); - assertEqualsAccessiblityEvent(expectedEvent, receivedEvent); - } - - @Override - public void onInterrupt() { - if (!mReplaying) { - return; - } - - if (!mExpectedInterrupt) { - throw new IllegalStateException("Unexpected call to onInterrupt()"); - } - - mExpectedInterrupt = false; - } - - @Override - protected void onServiceConnected() { - mIsSystemBoundAsClient = true; - } - - @Override - public boolean onUnbind(Intent intent) { - mIsSystemBoundAsClient = false; - return false; - } - - /** - * Returns if the system is bound as client to this service. - * - * @return True if the system is bound, false otherwise. - */ - public boolean isSystemBoundAsClient() { - return mIsSystemBoundAsClient; - } - - /** - * Compares all properties of the <code>expectedEvent</code> and the - * <code>receviedEvent</code> to verify that the received event is the one - * that is expected. - */ - private void assertEqualsAccessiblityEvent(AccessibilityEvent expectedEvent, - AccessibilityEvent receivedEvent) { - TestCase.assertEquals("addedCount has incorrect value", expectedEvent.getAddedCount(), - receivedEvent.getAddedCount()); - TestCase.assertEquals("beforeText has incorrect value", expectedEvent.getBeforeText(), - receivedEvent.getBeforeText()); - TestCase.assertEquals("checked has incorrect value", expectedEvent.isChecked(), - receivedEvent.isChecked()); - TestCase.assertEquals("className has incorrect value", expectedEvent.getClassName(), - receivedEvent.getClassName()); - TestCase.assertEquals("contentDescription has incorrect value", expectedEvent - .getContentDescription(), receivedEvent.getContentDescription()); - TestCase.assertEquals("currentItemIndex has incorrect value", expectedEvent - .getCurrentItemIndex(), receivedEvent.getCurrentItemIndex()); - TestCase.assertEquals("enabled has incorrect value", expectedEvent.isEnabled(), - receivedEvent.isEnabled()); - TestCase.assertEquals("eventType has incorrect value", expectedEvent.getEventType(), - receivedEvent.getEventType()); - TestCase.assertEquals("fromIndex has incorrect value", expectedEvent.getFromIndex(), - receivedEvent.getFromIndex()); - TestCase.assertEquals("fullScreen has incorrect value", expectedEvent.isFullScreen(), - receivedEvent.isFullScreen()); - TestCase.assertEquals("itemCount has incorrect value", expectedEvent.getItemCount(), - receivedEvent.getItemCount()); - assertEqualsNotificationAsParcelableData(expectedEvent, receivedEvent); - TestCase.assertEquals("password has incorrect value", expectedEvent.isPassword(), - receivedEvent.isPassword()); - TestCase.assertEquals("removedCount has incorrect value", expectedEvent.getRemovedCount(), - receivedEvent.getRemovedCount()); - assertEqualsText(expectedEvent, receivedEvent); - } - - /** - * Compares the {@link android.os.Parcelable} data of the - * <code>expectedEvent</code> and <code>receivedEvent</code> to verify that - * the received event is the one that is expected. - */ - private void assertEqualsNotificationAsParcelableData(AccessibilityEvent expectedEvent, - AccessibilityEvent receivedEvent) { - String message = "parcelableData has incorrect value"; - Message expectedMessage = (Message) expectedEvent.getParcelableData(); - Message receivedMessage = (Message) receivedEvent.getParcelableData(); - - if (expectedMessage == null) { - if (receivedMessage == null) { - return; - } - } - - TestCase.assertNotNull(message, receivedMessage); - - // we do a very simple sanity check since we do not test Message - TestCase.assertEquals(message, expectedMessage.what, receivedMessage.what); - } - - /** - * Compares the text of the <code>expectedEvent</code> and - * <code>receivedEvent</code> by comparing the string representation of the - * corresponding {@link CharSequence}s. - */ - private void assertEqualsText(AccessibilityEvent expectedEvent, - AccessibilityEvent receivedEvent) { - String message = "text has incorrect value"; - List<CharSequence> expectedText = expectedEvent.getText(); - List<CharSequence> receivedText = receivedEvent.getText(); - - TestCase.assertEquals(message, expectedText.size(), receivedText.size()); - - Iterator<CharSequence> expectedTextIterator = expectedText.iterator(); - Iterator<CharSequence> receivedTextIterator = receivedText.iterator(); - - for (int i = 0; i < expectedText.size(); i++) { - // compare the string representation - TestCase.assertEquals(message, expectedTextIterator.next().toString(), - receivedTextIterator.next().toString()); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java index e0ac393290ac..353199ab0b0b 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java @@ -645,20 +645,6 @@ public class NetworkScoreServiceTest { } @Test - public void testDump_noDumpPermission() { - doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( - eq(permission.DUMP), anyString()); - - try { - mNetworkScoreService.dump( - new FileDescriptor(), new PrintWriter(new StringWriter()), new String[0]); - fail("SecurityException expected"); - } catch (SecurityException e) { - // expected - } - } - - @Test public void testDump_doesNotCrash() { when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER); StringWriter stringWriter = new StringWriter(); 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/AccessibilityManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java index 92617716e28d..5d09e31a3dc7 100644 --- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java @@ -14,18 +14,24 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.accessibility; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertSame; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; +import android.app.Instrumentation; +import android.os.Looper; import android.os.UserHandle; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; @@ -33,6 +39,10 @@ import android.view.accessibility.IAccessibilityManagerClient; import com.android.internal.util.IntPair; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -40,44 +50,48 @@ import java.util.ArrayList; import java.util.List; /** - * Tests for the AccessibilityManager which mocking the backing service. + * Tests for the AccessibilityManager by mocking the backing service. */ -public class AccessibilityManagerTest extends AndroidTestCase { - - /** - * Timeout required for pending Binder calls or event processing to - * complete. - */ - public static final long TIMEOUT_BINDER_CALL = 50; - - @Mock - private IAccessibilityManager mMockService; +@RunWith(AndroidJUnit4.class) +public class AccessibilityManagerTest { + private static final boolean WITH_A11Y_ENABLED = true; + private static final boolean WITH_A11Y_DISABLED = false; + + @Mock private IAccessibilityManager mMockService; + private MessageCapturingHandler mHandler; + private Instrumentation mInstrumentation; + + @BeforeClass + public static void oneTimeInitialization() { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + } - @Override + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mHandler = new MessageCapturingHandler(null); + mInstrumentation = InstrumentationRegistry.getInstrumentation(); } private AccessibilityManager createManager(boolean enabled) throws Exception { - if (enabled) { - when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt())) - .thenReturn( - IntPair.of(AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED, - AccessibilityEvent.TYPES_ALL_MASK)); - } else { - when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt())) - .thenReturn(IntPair.of(0, AccessibilityEvent.TYPES_ALL_MASK)); - } + long serviceReturnValue = IntPair.of( + (enabled) ? AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED : 0, + AccessibilityEvent.TYPES_ALL_MASK); + when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt())) + .thenReturn(serviceReturnValue); AccessibilityManager manager = - new AccessibilityManager(mContext, mMockService, UserHandle.USER_CURRENT); + new AccessibilityManager(mHandler, mMockService, UserHandle.USER_CURRENT); verify(mMockService).addClient(any(IAccessibilityManagerClient.class), anyInt()); - + mHandler.setCallback(manager.getCallback()); + mHandler.sendAllMessages(); return manager; } - @MediumTest + @Test public void testGetAccessibilityServiceList() throws Exception { // create a list of installed accessibility services the mock service returns List<AccessibilityServiceInfo> expectedServices = new ArrayList<>(); @@ -99,59 +113,50 @@ public class AccessibilityManagerTest extends AndroidTestCase { assertEquals("All expected services must be returned", expectedServices, receivedServices); } - @MediumTest + @Test public void testInterrupt() throws Exception { - AccessibilityManager manager = createManager(true); + AccessibilityManager manager = createManager(WITH_A11Y_ENABLED); manager.interrupt(); verify(mMockService).interrupt(UserHandle.USER_CURRENT); } - @LargeTest + @Test public void testIsEnabled() throws Exception { - // invoke the method under test - AccessibilityManager manager = createManager(true); - boolean isEnabledServiceEnabled = manager.isEnabled(); + // Create manager with a11y enabled + AccessibilityManager manager = createManager(WITH_A11Y_ENABLED); + assertTrue("Must be enabled since the mock service is enabled", manager.isEnabled()); - // check expected result - assertTrue("Must be enabled since the mock service is enabled", isEnabledServiceEnabled); - - // disable accessibility + // Disable accessibility manager.getClient().setState(0); - - // wait for the asynchronous IBinder call to complete - Thread.sleep(TIMEOUT_BINDER_CALL); - - // invoke the method under test - boolean isEnabledServcieDisabled = manager.isEnabled(); - - // check expected result - assertFalse("Must be disabled since the mock service is disabled", - isEnabledServcieDisabled); + mHandler.sendAllMessages(); + assertFalse("Must be disabled since the mock service is disabled", manager.isEnabled()); } - @MediumTest + @Test public void testSendAccessibilityEvent_AccessibilityEnabled() throws Exception { - AccessibilityEvent sentEvent = AccessibilityEvent.obtain(); + AccessibilityEvent sentEvent = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_ANNOUNCEMENT); - AccessibilityManager manager = createManager(true); + AccessibilityManager manager = createManager(WITH_A11Y_ENABLED); manager.sendAccessibilityEvent(sentEvent); assertSame("The event should be recycled.", sentEvent, AccessibilityEvent.obtain()); } - @MediumTest + @Test public void testSendAccessibilityEvent_AccessibilityDisabled() throws Exception { AccessibilityEvent sentEvent = AccessibilityEvent.obtain(); - AccessibilityManager manager = createManager(false /* disabled */); - - try { - manager.sendAccessibilityEvent(sentEvent); - fail("No accessibility events are sent if accessibility is disabled"); - } catch (IllegalStateException ise) { - // check expected result - assertEquals("Accessibility off. Did you forget to check that?", ise.getMessage()); - } + AccessibilityManager manager = createManager(WITH_A11Y_DISABLED); + mInstrumentation.runOnMainSync(() -> { + try { + manager.sendAccessibilityEvent(sentEvent); + fail("No accessibility events are sent if accessibility is disabled"); + } catch (IllegalStateException ise) { + // check expected result + assertEquals("Accessibility off. Did you forget to check that?", ise.getMessage()); + } + }); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java index d44c1ca0c0ef..5887215257e3 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java @@ -20,11 +20,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; -import static org.mockito.Matchers.anyInt; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -48,6 +49,7 @@ import android.view.WindowManagerInternal; import android.view.WindowManagerInternal.MagnificationCallbacks; import com.android.internal.R; + import org.hamcrest.CoreMatchers; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; @@ -471,9 +473,10 @@ public class MagnificationControllerTest { public void testResetIfNeeded_doesWhatItSays() { mMagnificationController.register(); zoomIn2xToMiddle(); + reset(mMockAms); assertTrue(mMagnificationController.resetIfNeeded(false)); verify(mMockAms).notifyMagnificationChanged( - eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyInt(), anyInt()); + eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat()); assertFalse(mMagnificationController.isMagnifying()); assertFalse(mMagnificationController.resetIfNeeded(false)); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java b/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java index 003f7abf6b00..0dba35f2a164 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java @@ -42,6 +42,10 @@ public class MessageCapturingHandler extends Handler { return super.sendMessageAtTime(message, uptimeMillis); } + public void setCallback(Handler.Callback callback) { + mCallback = callback; + } + public void sendOneMessage() { Message message = timedMessages.remove(0).first; removeMessages(message.what, message.obj); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java index aa374073e4a5..5d0c23f81737 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java @@ -149,7 +149,7 @@ public class AccountsDbTest { // 2nd account Account account2 = new Account("name", "example2.com"); long accId2 = mAccountsDb.insertCeAccount(account2, "password"); - mAccountsDb.insertDeAccount(account2, accId); + mAccountsDb.insertDeAccount(account2, accId2); mAccountsDb.insertAuthToken(accId2, "type", "token"); mAccountsDb.deleteAuthTokensByAccountId(accId2); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java new file mode 100644 index 000000000000..8423affecd30 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java @@ -0,0 +1,52 @@ +/* + * 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.assertNull; + +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; + +import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; + +/** + * Tests for the {@link ActivityStackSupervisor} class. + * + * Build/Install/Run: + * bit FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests + */ +@MediumTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class ActivityStackSupervisorTests extends ActivityTestsBase { + /** + * This test ensures that we do not try to restore a task based off an invalid task id. The + * stack supervisor is a test version so there will be no tasks present. We should expect + * {@code null} to be returned in this case. + */ + @Test + public void testRestoringInvalidTask() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + TaskRecord task = service.mStackSupervisor.anyTaskForIdLocked(0 /*taskId*/, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, 0 /*stackId*/); + assertNull(task); + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index c5cc2ff22abd..52405863e8fe 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -25,7 +25,7 @@ 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.HandlerThread; import android.os.Looper; import android.support.test.InstrumentationRegistry; import com.android.server.AttributeCache; @@ -34,6 +34,7 @@ import com.android.server.wm.StackWindowController; import com.android.server.wm.WindowManagerService; import com.android.server.wm.WindowTestUtils; +import org.junit.After; import org.junit.Before; import org.mockito.MockitoAnnotations; @@ -42,8 +43,7 @@ import org.mockito.MockitoAnnotations; */ public class ActivityTestsBase { private final Context mContext = InstrumentationRegistry.getContext(); - private static boolean sLooperPrepared; - private Handler mHandler; + private HandlerThread mHandlerThread; // Grabbing an instance of {@link WindowManagerService} creates it if not present so this must // be called at before any tests. @@ -52,11 +52,13 @@ public class ActivityTestsBase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mHandlerThread = new HandlerThread("ActivityTestsBaseThread"); + mHandlerThread.start(); + } - if (!sLooperPrepared) { - sLooperPrepared = true; - Looper.prepare(); - } + @After + public void tearDown() { + mHandlerThread.quitSafely(); } protected ActivityManagerService createActivityManagerService() { @@ -126,7 +128,7 @@ public class ActivityTestsBase { @Override protected ActivityStackSupervisor createStackSupervisor() { - return new TestActivityStackSupervisor(this, new Handler().getLooper()); + return new TestActivityStackSupervisor(this, mHandlerThread.getLooper()); } } diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 0f1b81e49f9e..7a4746a7f54b 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -16,6 +16,7 @@ package com.android.server.am; +import android.app.ActivityManager; import android.app.IUserSwitchObserver; import android.content.Context; import android.content.IIntentReceiver; @@ -49,16 +50,20 @@ import java.util.Set; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.server.am.ActivityManagerService.CONTINUE_USER_SWITCH_MSG; +import static com.android.server.am.ActivityManagerService.REPORT_LOCKED_BOOT_COMPLETE_MSG; import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG; import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG; import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG; import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG; import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -71,9 +76,29 @@ public class UserControllerTest extends AndroidTestCase { private UserController mUserController; private TestInjector mInjector; + private static final List<String> START_FOREGROUND_USER_ACTIONS = + Arrays.asList( + Intent.ACTION_USER_STARTED, + Intent.ACTION_USER_SWITCHED, + Intent.ACTION_USER_STARTING); + + private static final List<String> START_BACKGROUND_USER_ACTIONS = + Arrays.asList( + Intent.ACTION_USER_STARTED, + Intent.ACTION_LOCKED_BOOT_COMPLETED, + Intent.ACTION_USER_STARTING); + + private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = + new HashSet<>(Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG, + SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG)); + + private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = + new HashSet<>(Arrays.asList(SYSTEM_USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG)); + @Override public void setUp() throws Exception { super.setUp(); + System.setProperty("dexmaker.share_classloader", "true"); mInjector = new TestInjector(getContext()); mUserController = new UserController(mInjector); setUpUser(TEST_USER_ID, 0); @@ -83,39 +108,62 @@ public class UserControllerTest extends AndroidTestCase { protected void tearDown() throws Exception { super.tearDown(); mInjector.handlerThread.quit(); - } @SmallTest - public void testStartUser() throws RemoteException { - mUserController.startUser(TEST_USER_ID, true); + public void testStartUser_foreground() throws RemoteException { + mUserController.startUser(TEST_USER_ID, true /* foreground */); Mockito.verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt()); Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean()); Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true); - startUserAssertions(); + Mockito.verify(mInjector.getActivityStackSupervisor()).setLockTaskModeLocked( + nullable(TaskRecord.class), + eq(ActivityManager.LOCK_TASK_MODE_NONE), + anyString(), + anyBoolean()); + startForegroundUserAssertions(); + } + + @SmallTest + public void testStartUser_background() throws RemoteException { + mUserController.startUser(TEST_USER_ID, false /* foreground */); + Mockito.verify( + mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt()); + Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean()); + Mockito.verify(mInjector.getActivityStackSupervisor(), never()).setLockTaskModeLocked( + nullable(TaskRecord.class), + eq(ActivityManager.LOCK_TASK_MODE_NONE), + anyString(), + anyBoolean()); + startBackgroundUserAssertions(); } @SmallTest public void testStartUserUIDisabled() throws RemoteException { mUserController.mUserSwitchUiEnabled = false; - mUserController.startUser(TEST_USER_ID, true); + mUserController.startUser(TEST_USER_ID, true /* foreground */); Mockito.verify(mInjector.getWindowManager(), never()) .startFreezingScreen(anyInt(), anyInt()); Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean()); - startUserAssertions(); + startForegroundUserAssertions(); } - private void startUserAssertions() throws RemoteException { - List<String> expectedActions = Arrays.asList(Intent.ACTION_USER_STARTED, - Intent.ACTION_USER_SWITCHED, Intent.ACTION_USER_STARTING); + private void startUserAssertions( + List<String> expectedActions, Set<Integer> expectedMessageCodes) + throws RemoteException { assertEquals(expectedActions, getActions(mInjector.sentIntents)); - Set<Integer> expectedCodes = new HashSet<>( - Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG, - SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG)); Set<Integer> actualCodes = mInjector.handler.getMessageCodes(); - assertEquals("Unexpected message sent", expectedCodes, actualCodes); + assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes); + } + + private void startBackgroundUserAssertions() throws RemoteException { + startUserAssertions(START_BACKGROUND_USER_ACTIONS, START_BACKGROUND_USER_MESSAGE_CODES); + } + + private void startForegroundUserAssertions() throws RemoteException { + startUserAssertions(START_FOREGROUND_USER_ACTIONS, START_FOREGROUND_USER_MESSAGE_CODES); Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG); assertNotNull(reportMsg); UserState userState = (UserState) reportMsg.obj; @@ -275,6 +323,7 @@ public class UserControllerTest extends AndroidTestCase { UserManagerService userManagerMock; UserManagerInternal userManagerInternalMock; WindowManagerService windowManagerMock; + ActivityStackSupervisor activityStackSupervisor; private Context mCtx; List<Intent> sentIntents = new ArrayList<>(); @@ -287,6 +336,7 @@ public class UserControllerTest extends AndroidTestCase { userManagerMock = mock(UserManagerService.class); userManagerInternalMock = mock(UserManagerInternal.class); windowManagerMock = mock(WindowManagerService.class); + activityStackSupervisor = mock(ActivityStackSupervisor.class); } @Override @@ -321,12 +371,6 @@ public class UserControllerTest extends AndroidTestCase { } @Override - void stackSupervisorSetLockTaskModeLocked(TaskRecord task, int lockTaskModeState, - String reason, boolean andResume) { - Log.i(TAG, "stackSupervisorSetLockTaskModeLocked"); - } - - @Override WindowManagerService getWindowManager() { return windowManagerMock; } @@ -347,16 +391,15 @@ public class UserControllerTest extends AndroidTestCase { } @Override - boolean stackSupervisorSwitchUserLocked(int userId, UserState uss) { - Log.i(TAG, "stackSupervisorSwitchUserLocked " + userId); - return true; + void startHomeActivityLocked(int userId, String reason) { + Log.i(TAG, "startHomeActivityLocked " + userId); } @Override - void startHomeActivityLocked(int userId, String reason) { - Log.i(TAG, "startHomeActivityLocked " + userId); + ActivityStackSupervisor getActivityStackSupervisor() { + return activityStackSupervisor; } - } + } private static class TestHandler extends Handler { private final List<Message> mMessages = new ArrayList<>(); diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java index f3f68ff06e03..2663aaf35b3d 100644 --- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java @@ -50,6 +50,7 @@ import com.android.server.LocalServices; import org.mockito.ArgumentCaptor; +import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; @@ -249,6 +250,25 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase { assertEquals(7, updates.size()); } + public void testGetInstalledProvidersForPackage() { + List<AppWidgetProviderInfo> allProviders = mManager.getInstalledProviders(); + assertTrue(!allProviders.isEmpty()); + String packageName = allProviders.get(0).provider.getPackageName(); + List<AppWidgetProviderInfo> providersForPackage = mManager.getInstalledProvidersForPackage( + packageName, null); + // Remove providers from allProviders that don't have the given package name. + Iterator<AppWidgetProviderInfo> iter = allProviders.iterator(); + while (iter.hasNext()) { + if (!iter.next().provider.getPackageName().equals(packageName)) { + iter.remove(); + } + } + assertEquals(allProviders.size(), providersForPackage.size()); + for (int i = 0; i < allProviders.size(); i++) { + assertEquals(allProviders.get(i).provider, providersForPackage.get(i).provider); + } + } + private int setupHostAndWidget() { List<PendingHostUpdate> updates = mService.startListening( mMockHost, mPkgName, HOST_ID, new int[0]).getList(); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index 72fb78e89ea2..afc0f67fe993 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -360,6 +360,19 @@ public class DexManagerTests { assertNull(mDexManager.getPackageUseInfo(frameworkDex)); } + @Test + public void testNotifySecondaryFromProtected() { + // Foo loads its own secondary files. + List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs(); + notifyDexLoad(mFooUser0, fooSecondaries, mUser0); + + PackageUseInfo pui = getPackageUseInfo(mFooUser0); + assertNotNull(pui); + assertFalse(pui.isUsedByOtherApps()); + assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size()); + assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0); + } + private void assertSecondaryUse(TestData testData, PackageUseInfo pui, List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) { for (String dex : secondaries) { @@ -394,6 +407,8 @@ public class DexManagerTests { ai.setBaseCodePath(codeDir + "/base.dex"); ai.setSplitCodePaths(new String[] {codeDir + "/split-1.dex", codeDir + "/split-2.dex"}); ai.dataDir = "/data/user/" + userId + "/" + packageName; + ai.deviceProtectedDataDir = "/data/user_de/" + userId + "/" + packageName; + ai.credentialProtectedDataDir = "/data/user_ce/" + userId + "/" + packageName; ai.packageName = packageName; return ai; } @@ -426,6 +441,13 @@ public class DexManagerTests { return paths; } + List<String> getSecondaryDexPathsFromProtectedDirs() { + List<String> paths = new ArrayList<>(); + paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex"); + paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex"); + return paths; + } + List<String> getBaseAndSplitDexPaths() { List<String> paths = new ArrayList<>(); paths.add(mPackageInfo.applicationInfo.sourceDir); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 92233b1fed39..f80ee7386f99 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -356,6 +356,7 @@ public final class Call { private final StatusHints mStatusHints; private final Bundle mExtras; private final Bundle mIntentExtras; + private final long mCreationTimeMillis; /** * Whether the supplied capabilities supports the specified capability. @@ -578,9 +579,12 @@ public final class Call { } /** - * @return The time the {@code Call} has been connected. This information is updated - * periodically, but user interfaces should not rely on this to display any "call time - * clock". + * Returns the time the {@link Call} connected (i.e. became active). This information is + * updated periodically, but user interfaces should not rely on this to display the "call + * time clock". For the time when the call was first added to Telecom, see + * {@link #getCreationTimeMillis()}. + * + * @return The time the {@link Call} connected in milliseconds since the epoch. */ public final long getConnectTimeMillis() { return mConnectTimeMillis; @@ -622,6 +626,18 @@ public final class Call { return mIntentExtras; } + /** + * Returns the time when the call was first created and added to Telecom. This is the same + * time that is logged as the start time in the Call Log (see + * {@link android.provider.CallLog.Calls#DATE}). To determine when the call was connected + * (became active), see {@link #getConnectTimeMillis()}. + * + * @return The creation time of the call, in millis since the epoch. + */ + public long getCreationTimeMillis() { + return mCreationTimeMillis; + } + @Override public boolean equals(Object o) { if (o instanceof Details) { @@ -641,28 +657,29 @@ public final class Call { Objects.equals(mVideoState, d.mVideoState) && Objects.equals(mStatusHints, d.mStatusHints) && areBundlesEqual(mExtras, d.mExtras) && - areBundlesEqual(mIntentExtras, d.mIntentExtras); + areBundlesEqual(mIntentExtras, d.mIntentExtras) && + Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis); } return false; } @Override public int hashCode() { - return - Objects.hashCode(mHandle) + - Objects.hashCode(mHandlePresentation) + - Objects.hashCode(mCallerDisplayName) + - Objects.hashCode(mCallerDisplayNamePresentation) + - Objects.hashCode(mAccountHandle) + - Objects.hashCode(mCallCapabilities) + - Objects.hashCode(mCallProperties) + - Objects.hashCode(mDisconnectCause) + - Objects.hashCode(mConnectTimeMillis) + - Objects.hashCode(mGatewayInfo) + - Objects.hashCode(mVideoState) + - Objects.hashCode(mStatusHints) + - Objects.hashCode(mExtras) + - Objects.hashCode(mIntentExtras); + return Objects.hash(mHandle, + mHandlePresentation, + mCallerDisplayName, + mCallerDisplayNamePresentation, + mAccountHandle, + mCallCapabilities, + mCallProperties, + mDisconnectCause, + mConnectTimeMillis, + mGatewayInfo, + mVideoState, + mStatusHints, + mExtras, + mIntentExtras, + mCreationTimeMillis); } /** {@hide} */ @@ -681,7 +698,8 @@ public final class Call { int videoState, StatusHints statusHints, Bundle extras, - Bundle intentExtras) { + Bundle intentExtras, + long creationTimeMillis) { mTelecomCallId = telecomCallId; mHandle = handle; mHandlePresentation = handlePresentation; @@ -697,6 +715,7 @@ public final class Call { mStatusHints = statusHints; mExtras = extras; mIntentExtras = intentExtras; + mCreationTimeMillis = creationTimeMillis; } /** {@hide} */ @@ -716,7 +735,8 @@ public final class Call { parcelableCall.getVideoState(), parcelableCall.getStatusHints(), parcelableCall.getExtras(), - parcelableCall.getIntentExtras()); + parcelableCall.getIntentExtras(), + parcelableCall.getCreationTimeMillis()); } @Override diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java index 85a92d1a135a..6212a77feba8 100644 --- a/telecomm/java/android/telecom/ParcelableCall.java +++ b/telecomm/java/android/telecom/ParcelableCall.java @@ -59,6 +59,7 @@ public final class ParcelableCall implements Parcelable { private final List<String> mConferenceableCallIds; private final Bundle mIntentExtras; private final Bundle mExtras; + private final long mCreationTimeMillis; public ParcelableCall( String id, @@ -85,7 +86,8 @@ public final class ParcelableCall implements Parcelable { int videoState, List<String> conferenceableCallIds, Bundle intentExtras, - Bundle extras) { + Bundle extras, + long creationTimeMillis) { mId = id; mState = state; mDisconnectCause = disconnectCause; @@ -111,6 +113,7 @@ public final class ParcelableCall implements Parcelable { mConferenceableCallIds = Collections.unmodifiableList(conferenceableCallIds); mIntentExtras = intentExtras; mExtras = extras; + mCreationTimeMillis = creationTimeMillis; } /** The unique ID of the call. */ @@ -289,6 +292,13 @@ public final class ParcelableCall implements Parcelable { return mIsVideoCallProviderChanged; } + /** + * @return The time the call was created, in milliseconds since the epoch. + */ + public long getCreationTimeMillis() { + return mCreationTimeMillis; + } + /** Responsible for creating ParcelableCall objects for deserialized Parcels. */ public static final Parcelable.Creator<ParcelableCall> CREATOR = new Parcelable.Creator<ParcelableCall> () { @@ -324,6 +334,7 @@ public final class ParcelableCall implements Parcelable { int supportedAudioRoutes = source.readInt(); boolean isRttCallChanged = source.readByte() == 1; ParcelableRttCall rttCall = source.readParcelable(classLoader); + long creationTimeMillis = source.readLong(); return new ParcelableCall( id, state, @@ -349,7 +360,8 @@ public final class ParcelableCall implements Parcelable { videoState, conferenceableCallIds, intentExtras, - extras); + extras, + creationTimeMillis); } @Override @@ -393,6 +405,7 @@ public final class ParcelableCall implements Parcelable { destination.writeInt(mSupportedAudioRoutes); destination.writeByte((byte) (mIsRttCallChanged ? 1 : 0)); destination.writeParcelable(mRttCall, 0); + destination.writeLong(mCreationTimeMillis); } @Override diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index b4c531e99a95..f9875c5859ab 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -53,8 +53,9 @@ public class TelephonyIntents { * * <p class="note">This is a protected intent that can only be sent * by the system. + * @deprecated use {@link Intent#ACTION_SERVICE_STATE} */ - public static final String ACTION_SERVICE_STATE_CHANGED = "android.intent.action.SERVICE_STATE"; + public static final String ACTION_SERVICE_STATE_CHANGED = Intent.ACTION_SERVICE_STATE; /** * <p>Broadcast Action: The radio technology has changed. The intent will have the following diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk index 36ebc901a86b..71067aef52c7 100644 --- a/tests/UiBench/Android.mk +++ b/tests/UiBench/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_SDK_VERSION := 24 +LOCAL_SDK_VERSION := current LOCAL_MIN_SDK_VERSION := 21 # omit gradle 'build' dir diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 8461905d8034..90f713b67985 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -155,7 +155,10 @@ bool ResourceParser::FlattenXmlSubtree( xml::XmlPullParser* parser, std::string* out_raw_string, StyleString* out_style_string, std::vector<UntranslatableSection>* out_untranslatable_sections) { // Keeps track of formatting tags (<b>, <i>) and the range of characters for which they apply. - std::vector<Span> span_stack; + // The stack elements refer to the indices in out_style_string->spans. + // By first adding to the out_style_string->spans vector, and then using the stack to refer + // to this vector, the original order of tags is preserved in cases such as <b><i>hello</b></i>. + std::vector<size_t> span_stack; // Clear the output variables. out_raw_string->clear(); @@ -192,7 +195,9 @@ bool ResourceParser::FlattenXmlSubtree( return false; } - span_stack.push_back(Span{std::move(span_name), static_cast<uint32_t>(builder.Utf16Len())}); + out_style_string->spans.push_back( + Span{std::move(span_name), static_cast<uint32_t>(builder.Utf16Len())}); + span_stack.push_back(out_style_string->spans.size() - 1); } else if (parser->element_namespace() == sXliffNamespaceUri) { if (parser->element_name() == "g") { if (untranslatable_start_depth) { @@ -233,9 +238,8 @@ bool ResourceParser::FlattenXmlSubtree( if (parser->element_namespace().empty()) { // This is an HTML tag which we encode as a span. Update the span // stack and pop the top entry. - Span& top_span = span_stack.back(); + Span& top_span = out_style_string->spans[span_stack.back()]; top_span.last_char = builder.Utf16Len() - 1; - out_style_string->spans.push_back(std::move(top_span)); span_stack.pop_back(); } else if (untranslatable_start_depth == make_value(depth)) { // This is the end of an untranslatable section. Use UTF8 indices/lengths. diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index eefa320a4418..8062c2e6afea 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -101,20 +101,24 @@ TEST_F(ResourceParserTest, ParseStyledString) { // Use a surrogate pair unicode point so that we can verify that the span // indices use UTF-16 length and not UTF-8 length. std::string input = - "<string name=\"foo\">This is my aunt\u2019s <b>string</b></string>"; + "<string name=\"foo\">This is my aunt\u2019s <b>fickle <small>string</small></b></string>"; ASSERT_TRUE(TestParse(input)); StyledString* str = test::GetValue<StyledString>(&table_, "string/foo"); ASSERT_NE(nullptr, str); - const std::string expected_str = "This is my aunt\u2019s string"; + const std::string expected_str = "This is my aunt\u2019s fickle string"; EXPECT_EQ(expected_str, *str->value->str); - EXPECT_EQ(1u, str->value->spans.size()); + EXPECT_EQ(2u, str->value->spans.size()); EXPECT_TRUE(str->untranslatable_sections.empty()); EXPECT_EQ(std::string("b"), *str->value->spans[0].name); EXPECT_EQ(17u, str->value->spans[0].first_char); - EXPECT_EQ(23u, str->value->spans[0].last_char); + EXPECT_EQ(30u, str->value->spans[0].last_char); + + EXPECT_EQ(std::string("small"), *str->value->spans[1].name); + EXPECT_EQ(24u, str->value->spans[1].first_char); + EXPECT_EQ(30u, str->value->spans[1].last_char); } TEST_F(ResourceParserTest, ParseStringWithWhitespace) { diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp index fad9edd04e4c..a031ea4c31ec 100644 --- a/tools/aapt2/compile/PseudolocaleGenerator.cpp +++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp @@ -22,136 +22,194 @@ #include "ResourceValues.h" #include "ValueVisitor.h" #include "compile/Pseudolocalizer.h" +#include "util/Util.h" using android::StringPiece; +using android::StringPiece16; namespace aapt { -std::unique_ptr<StyledString> PseudolocalizeStyledString( - StyledString* string, Pseudolocalizer::Method method, StringPool* pool) { - Pseudolocalizer localizer(method); +// The struct that represents both Span objects and UntranslatableSections. +struct UnifiedSpan { + // Only present for Span objects. If not present, this was an UntranslatableSection. + Maybe<std::string> tag; - const StringPiece original_text = *string->value->str; + // The UTF-16 index into the string where this span starts. + uint32_t first_char; - StyleString localized; + // The UTF-16 index into the string where this span ends, inclusive. + uint32_t last_char; +}; - // Copy the spans. We will update their offsets when we localize. - localized.spans.reserve(string->value->spans.size()); - for (const StringPool::Span& span : string->value->spans) { - localized.spans.push_back( - Span{*span.name, span.first_char, span.last_char}); +inline static bool operator<(const UnifiedSpan& left, const UnifiedSpan& right) { + if (left.first_char < right.first_char) { + return true; + } else if (left.first_char > right.first_char) { + return false; + } else if (left.last_char < right.last_char) { + return true; } + return false; +} - // The ranges are all represented with a single value. This is the start of - // one range and end of another. - struct Range { - size_t start; - - // If set to true, toggles the state of translatability. - bool toggle_translatability; - - // Once the new string is localized, these are the pointers to the spans to adjust. - // Since this struct represents the start of one range and end of another, - // we have the two pointers respectively. - uint32_t* update_start; - uint32_t* update_end; - }; - - auto cmp = [](const Range& r, size_t index) -> bool { - return r.start < index; - }; - - // Construct the ranges. The ranges are represented like so: [0, 2, 5, 7] - // The ranges are the spaces in between. In this example, with a total string - // length of 9, the vector represents: (0,1], (2,4], (5,6], (7,9] - // - std::vector<Range> ranges; - ranges.push_back(Range{0, false, nullptr, nullptr}); - ranges.push_back(Range{original_text.size() - 1, false, nullptr, nullptr}); - for (size_t i = 0; i < string->value->spans.size(); i++) { - const StringPool::Span& span = string->value->spans[i]; - - // Insert or update the Range marker for the start of this span. - auto iter = - std::lower_bound(ranges.begin(), ranges.end(), span.first_char, cmp); - if (iter != ranges.end() && iter->start == span.first_char) { - iter->update_start = &localized.spans[i].first_char; - } else { - ranges.insert(iter, Range{span.first_char, false, &localized.spans[i].first_char, nullptr}); - } +inline static UnifiedSpan SpanToUnifiedSpan(const StringPool::Span& span) { + return UnifiedSpan{*span.name, span.first_char, span.last_char}; +} + +inline static UnifiedSpan UntranslatableSectionToUnifiedSpan(const UntranslatableSection& section) { + return UnifiedSpan{ + {}, static_cast<uint32_t>(section.start), static_cast<uint32_t>(section.end) - 1}; +} - // Insert or update the Range marker for the end of this span. - iter = std::lower_bound(ranges.begin(), ranges.end(), span.last_char, cmp); - if (iter != ranges.end() && iter->start == span.last_char) { - iter->update_end = &localized.spans[i].last_char; +// Merges the Span and UntranslatableSections of this StyledString into a single vector of +// UnifiedSpans. This will first check that the Spans are sorted in ascending order. +static std::vector<UnifiedSpan> MergeSpans(const StyledString& string) { + // Ensure the Spans are sorted and converted. + std::vector<UnifiedSpan> sorted_spans; + sorted_spans.reserve(string.value->spans.size()); + std::transform(string.value->spans.begin(), string.value->spans.end(), + std::back_inserter(sorted_spans), SpanToUnifiedSpan); + + // Stable sort to ensure tag sequences like "<b><i>" are preserved. + std::stable_sort(sorted_spans.begin(), sorted_spans.end()); + + // Ensure the UntranslatableSections are sorted and converted. + std::vector<UnifiedSpan> sorted_untranslatable_sections; + sorted_untranslatable_sections.reserve(string.untranslatable_sections.size()); + std::transform(string.untranslatable_sections.begin(), string.untranslatable_sections.end(), + std::back_inserter(sorted_untranslatable_sections), + UntranslatableSectionToUnifiedSpan); + std::sort(sorted_untranslatable_sections.begin(), sorted_untranslatable_sections.end()); + + std::vector<UnifiedSpan> merged_spans; + merged_spans.reserve(sorted_spans.size() + sorted_untranslatable_sections.size()); + auto span_iter = sorted_spans.begin(); + auto untranslatable_iter = sorted_untranslatable_sections.begin(); + while (span_iter != sorted_spans.end() && + untranslatable_iter != sorted_untranslatable_sections.end()) { + if (*span_iter < *untranslatable_iter) { + merged_spans.push_back(std::move(*span_iter)); + ++span_iter; } else { - ranges.insert(iter, Range{span.last_char, false, nullptr, &localized.spans[i].last_char}); + merged_spans.push_back(std::move(*untranslatable_iter)); + ++untranslatable_iter; } } - // Parts of the string may be untranslatable. Merge those ranges - // in as well, so that we have continuous sections of text to - // feed into the pseudolocalizer. - // We do this by marking the beginning of a range as either toggling - // the translatability state or not. - for (const UntranslatableSection& section : string->untranslatable_sections) { - auto iter = std::lower_bound(ranges.begin(), ranges.end(), section.start, cmp); - if (iter != ranges.end() && iter->start == section.start) { - // An existing span starts (or ends) here. We just need to mark that - // the translatability should toggle here. If translatability was - // already being toggled, then that means we have two adjacent ranges of untranslatable - // text, so remove the toggle and only toggle at the end of this range, - // effectively merging these ranges. - iter->toggle_translatability = !iter->toggle_translatability; - } else { - // Insert a new range that specifies to toggle the translatability. - iter = ranges.insert(iter, Range{section.start, true, nullptr, nullptr}); - } + while (span_iter != sorted_spans.end()) { + merged_spans.push_back(std::move(*span_iter)); + ++span_iter; + } - // Update/create an end to the untranslatable section. - iter = std::lower_bound(iter, ranges.end(), section.end, cmp); - if (iter != ranges.end() && iter->start == section.end) { - iter->toggle_translatability = true; - } else { - iter = ranges.insert(iter, Range{section.end, true, nullptr, nullptr}); - } + while (untranslatable_iter != sorted_untranslatable_sections.end()) { + merged_spans.push_back(std::move(*untranslatable_iter)); + ++untranslatable_iter; } + return merged_spans; +} - localized.str += localizer.Start(); +std::unique_ptr<StyledString> PseudolocalizeStyledString(StyledString* string, + Pseudolocalizer::Method method, + StringPool* pool) { + Pseudolocalizer localizer(method); - // Iterate over the ranges and localize each section. - // The text starts as translatable, and each time a range has toggle_translatability - // set to true, we toggle whether to translate or not. - // This assumes no untranslatable ranges overlap. - bool translatable = true; - for (size_t i = 0; i < ranges.size(); i++) { - const size_t start = ranges[i].start; - size_t len = original_text.size() - start; - if (i + 1 < ranges.size()) { - len = ranges[i + 1].start - start; - } + // Collect the spans and untranslatable sections into one set of spans, sorted by first_char. + // This will effectively subdivide the string into multiple sections that can be individually + // pseudolocalized, while keeping the span indices synchronized. + std::vector<UnifiedSpan> merged_spans = MergeSpans(*string); - if (ranges[i].update_start) { - *ranges[i].update_start = localized.str.size(); - } + // All Span indices are UTF-16 based, according to the resources.arsc format expected by the + // runtime. So we will do all our processing in UTF-16, then convert back. + const std::u16string text16 = util::Utf8ToUtf16(*string->value->str); - if (ranges[i].update_end) { - *ranges[i].update_end = localized.str.size(); - } + // Convenient wrapper around the text that allows us to work with StringPieces. + const StringPiece16 text(text16); + + // The new string. + std::string new_string = localizer.Start(); + + // The stack that keeps track of what nested Span we're in. + std::vector<size_t> span_stack; + + // The current position in the original text. + uint32_t cursor = 0u; + + // The current position in the new text. + uint32_t new_cursor = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(new_string.data()), + new_string.size(), false); - if (ranges[i].toggle_translatability) { - translatable = !translatable; + // We assume no nesting of untranslatable sections, since XLIFF doesn't allow it. + bool translatable = true; + size_t span_idx = 0u; + while (span_idx < merged_spans.size() || !span_stack.empty()) { + UnifiedSpan* span = span_idx >= merged_spans.size() ? nullptr : &merged_spans[span_idx]; + UnifiedSpan* parent_span = span_stack.empty() ? nullptr : &merged_spans[span_stack.back()]; + + if (span != nullptr) { + if (parent_span == nullptr || parent_span->last_char > span->first_char) { + // There is no parent, or this span is the child of the parent. + // Pseudolocalize all the text until this span. + const StringPiece16 substr = text.substr(cursor, span->first_char - cursor); + cursor += substr.size(); + + // Pseudolocalize the substring. + std::string new_substr = util::Utf16ToUtf8(substr); + if (translatable) { + new_substr = localizer.Text(new_substr); + } + new_cursor += utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(new_substr.data()), + new_substr.size(), false); + new_string += new_substr; + + // Rewrite the first_char. + span->first_char = new_cursor; + if (!span->tag) { + // An untranslatable section has begun! + translatable = false; + } + span_stack.push_back(span_idx); + ++span_idx; + continue; + } } - if (translatable) { - localized.str += localizer.Text(original_text.substr(start, len)); - } else { - localized.str += original_text.substr(start, len); + if (parent_span != nullptr) { + // There is a parent, and either this span is not a child of it, or there are no more spans. + // Pop this off the stack. + const StringPiece16 substr = text.substr(cursor, parent_span->last_char - cursor + 1); + cursor += substr.size(); + + // Pseudolocalize the substring. + std::string new_substr = util::Utf16ToUtf8(substr); + if (translatable) { + new_substr = localizer.Text(new_substr); + } + new_cursor += utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(new_substr.data()), + new_substr.size(), false); + new_string += new_substr; + + parent_span->last_char = new_cursor - 1; + if (parent_span->tag) { + // An end to an untranslatable section. + translatable = true; + } + span_stack.pop_back(); } } - localized.str += localizer.End(); + // Finish the pseudolocalization at the end of the string. + new_string += localizer.Text(util::Utf16ToUtf8(text.substr(cursor, text.size() - cursor))); + new_string += localizer.End(); + + StyleString localized; + localized.str = std::move(new_string); + // Convert the UnifiedSpans into regular Spans, skipping the UntranslatableSections. + for (UnifiedSpan& span : merged_spans) { + if (span.tag) { + localized.spans.push_back(Span{std::move(span.tag.value()), span.first_char, span.last_char}); + } + } return util::make_unique<StyledString>(pool->MakeRef(localized)); } @@ -175,8 +233,7 @@ class Visitor : public RawValueVisitor { if (sub_visitor.value) { localized->values[i] = std::move(sub_visitor.item); } else { - localized->values[i] = - std::unique_ptr<Item>(plural->values[i]->Clone(pool_)); + localized->values[i] = std::unique_ptr<Item>(plural->values[i]->Clone(pool_)); } } } @@ -210,8 +267,7 @@ class Visitor : public RawValueVisitor { } result += localizer_.End(); - std::unique_ptr<String> localized = - util::make_unique<String>(pool_->MakeRef(result)); + std::unique_ptr<String> localized = util::make_unique<String>(pool_->MakeRef(result)); localized->SetSource(string->GetSource()); localized->SetWeak(true); item = std::move(localized); @@ -282,14 +338,10 @@ void PseudolocalizeIfNeeded(const Pseudolocalizer::Method method, } } -/** - * A value is pseudolocalizable if it does not define a locale (or is the - * default locale) - * and is translatable. - */ +// A value is pseudolocalizable if it does not define a locale (or is the default locale) and is +// translatable. static bool IsPseudolocalizable(ResourceConfigValue* config_value) { - const int diff = - config_value->config.diff(ConfigDescription::DefaultConfig()); + const int diff = config_value->config.diff(ConfigDescription::DefaultConfig()); if (diff & ConfigDescription::CONFIG_LOCALE) { return false; } @@ -298,19 +350,16 @@ static bool IsPseudolocalizable(ResourceConfigValue* config_value) { } // namespace -bool PseudolocaleGenerator::Consume(IAaptContext* context, - ResourceTable* table) { +bool PseudolocaleGenerator::Consume(IAaptContext* context, ResourceTable* table) { for (auto& package : table->packages) { for (auto& type : package->types) { for (auto& entry : type->entries) { - std::vector<ResourceConfigValue*> values = - entry->FindValuesIf(IsPseudolocalizable); - + std::vector<ResourceConfigValue*> values = entry->FindValuesIf(IsPseudolocalizable); for (ResourceConfigValue* value : values) { - PseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value, - &table->string_pool, entry.get()); - PseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value, - &table->string_pool, entry.get()); + PseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value, &table->string_pool, + entry.get()); + PseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value, &table->string_pool, + entry.get()); } } } diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp index 4db37db55eb7..b08e1dab35a9 100644 --- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp +++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp @@ -25,7 +25,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) { StringPool pool; StyleString original_style; original_style.str = "Hello world!"; - original_style.spans = {Span{"b", 2, 3}, Span{"b", 6, 7}, Span{"i", 1, 10}}; + original_style.spans = {Span{"i", 1, 10}, Span{"b", 2, 3}, Span{"b", 6, 7}}; std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString( util::make_unique<StyledString>(pool.MakeRef(original_style)).get(), @@ -34,22 +34,19 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) { EXPECT_EQ(original_style.str, *new_string->value->str); ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size()); - EXPECT_EQ(std::string("He").size(), new_string->value->spans[0].first_char); - EXPECT_EQ(std::string("Hel").size(), new_string->value->spans[0].last_char); - EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name); + EXPECT_EQ(std::string("i"), *new_string->value->spans[0].name); + EXPECT_EQ(std::u16string(u"H").size(), new_string->value->spans[0].first_char); + EXPECT_EQ(std::u16string(u"Hello worl").size(), new_string->value->spans[0].last_char); - EXPECT_EQ(std::string("Hello ").size(), - new_string->value->spans[1].first_char); - EXPECT_EQ(std::string("Hello w").size(), - new_string->value->spans[1].last_char); EXPECT_EQ(std::string("b"), *new_string->value->spans[1].name); + EXPECT_EQ(std::u16string(u"He").size(), new_string->value->spans[1].first_char); + EXPECT_EQ(std::u16string(u"Hel").size(), new_string->value->spans[1].last_char); - EXPECT_EQ(std::string("H").size(), new_string->value->spans[2].first_char); - EXPECT_EQ(std::string("Hello worl").size(), - new_string->value->spans[2].last_char); - EXPECT_EQ(std::string("i"), *new_string->value->spans[2].name); + EXPECT_EQ(std::string("b"), *new_string->value->spans[2].name); + EXPECT_EQ(std::u16string(u"Hello ").size(), new_string->value->spans[2].first_char); + EXPECT_EQ(std::u16string(u"Hello w").size(), new_string->value->spans[2].last_char); - original_style.spans.push_back(Span{"em", 0, 11u}); + original_style.spans.insert(original_style.spans.begin(), Span{"em", 0, 11u}); new_string = PseudolocalizeStyledString( util::make_unique<StyledString>(pool.MakeRef(original_style)).get(), @@ -58,23 +55,128 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) { EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *new_string->value->str); ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size()); - EXPECT_EQ(std::string("[Ĥé").size(), new_string->value->spans[0].first_char); - EXPECT_EQ(std::string("[Ĥéļ").size(), new_string->value->spans[0].last_char); + EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char); + EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵöŕļð").size(), new_string->value->spans[0].last_char); + + EXPECT_EQ(std::u16string(u"[Ĥ").size(), new_string->value->spans[1].first_char); + EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵöŕļ").size(), new_string->value->spans[1].last_char); + + EXPECT_EQ(std::u16string(u"[Ĥé").size(), new_string->value->spans[2].first_char); + EXPECT_EQ(std::u16string(u"[Ĥéļ").size(), new_string->value->spans[2].last_char); + + EXPECT_EQ(std::u16string(u"[Ĥéļļö ").size(), new_string->value->spans[3].first_char); + EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵ").size(), new_string->value->spans[3].last_char); +} + +TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentNestedTags) { + StringPool pool; + StyleString original_style; + original_style.str = "bold"; + original_style.spans = {Span{"b", 0, 3}, Span{"i", 0, 3}}; + + std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString( + util::make_unique<StyledString>(pool.MakeRef(original_style)).get(), + Pseudolocalizer::Method::kAccent, &pool); + ASSERT_NE(nullptr, new_string); + ASSERT_EQ(2u, new_string->value->spans.size()); + EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str); + + EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name); + EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char); + EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[0].last_char); + + EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name); + EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[1].first_char); + EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[1].last_char); +} + +TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentTagsUnsorted) { + StringPool pool; + StyleString original_style; + original_style.str = "bold"; + original_style.spans = {Span{"i", 2, 3}, Span{"b", 0, 1}}; + + std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString( + util::make_unique<StyledString>(pool.MakeRef(original_style)).get(), + Pseudolocalizer::Method::kAccent, &pool); + ASSERT_NE(nullptr, new_string); + ASSERT_EQ(2u, new_string->value->spans.size()); + EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str); + + EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name); + EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char); + EXPECT_EQ(std::u16string(u"[ɓ").size(), new_string->value->spans[0].last_char); + + EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name); + EXPECT_EQ(std::u16string(u"[ɓö").size(), new_string->value->spans[1].first_char); + EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[1].last_char); +} + +TEST(PseudolocaleGeneratorTest, PseudolocalizeNestedAndAdjacentTags) { + StringPool pool; + StyleString original_style; + original_style.str = "This sentence is not what you think it is at all."; + original_style.spans = {Span{"b", 16u, 19u}, Span{"em", 29u, 47u}, Span{"i", 38u, 40u}, + Span{"b", 44u, 47u}}; + + std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString( + util::make_unique<StyledString>(pool.MakeRef(original_style)).get(), + Pseudolocalizer::Method::kAccent, &pool); + ASSERT_NE(nullptr, new_string); + ASSERT_EQ(4u, new_string->value->spans.size()); + EXPECT_EQ(std::string( + "[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļļ. one two three four five six]"), + *new_string->value->str); + + EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name); + EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš").size(), new_string->value->spans[0].first_char); + EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñö").size(), new_string->value->spans[0].last_char); - EXPECT_EQ(std::string("[Ĥéļļö ").size(), + EXPECT_EQ(std::string("em"), *new_string->value->spans[1].name); + EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû").size(), new_string->value->spans[1].first_char); - EXPECT_EQ(std::string("[Ĥéļļö ŵ").size(), + EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļ").size(), new_string->value->spans[1].last_char); - EXPECT_EQ(std::string("[Ĥ").size(), new_string->value->spans[2].first_char); - EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļ").size(), + EXPECT_EQ(std::string("i"), *new_string->value->spans[2].name); + EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ").size(), + new_string->value->spans[2].first_char); + EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ î").size(), new_string->value->spans[2].last_char); - EXPECT_EQ(std::string("[").size(), new_string->value->spans[3].first_char); - EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð").size(), + EXPECT_EQ(std::string("b"), *new_string->value->spans[3].name); + EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ").size(), + new_string->value->spans[3].first_char); + EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļ").size(), new_string->value->spans[3].last_char); } +TEST(PseudolocaleGeneratorTest, PseudolocalizePartsOfString) { + StringPool pool; + StyleString original_style; + original_style.str = "This should NOT be pseudolocalized."; + original_style.spans = {Span{"em", 4u, 14u}, Span{"i", 18u, 33u}}; + std::unique_ptr<StyledString> original_string = + util::make_unique<StyledString>(pool.MakeRef(original_style)); + original_string->untranslatable_sections = {UntranslatableSection{11u, 15u}}; + + std::unique_ptr<StyledString> new_string = + PseudolocalizeStyledString(original_string.get(), Pseudolocalizer::Method::kAccent, &pool); + ASSERT_NE(nullptr, new_string); + ASSERT_EQ(2u, new_string->value->spans.size()); + EXPECT_EQ(std::string("[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžéð. one two three four]"), + *new_string->value->str); + + EXPECT_EQ(std::string("em"), *new_string->value->spans[0].name); + EXPECT_EQ(std::u16string(u"[Ţĥîš").size(), new_string->value->spans[0].first_char); + EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NO").size(), new_string->value->spans[0].last_char); + + EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name); + EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NOT ɓé").size(), new_string->value->spans[1].first_char); + EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžé").size(), + new_string->value->spans[1].last_char); +} + TEST(PseudolocaleGeneratorTest, PseudolocalizeOnlyDefaultConfigs) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() @@ -138,7 +240,7 @@ TEST(PseudolocaleGeneratorTest, RespectUntranslateableSections) { { StyleString original_style; original_style.str = "Hello world!"; - original_style.spans = {Span{"b", 2, 3}, Span{"b", 6, 7}, Span{"i", 1, 10}}; + original_style.spans = {Span{"i", 1, 10}, Span{"b", 2, 3}, Span{"b", 6, 7}}; auto styled_string = util::make_unique<StyledString>(table->string_pool.MakeRef(original_style)); 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; } |